From 8739e3b347e621332b2ea90c78bb75dce62edae9 Mon Sep 17 00:00:00 2001 From: tiger tiger tiger Date: Tue, 7 Jul 2026 23:29:25 +0200 Subject: [PATCH] chore: clean recommit --- Directory.Build.targets | 16 + Examples/Audio/ModulePlaying.cs | 289 ++++---- Examples/Audio/MusicStreamDemo.cs | 238 +++++-- Examples/Audio/SoundLoading.cs | 116 +-- Examples/Core/BasicScreenManager.cs | 279 ++++---- Examples/Core/BasicWindow.cs | 59 +- Examples/Core/Camera2dDemo.cs | 261 +++---- Examples/Core/Camera2dPlatformer.cs | 297 ++++---- Examples/Core/Camera3dFirstPerson.cs | 335 +++++---- Examples/Core/Camera3dFree.cs | 142 ++-- Examples/Core/Camera3dMode.cs | 115 +-- Examples/Core/Customlogging.cs | 90 ++- Examples/Core/DeltaTime.cs | 199 +++--- Examples/Core/DropFiles.cs | 147 ++-- Examples/Core/InputGamepad.cs | 825 +++++++++++++--------- Examples/Core/InputGestures.cs | 273 +++---- Examples/Core/InputGesturesTestBed.cs | 747 +++++++++++--------- Examples/Core/InputKeys.cs | 111 +-- Examples/Core/InputMouse.cs | 177 +++-- Examples/Core/InputMouseWheel.cs | 78 +- Examples/Core/InputMultitouch.cs | 130 ++-- Examples/Core/InputVirtualControls.cs | 261 ++++--- Examples/Core/LoadingThread.cs | 248 ++++--- Examples/Core/Picking3D.cs | 226 +++--- Examples/Core/RandomValues.cs | 110 +-- Examples/Core/ScissorTest.cs | 130 ++-- Examples/Core/SmoothPixelperfect.cs | 254 ++++--- Examples/Core/SplitScreen.cs | 229 +++--- Examples/Core/StorageValues.cs | 167 +++-- Examples/Core/VrSimulator.cs | 241 ++++--- Examples/Core/WindowFlags.cs | 463 ++++++------ Examples/Core/WindowLetterbox.cs | 240 ++++--- Examples/Core/WorldScreen.cs | 163 +++-- Examples/ExampleRegistry.cs | 73 ++ Examples/Examples.csproj | 52 +- Examples/IExample.cs | 46 ++ Examples/Models/BillboardDemo.cs | 220 +++--- Examples/Models/BoxCollisions.cs | 252 ++++--- Examples/Models/CubicmapDemo.cs | 175 +++-- Examples/Models/DynamicMesh.cs | 166 +++-- Examples/Models/FirstPersonMaze.cs | 243 ++++--- Examples/Models/GeometricShapes.cs | 127 ++-- Examples/Models/HeightmapDemo.cs | 141 ++-- Examples/Models/LoadingGltf.cs | 169 +++-- Examples/Models/LoadingIqm.cs | 172 +++-- Examples/Models/MeshDemo.cs | 101 ++- Examples/Models/MeshGeneration.cs | 252 ++++--- Examples/Models/MeshPicking.cs | 438 +++++++----- Examples/Models/ModelCubeTexture.cs | 146 ++-- Examples/Models/ModelLoading.cs | 278 +++++--- Examples/Models/OrthographicProjection.cs | 175 +++-- Examples/Models/SkyboxDemo.cs | 260 ++++--- Examples/Models/SolarSystem.cs | 255 ++++--- Examples/Models/WavingCubes.cs | 190 ++--- Examples/Models/YawPitchRoll.cs | 298 ++++---- Examples/Program.cs | 234 ++---- Examples/Shaders/BasicLighting.cs | 298 ++++---- Examples/Shaders/BasicPbr.cs | 333 +++++---- Examples/Shaders/CustomUniform.cs | 233 +++--- Examples/Shaders/Eratosthenes.cs | 152 ++-- Examples/Shaders/Fog.cs | 249 ++++--- Examples/Shaders/HotReloading.cs | 256 ++++--- Examples/Shaders/HybridRender.cs | 219 +++--- Examples/Shaders/JuliaSet.cs | 398 ++++++----- Examples/Shaders/MeshInstancing.cs | 419 ++++------- Examples/Shaders/ModelShader.cs | 178 +++-- Examples/Shaders/MultiSample2d.cs | 193 +++-- Examples/Shaders/PaletteSwitch.cs | 207 +++--- Examples/Shaders/PostProcessing.cs | 261 ++++--- Examples/Shaders/Raymarching.cs | 208 +++--- Examples/Shaders/ShapesTextures.cs | 194 ++--- Examples/Shaders/SimpleMask.cs | 203 +++--- Examples/Shaders/Spotlight.cs | 349 +++++---- Examples/Shaders/TextureDrawing.cs | 132 ++-- Examples/Shaders/TextureOutline.cs | 162 +++-- Examples/Shaders/TextureWaves.cs | 149 ++-- Examples/Shaders/WriteDepth.cs | 167 +++-- Examples/Shapes/BasicShapes.cs | 139 ++-- Examples/Shapes/BouncingBall.cs | 185 +++-- Examples/Shapes/CollisionArea.cs | 248 ++++--- Examples/Shapes/ColorsPalette.cs | 281 ++++---- Examples/Shapes/DrawCircleSector.cs | 153 ++-- Examples/Shapes/DrawRectangleRounded.cs | 180 +++-- Examples/Shapes/DrawRing.cs | 233 +++--- Examples/Shapes/EasingsBallAnim.cs | 220 +++--- Examples/Shapes/EasingsBoxAnim.cs | 253 ++++--- Examples/Shapes/EasingsRectangleArray.cs | 214 +++--- Examples/Shapes/FollowingEyes.cs | 187 +++-- Examples/Shapes/LinesBezier.cs | 134 +++- Examples/Shapes/LogoRaylibAnim.cs | 358 +++++----- Examples/Shapes/LogoRaylibShape.cs | 83 ++- Examples/Shapes/RectangleScaling.cs | 218 +++--- Examples/Shared/Easings.cs | 38 +- Examples/Shared/PbrLights.cs | 12 +- Examples/Shared/Rlights.cs | 12 +- Examples/Text/CodepointsLoading.cs | 169 +++-- Examples/Text/FontFilters.cs | 247 ++++--- Examples/Text/FontLoading.cs | 145 ++-- Examples/Text/FontSdf.cs | 259 ++++--- Examples/Text/FontSpritefont.cs | 137 ++-- Examples/Text/FormatText.cs | 93 ++- Examples/Text/InputBox.cs | 298 ++++---- Examples/Text/RaylibFonts.cs | 139 ++-- Examples/Text/RectangleBounds.cs | 327 +++++---- Examples/Text/Unicode.cs | 759 ++++++++++++-------- Examples/Text/WritingAnim.cs | 117 +-- Examples/Textures/BackgroundScrolling.cs | 197 +++--- Examples/Textures/BlendModes.cs | 193 ++--- Examples/Textures/Bunnymark.cs | 198 +++--- Examples/Textures/DrawTiled.cs | 402 ++++++----- Examples/Textures/ImageDrawing.cs | 126 ++-- Examples/Textures/ImageGeneration.cs | 219 +++--- Examples/Textures/ImageLoading.cs | 100 ++- Examples/Textures/ImageProcessing.cs | 379 +++++----- Examples/Textures/ImageText.cs | 151 ++-- Examples/Textures/LogoRaylibTexture.cs | 95 ++- Examples/Textures/MousePainting.cs | 537 +++++++------- Examples/Textures/NpatchDrawing.cs | 183 +++-- Examples/Textures/ParticlesBlending.cs | 255 ++++--- Examples/Textures/Polygon.cs | 175 +++-- Examples/Textures/RawData.cs | 145 ++-- Examples/Textures/SpriteAnim.cs | 207 +++--- Examples/Textures/SpriteButton.cs | 181 +++-- Examples/Textures/SpriteExplosion.cs | 227 +++--- Examples/Textures/SrcRecDstRec.cs | 140 ++-- Examples/Textures/TexturedCurve.cs | 207 +++--- Examples/Textures/ToImage.cs | 107 +-- Examples/Web/Host.cs | 86 +++ Examples/Web/README.md | 118 ++++ Examples/Web/index.html | 70 ++ Examples/Web/main.js | 157 ++++ Examples/Web/scaleUtils.js | 67 ++ Examples/Web/scaleUtils.test.js | 92 +++ README.md | 19 - 134 files changed, 16372 insertions(+), 11578 deletions(-) create mode 100644 Examples/ExampleRegistry.cs create mode 100644 Examples/IExample.cs create mode 100644 Examples/Web/Host.cs create mode 100644 Examples/Web/README.md create mode 100644 Examples/Web/index.html create mode 100644 Examples/Web/main.js create mode 100644 Examples/Web/scaleUtils.js create mode 100644 Examples/Web/scaleUtils.test.js diff --git a/Directory.Build.targets b/Directory.Build.targets index bc743c2..8584d9b 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -7,4 +7,20 @@ Library + + + + + <_LocalNuGetDir>$(MSBuildThisFileDirectory)nuget + <_LocalRaylibCsNupkg>$(_LocalNuGetDir)/Raylib-cs.$(RaylibCsVersion).nupkg + + + + diff --git a/Examples/Audio/ModulePlaying.cs b/Examples/Audio/ModulePlaying.cs index be9ab92..39d7829 100644 --- a/Examples/Audio/ModulePlaying.cs +++ b/Examples/Audio/ModulePlaying.cs @@ -1,13 +1,15 @@ /******************************************************************************************* * -* raylib [audio] example - Module playing (streaming) +* raylib [audio] example - module playing * -* NOTE: This example requires OpenAL Soft library installed +* Example complexity rating: [★☆☆☆] 1/4 * -* This example has been created using raylib 1.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.5, last time updated with raylib 3.5 * -* Copyright (c) 2016 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) 2016-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -16,11 +18,20 @@ using static Raylib_cs.Raylib; namespace Examples.Audio; -public class ModulePlaying +public partial class ModulePlaying : IExample { - const int MaxCircles = 64; + private const int screenWidth = 800; + private const int screenHeight = 450; - struct CircleWave + private const int MaxCircles = 64; + + public string Name => "Audio / Module Playing"; + + public string Title => "raylib [audio] example - module playing"; + + public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint; + + private struct CircleWave { public Vector2 Position; public float Radius; @@ -29,20 +40,18 @@ public class ModulePlaying public Color Color; } - public static int Main() + private Color[] colors; + private CircleWave[] circles; + private Music music; + private float pitch; + private float timePlayed; + private bool pause; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitAudioDevice(); // Initialize audio device - SetConfigFlags(ConfigFlags.Msaa4xHint); // NOTE: Try to enable MSAA 4X - - InitWindow(screenWidth, screenHeight, "raylib [audio] example - module playing (streaming)"); - - InitAudioDevice(); - - Color[] colors = new Color[14] { + colors = new Color[14] { Color.Orange, Color.Red, Color.Gold, @@ -59,133 +68,163 @@ public class ModulePlaying Color.Beige }; - // Creates ome circles for visual effect - CircleWave[] circles = new CircleWave[MaxCircles]; + // Creates some circles for visual effect + circles = new CircleWave[MaxCircles]; - for (int i = MaxCircles - 1; i >= 0; i--) + for (var i = MaxCircles - 1; i >= 0; i--) { circles[i].Alpha = 0.0f; circles[i].Radius = GetRandomValue(10, 40); circles[i].Position.X = GetRandomValue((int)circles[i].Radius, screenWidth - (int)circles[i].Radius); circles[i].Position.Y = GetRandomValue((int)circles[i].Radius, screenHeight - (int)circles[i].Radius); - circles[i].Speed = (float)GetRandomValue(1, 100) / 20000.0f; + circles[i].Speed = (float)GetRandomValue(1, 100) / 2000.0f; circles[i].Color = colors[GetRandomValue(0, 13)]; } - Music music = LoadMusicStream("resources/audio/mini1111.xm"); + music = LoadMusicStream("resources/audio/mini1111.xm"); music.Looping = false; - float pitch = 1.0f; + pitch = 1.0f; PlayMusicStream(music); - float timePlayed = 0.0f; - bool pause = false; + timePlayed = 0.0f; + pause = false; + } - SetTargetFPS(60); + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateMusicStream(music); // Update music buffer with new stream data + + // Restart music playing (stop and play) + if (IsKeyPressed(KeyboardKey.Space)) + { + StopMusicStream(music); + PlayMusicStream(music); + } + + // Pause/Resume music playing + if (IsKeyPressed(KeyboardKey.P)) + { + pause = !pause; + + if (pause) + { + PauseMusicStream(music); + } + else + { + ResumeMusicStream(music); + } + } + + if (IsKeyDown(KeyboardKey.Down)) + { + pitch -= 0.01f; + } + else if (IsKeyDown(KeyboardKey.Up)) + { + pitch += 0.01f; + } + + SetMusicPitch(music, pitch); + + // Get timePlayed scaled to bar dimensions + timePlayed = GetMusicTimePlayed(music) / GetMusicTimeLength(music) * (screenWidth - 40); + + // Color circles animation + for (var i = MaxCircles - 1; (i >= 0) && !pause; i--) + { + circles[i].Alpha += circles[i].Speed; + circles[i].Radius += circles[i].Speed * 10.0f; + + if (circles[i].Alpha > 1.0f) + { + circles[i].Speed *= -1; + } + + if (circles[i].Alpha <= 0.0f) + { + circles[i].Alpha = 0.0f; + circles[i].Radius = GetRandomValue(10, 40); + circles[i].Position.X = GetRandomValue( + (int)circles[i].Radius, + screenWidth - (int)circles[i].Radius + ); + circles[i].Position.Y = GetRandomValue( + (int)circles[i].Radius, + screenHeight - (int)circles[i].Radius + ); + circles[i].Color = colors[GetRandomValue(0, 13)]; + circles[i].Speed = (float)GetRandomValue(1, 100) / 2000.0f; + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + for (var i = MaxCircles - 1; i >= 0; i--) + { + DrawCircleV( + circles[i].Position, + circles[i].Radius, + Fade(circles[i].Color, circles[i].Alpha) + ); + } + + // Draw time bar + DrawRectangle(20, screenHeight - 20 - 12, screenWidth - 40, 12, Color.LightGray); + DrawRectangle(20, screenHeight - 20 - 12, (int)timePlayed, 12, Color.Maroon); + DrawRectangleLines(20, screenHeight - 20 - 12, screenWidth - 40, 12, Color.Gray); + + // Draw help instructions + DrawRectangle(20, 20, 425, 145, Color.White); + DrawRectangleLines(20, 20, 425, 145, Color.Gray); + DrawText("PRESS SPACE TO RESTART MUSIC", 40, 40, 20, Color.Black); + DrawText("PRESS P TO PAUSE/RESUME", 40, 70, 20, Color.Black); + DrawText("PRESS UP/DOWN TO CHANGE SPEED", 40, 100, 20, Color.Black); + DrawText($"SPEED: {pitch:F6}", 40, 130, 20, Color.Maroon); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadMusicStream(music); // Unload music stream buffers from RAM + + CloseAudioDevice(); // Close audio device (music streaming is automatically stopped) + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + SetConfigFlags(ConfigFlags.Msaa4xHint); // NOTE: Try to enable MSAA 4X + + InitWindow(screenWidth, screenHeight, "raylib [audio] example - module playing"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new ModulePlaying(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - UpdateMusicStream(music); // Update music buffer with new stream data - - // Restart music playing (stop and play) - if (IsKeyPressed(KeyboardKey.Space)) - { - StopMusicStream(music); - PlayMusicStream(music); - } - - // Pause/Resume music playing - if (IsKeyPressed(KeyboardKey.P)) - { - pause = !pause; - - if (pause) - { - PauseMusicStream(music); - } - else - { - ResumeMusicStream(music); - } - } - - if (IsKeyDown(KeyboardKey.Down)) - { - pitch -= 0.01f; - } - else if (IsKeyDown(KeyboardKey.Up)) - { - pitch += 0.01f; - } - - SetMusicPitch(music, pitch); - - // Get timePlayed scaled to bar dimensions - timePlayed = GetMusicTimePlayed(music) / GetMusicTimeLength(music) * (screenWidth - 40); - - // Color circles animation - for (int i = MaxCircles - 1; (i >= 0) && !pause; i--) - { - circles[i].Alpha += circles[i].Speed; - circles[i].Radius += circles[i].Speed * 10.0f; - - if (circles[i].Alpha > 1.0f) - { - circles[i].Speed *= -1; - } - - if (circles[i].Alpha <= 0.0f) - { - circles[i].Alpha = 0.0f; - circles[i].Radius = GetRandomValue(10, 40); - circles[i].Position.X = GetRandomValue( - (int)circles[i].Radius, - screenWidth - (int)circles[i].Radius - ); - circles[i].Position.Y = GetRandomValue( - (int)circles[i].Radius, - screenHeight - (int)circles[i].Radius - ); - circles[i].Color = colors[GetRandomValue(0, 13)]; - circles[i].Speed = (float)GetRandomValue(1, 100) / 2000.0f; - } - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - for (int i = MaxCircles - 1; i >= 0; i--) - { - DrawCircleV( - circles[i].Position, - circles[i].Radius, - ColorAlpha(circles[i].Color, circles[i].Alpha) - ); - } - - // Draw time bar - DrawRectangle(20, screenHeight - 20 - 12, screenWidth - 40, 12, Color.LightGray); - DrawRectangle(20, screenHeight - 20 - 12, (int)timePlayed, 12, Color.Maroon); - DrawRectangleLines(20, screenHeight - 20 - 12, screenWidth - 40, 12, Color.Gray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadMusicStream(music); - - CloseAudioDevice(); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Audio/MusicStreamDemo.cs b/Examples/Audio/MusicStreamDemo.cs index bf8ed9e..c25d0b8 100644 --- a/Examples/Audio/MusicStreamDemo.cs +++ b/Examples/Audio/MusicStreamDemo.cs @@ -1,13 +1,15 @@ /******************************************************************************************* * -* raylib [audio] example - IntPtr playing (streaming) +* raylib [audio] example - music stream * -* NOTE: This example requires OpenAL Soft library installed +* Example complexity rating: [★☆☆☆] 1/4 * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.3, last time updated with raylib 4.2 * -* Copyright (c) 2015 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -15,90 +17,176 @@ using static Raylib_cs.Raylib; namespace Examples.Audio; -public class MusicStreamDemo +public partial class MusicStreamDemo : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Audio / Music Stream Demo"; + + public string Title => "raylib [audio] example - music stream"; + + public int TargetFps => 30; + + private Music music; + private float timePlayed; + private bool pause; + private float pan; + private float volume; + + public void Init() + { + InitAudioDevice(); // Initialize audio device + + music = LoadMusicStream("resources/audio/country.mp3"); + + PlayMusicStream(music); + + timePlayed = 0.0f; // Time played normalized [0.0f..1.0f] + pause = false; // Music playing paused + + pan = 0.0f; // Default audio pan center [-1.0f..1.0f] + SetMusicPan(music, pan); + + volume = 0.8f; // Default audio volume [0.0f..1.0f] + SetMusicVolume(music, volume); + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateMusicStream(music); // Update music buffer with new stream data + + // Restart music playing (stop and play) + if (IsKeyPressed(KeyboardKey.Space)) + { + StopMusicStream(music); + PlayMusicStream(music); + } + + // Pause/Resume music playing + if (IsKeyPressed(KeyboardKey.P)) + { + pause = !pause; + + if (pause) + { + PauseMusicStream(music); + } + else + { + ResumeMusicStream(music); + } + } + + // Set audio pan + if (IsKeyDown(KeyboardKey.Left)) + { + pan -= 0.05f; + if (pan < -1.0f) + { + pan = -1.0f; + } + SetMusicPan(music, pan); + } + else if (IsKeyDown(KeyboardKey.Right)) + { + pan += 0.05f; + if (pan > 1.0f) + { + pan = 1.0f; + } + SetMusicPan(music, pan); + } + + // Set audio volume + if (IsKeyDown(KeyboardKey.Down)) + { + volume -= 0.05f; + if (volume < 0.0f) + { + volume = 0.0f; + } + SetMusicVolume(music, volume); + } + else if (IsKeyDown(KeyboardKey.Up)) + { + volume += 0.05f; + if (volume > 1.0f) + { + volume = 1.0f; + } + SetMusicVolume(music, volume); + } + + // Get normalized time played for current music stream + timePlayed = GetMusicTimePlayed(music) / GetMusicTimeLength(music); + + if (timePlayed > 1.0f) + { + timePlayed = 1.0f; // Make sure time played is no longer than music + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("MUSIC SHOULD BE PLAYING!", 255, 150, 20, Color.LightGray); + + DrawText("LEFT-RIGHT for PAN CONTROL", 320, 74, 10, Color.DarkBlue); + DrawRectangle(300, 100, 200, 12, Color.LightGray); + DrawRectangleLines(300, 100, 200, 12, Color.Gray); + DrawRectangle((int)(300 + (pan + 1.0f) / 2.0f * 200 - 5), 92, 10, 28, Color.DarkGray); + + DrawRectangle(200, 200, 400, 12, Color.LightGray); + DrawRectangle(200, 200, (int)(timePlayed * 400.0f), 12, Color.Maroon); + DrawRectangleLines(200, 200, 400, 12, Color.Gray); + + DrawText("PRESS SPACE TO RESTART MUSIC", 215, 250, 20, Color.LightGray); + DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 280, 20, Color.LightGray); + + DrawText("UP-DOWN for VOLUME CONTROL", 320, 334, 10, Color.DarkGreen); + DrawRectangle(300, 360, 200, 12, Color.LightGray); + DrawRectangleLines(300, 360, 200, 12, Color.Gray); + DrawRectangle((int)(300 + volume * 200 - 5), 352, 10, 28, Color.DarkGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadMusicStream(music); // Unload music stream buffers from RAM + + CloseAudioDevice(); // Close audio device (music streaming is automatically stopped) + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [audio] example - music stream"); - InitWindow(screenWidth, screenHeight, "raylib [audio] example - music playing (streaming)"); - InitAudioDevice(); - - Music music = LoadMusicStream("resources/audio/country.mp3"); - PlayMusicStream(music); - - float timePlayed = 0.0f; - bool pause = false; - - SetTargetFPS(60); + SetTargetFPS(30); // Set our game to run at 30 frames-per-second //-------------------------------------------------------------------------------------- + var game = new MusicStreamDemo(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - UpdateMusicStream(music); // Update music buffer with new stream data - - // Restart music playing (stop and play) - if (IsKeyPressed(KeyboardKey.Space)) - { - StopMusicStream(music); - PlayMusicStream(music); - } - - // Pause/Resume music playing - if (IsKeyPressed(KeyboardKey.P)) - { - pause = !pause; - - if (pause) - { - PauseMusicStream(music); - } - else - { - ResumeMusicStream(music); - } - } - - // Get timePlayed scaled to bar dimensions (400 pixels) - timePlayed = GetMusicTimePlayed(music) / GetMusicTimeLength(music) * 400; - - if (timePlayed > 400) - { - StopMusicStream(music); - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("MUSIC SHOULD BE PLAYING!", 255, 150, 20, Color.LightGray); - - DrawRectangle(200, 200, 400, 12, Color.LightGray); - DrawRectangle(200, 200, (int)timePlayed, 12, Color.Maroon); - DrawRectangleLines(200, 200, 400, 12, Color.Gray); - - DrawText("PRESS SPACE TO RESTART MUSIC", 215, 250, 20, Color.LightGray); - DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 280, 20, Color.LightGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadMusicStream(music); - - CloseAudioDevice(); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Audio/SoundLoading.cs b/Examples/Audio/SoundLoading.cs index 1aed2bb..025852c 100644 --- a/Examples/Audio/SoundLoading.cs +++ b/Examples/Audio/SoundLoading.cs @@ -1,13 +1,15 @@ /******************************************************************************************* * -* raylib [audio] example - Sound loading and playing +* raylib [audio] example - sound loading * -* NOTE: This example requires OpenAL Soft library installed +* Example complexity rating: [★☆☆☆] 1/4 * -* This example has been created using raylib 1.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.1, last time updated with raylib 3.5 * -* Copyright (c) 2014 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -15,60 +17,84 @@ using static Raylib_cs.Raylib; namespace Examples.Audio; -public class SoundLoading +public partial class SoundLoading : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Audio / Sound Loading"; + + public string Title => "raylib [audio] example - sound loading"; + + private Sound fxWav; + private Sound fxOgg; + + public void Init() + { + InitAudioDevice(); // Initialize audio device + + fxWav = LoadSound("resources/audio/sound.wav"); // Load WAV audio file + fxOgg = LoadSound("resources/audio/target.ogg"); // Load OGG audio file + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KeyboardKey.Space)) + { + PlaySound(fxWav); // Play WAV sound + } + + if (IsKeyPressed(KeyboardKey.Enter)) + { + PlaySound(fxOgg); // Play OGG sound + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("Press SPACE to PLAY the WAV sound!", 200, 180, 20, Color.LightGray); + DrawText("Press ENTER to PLAY the OGG sound!", 200, 220, 20, Color.LightGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadSound(fxWav); // Unload sound data + UnloadSound(fxOgg); // Unload sound data + + CloseAudioDevice(); // Close audio device + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound loading"); - InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound loading and playing"); - InitAudioDevice(); - - Sound fxWav = LoadSound("resources/audio/sound.wav"); - Sound fxOgg = LoadSound("resources/audio/target.ogg"); - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new SoundLoading(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyPressed(KeyboardKey.Space)) - { - PlaySound(fxWav); - } - - if (IsKeyPressed(KeyboardKey.Enter)) - { - PlaySound(fxOgg); - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("Press SPACE to PLAY the WAV sound!", 200, 180, 20, Color.LightGray); - DrawText("Press ENTER to PLAY the OGG sound!", 200, 220, 20, Color.LightGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadSound(fxWav); - UnloadSound(fxOgg); - - CloseAudioDevice(); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/BasicScreenManager.cs b/Examples/Core/BasicScreenManager.cs index 62de113..308d5a0 100644 --- a/Examples/Core/BasicScreenManager.cs +++ b/Examples/Core/BasicScreenManager.cs @@ -1,21 +1,17 @@ /******************************************************************************************* * -* raylib [core] example - Basic window +* raylib [core] example - basic screen manager * -* Welcome to raylib! +* Example complexity rating: [★☆☆☆] 1/4 * -* To test examples, just press F6 and execute raylib_compile_execute script -* Note that compiled executable is placed in the same folder as .c file +* NOTE: This example illustrates a very simple screen manager based on a states machines * -* You can find all basic examples on C:\raylib\raylib\examples folder or -* raylib official webpage: www.raylib.com +* Example originally created with raylib 4.0, last time updated with raylib 4.0 * -* Enjoy using raylib. :) +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software * -* This example has been created using raylib 1.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) -* -* Copyright (c) 2013-2016 Ramon Santamaria (@raysan5) +* Copyright (c) 2021-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -23,7 +19,7 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -enum GameScreen +internal enum GameScreen { Logo = 0, Title, @@ -31,143 +27,160 @@ enum GameScreen Ending } -public class BasicScreenManager +public partial class BasicScreenManager : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Core / Basic Screen Manager"; + + public string Title => "raylib [core] example - basic screen manager"; + + private GameScreen currentScreen; + private int framesCounter; + + public void Init() + { + currentScreen = GameScreen.Logo; + + // TODO: Initialize all required variables and load all required data here! + + framesCounter = 0; // Useful to count frames + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + switch (currentScreen) + { + case GameScreen.Logo: + { + // TODO: Update LOGO screen variables here! + + framesCounter++; // Count frames + + // Wait for 2 seconds (120 frames) before jumping to TITLE screen + if (framesCounter > 120) + { + currentScreen = GameScreen.Title; + } + } + break; + case GameScreen.Title: + { + // TODO: Update TITLE screen variables here! + + // Press enter to change to GAMEPLAY screen + if (IsKeyPressed(KeyboardKey.Enter) || IsGestureDetected(Gesture.Tap)) + { + currentScreen = GameScreen.Gameplay; + } + } + break; + case GameScreen.Gameplay: + { + // TODO: Update GAMEPLAY screen variables here! + + // Press enter to change to ENDING screen + if (IsKeyPressed(KeyboardKey.Enter) || IsGestureDetected(Gesture.Tap)) + { + currentScreen = GameScreen.Ending; + } + } + break; + case GameScreen.Ending: + { + // TODO: Update ENDING screen variables here! + + // Press enter to return to TITLE screen + if (IsKeyPressed(KeyboardKey.Enter) || IsGestureDetected(Gesture.Tap)) + { + currentScreen = GameScreen.Title; + } + } + break; + default: + break; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(Color.RayWhite); + + switch (currentScreen) + { + case GameScreen.Logo: + { + // TODO: Draw LOGO screen here! + DrawText("LOGO SCREEN", 20, 20, 40, Color.LightGray); + DrawText("WAIT for 2 SECONDS...", 290, 220, 20, Color.Gray); + } + break; + case GameScreen.Title: + { + // TODO: Draw TITLE screen here! + DrawRectangle(0, 0, screenWidth, screenHeight, Color.Green); + DrawText("TITLE SCREEN", 20, 20, 40, Color.DarkGreen); + DrawText("PRESS ENTER or TAP to JUMP to GAMEPLAY SCREEN", 120, 220, 20, Color.DarkGreen); + } + break; + case GameScreen.Gameplay: + { + // TODO: Draw GAMEPLAY screen here! + DrawRectangle(0, 0, screenWidth, screenHeight, Color.Purple); + DrawText("GAMEPLAY SCREEN", 20, 20, 40, Color.Maroon); + DrawText("PRESS ENTER or TAP to JUMP to ENDING SCREEN", 130, 220, 20, Color.Maroon); + + } + break; + case GameScreen.Ending: + { + // TODO: Draw ENDING screen here! + DrawRectangle(0, 0, screenWidth, screenHeight, Color.Blue); + DrawText("ENDING SCREEN", 20, 20, 40, Color.DarkBlue); + DrawText("PRESS ENTER or TAP to RETURN to TITLE SCREEN", 120, 220, 20, Color.DarkBlue); + } + break; + default: + break; + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + // TODO: Unload all loaded data (textures, fonts, audio) here! + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - basic screen manager"); - GameScreen currentScreen = GameScreen.Logo; - - // TODO: Initialize all required variables and load all required data here! - - // Useful to count frames - int framesCounter = 0; - SetTargetFPS(60); // Set desired framerate (frames-per-second) //-------------------------------------------------------------------------------------- + var game = new BasicScreenManager(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - switch (currentScreen) - { - case GameScreen.Logo: - { - // TODO: Update LOGO screen variables here! - - // Count frames - framesCounter++; - - // Wait for 2 seconds (120 frames) before jumping to TITLE screen - if (framesCounter > 120) - { - currentScreen = GameScreen.Title; - } - } - break; - case GameScreen.Title: - { - // TODO: Update TITLE screen variables here! - - // Press enter to change to GAMEPLAY screen - if (IsKeyPressed(KeyboardKey.Enter) || IsGestureDetected(Gesture.Tap)) - { - currentScreen = GameScreen.Gameplay; - } - } - break; - case GameScreen.Gameplay: - { - // TODO: Update GAMEPLAY screen variables here! - - // Press enter to change to ENDING screen - if (IsKeyPressed(KeyboardKey.Enter) || IsGestureDetected(Gesture.Tap)) - { - currentScreen = GameScreen.Ending; - } - } - break; - case GameScreen.Ending: - { - // TODO: Update ENDING screen variables here! - - // Press enter to return to TITLE screen - if (IsKeyPressed(KeyboardKey.Enter) || IsGestureDetected(Gesture.Tap)) - { - currentScreen = GameScreen.Title; - } - } - break; - default: - break; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(Color.RayWhite); - - switch (currentScreen) - { - case GameScreen.Logo: - { - // TODO: Draw LOGO screen here! - DrawText("LOGO SCREEN", 20, 20, 40, Color.LightGray); - DrawText("WAIT for 2 SECONDS...", 290, 220, 20, Color.Gray); - - } - break; - case GameScreen.Title: - { - // TODO: Draw TITLE screen here! - DrawRectangle(0, 0, screenWidth, screenHeight, Color.Green); - DrawText("TITLE SCREEN", 20, 20, 40, Color.DarkGreen); - DrawText("PRESS ENTER or TAP to JUMP to GAMEPLAY SCREEN", 120, 220, 20, Color.DarkGreen); - - } - break; - case GameScreen.Gameplay: - { - // TODO: Draw GAMEPLAY screen here! - DrawRectangle(0, 0, screenWidth, screenHeight, Color.Purple); - DrawText("GAMEPLAY SCREEN", 20, 20, 40, Color.Maroon); - DrawText("PRESS ENTER or TAP to JUMP to ENDING SCREEN", 130, 220, 20, Color.Maroon); - - } - break; - case GameScreen.Ending: - { - // TODO: Draw ENDING screen here! - DrawRectangle(0, 0, screenWidth, screenHeight, Color.Blue); - DrawText("ENDING SCREEN", 20, 20, 40, Color.DarkBlue); - DrawText("PRESS ENTER or TAP to RETURN to TITLE SCREEN", 120, 220, 20, Color.DarkBlue); - - } - break; - default: - break; - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - - // TODO: Unload all loaded data (textures, fonts, audio) here! - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/BasicWindow.cs b/Examples/Core/BasicWindow.cs index 4180912..f24c930 100644 --- a/Examples/Core/BasicWindow.cs +++ b/Examples/Core/BasicWindow.cs @@ -33,43 +33,56 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class BasicWindow +public partial class BasicWindow : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Core / Basic Window"; + + public string Title => "raylib [core] example - basic window"; + + public void Init() + { + } + + public void Update() + { + + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("Congrats! You created your first window!", 190, 200, 20, Color.LightGray); + + EndDrawing(); + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window"); - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + + var game = new BasicWindow(); + game.Init(); // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update your variables here - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("Congrats! You created your first window!", 190, 200, 20, Color.LightGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); - //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context return 0; } diff --git a/Examples/Core/Camera2dDemo.cs b/Examples/Core/Camera2dDemo.cs index 1463c24..bd9aebf 100644 --- a/Examples/Core/Camera2dDemo.cs +++ b/Examples/Core/Camera2dDemo.cs @@ -13,31 +13,37 @@ * ********************************************************************************************/ +using System; using System.Numerics; using static Raylib_cs.Raylib; namespace Examples.Core; -public class Camera2dDemo +public partial class Camera2dDemo : IExample { public const int MaxBuildings = 100; - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Core / Camera 2D Demo"; + + public string Title => "raylib [core] example - 2d camera"; + + private Rectangle player; + private Rectangle[] buildings; + private Color[] buildColors; + private Camera2D camera; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + player = new(400, 280, 40, 40); + buildings = new Rectangle[MaxBuildings]; + buildColors = new Color[MaxBuildings]; - InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera"); + var spacing = 0; - Rectangle player = new(400, 280, 40, 40); - Rectangle[] buildings = new Rectangle[MaxBuildings]; - Color[] buildColors = new Color[MaxBuildings]; - - int spacing = 0; - - for (int i = 0; i < MaxBuildings; i++) + for (var i = 0; i < MaxBuildings; i++) { buildings[i].Width = GetRandomValue(50, 200); buildings[i].Height = GetRandomValue(100, 800); @@ -54,124 +60,139 @@ public class Camera2dDemo ); } - Camera2D camera = new(); + camera = new(); camera.Target = new Vector2(player.X + 20, player.Y + 20); - camera.Offset = new Vector2(screenWidth / 2, screenHeight / 2); + camera.Offset = new Vector2(screenWidth / 2.0f, screenHeight / 2.0f); camera.Rotation = 0.0f; camera.Zoom = 1.0f; + } - SetTargetFPS(60); + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + // Player movement + if (IsKeyDown(KeyboardKey.Right)) + { + player.X += 2; + } + else if (IsKeyDown(KeyboardKey.Left)) + { + player.X -= 2; + } + + // Camera target follows player + camera.Target = new Vector2(player.X + 20, player.Y + 20); + + // Camera rotation controls + if (IsKeyDown(KeyboardKey.A)) + { + camera.Rotation--; + } + else if (IsKeyDown(KeyboardKey.S)) + { + camera.Rotation++; + } + + // Limit camera rotation to 80 degrees (-40 to 40) + if (camera.Rotation > 40) + { + camera.Rotation = 40; + } + else if (camera.Rotation < -40) + { + camera.Rotation = -40; + } + + // Camera zoom controls + // Uses log scaling to provide consistent zoom speed + camera.Zoom = MathF.Exp(MathF.Log(camera.Zoom) + ((float)GetMouseWheelMove() * 0.1f)); + + if (camera.Zoom > 3.0f) + { + camera.Zoom = 3.0f; + } + else if (camera.Zoom < 0.1f) + { + camera.Zoom = 0.1f; + } + + // Camera reset (zoom and rotation) + if (IsKeyPressed(KeyboardKey.R)) + { + camera.Zoom = 1.0f; + camera.Rotation = 0.0f; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode2D(camera); + + DrawRectangle(-6000, 320, 13000, 8000, Color.DarkGray); + + for (var i = 0; i < MaxBuildings; i++) + { + DrawRectangleRec(buildings[i], buildColors[i]); + } + + DrawRectangleRec(player, Color.Red); + + DrawLine((int)camera.Target.X, -screenHeight * 10, (int)camera.Target.X, screenHeight * 10, Color.Green); + DrawLine(-screenWidth * 10, (int)camera.Target.Y, screenWidth * 10, (int)camera.Target.Y, Color.Green); + + EndMode2D(); + + DrawText("SCREEN AREA", 640, 10, 20, Color.Red); + + DrawRectangle(0, 0, screenWidth, 5, Color.Red); + DrawRectangle(0, 5, 5, screenHeight - 10, Color.Red); + DrawRectangle(screenWidth - 5, 5, 5, screenHeight - 10, Color.Red); + DrawRectangle(0, screenHeight - 5, screenWidth, 5, Color.Red); + + DrawRectangle(10, 10, 250, 113, Fade(Color.SkyBlue, 0.5f)); + DrawRectangleLines(10, 10, 250, 113, Color.Blue); + + DrawText("Free 2D camera controls:", 20, 20, 10, Color.Black); + DrawText("- Right/Left to move player", 40, 40, 10, Color.DarkGray); + DrawText("- Mouse Wheel to Zoom in-out", 40, 60, 10, Color.DarkGray); + DrawText("- A / S to Rotate", 40, 80, 10, Color.DarkGray); + DrawText("- R to reset Zoom and Rotation", 40, 100, 10, Color.DarkGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new Camera2dDemo(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - - // Player movement - if (IsKeyDown(KeyboardKey.Right)) - { - player.X += 2; - } - else if (IsKeyDown(KeyboardKey.Left)) - { - player.X -= 2; - } - - // Camera3D target follows player - camera.Target = new Vector2(player.X + 20, player.Y + 20); - - // Camera3D rotation controls - if (IsKeyDown(KeyboardKey.A)) - { - camera.Rotation--; - } - else if (IsKeyDown(KeyboardKey.S)) - { - camera.Rotation++; - } - - // Limit camera rotation to 80 degrees (-40 to 40) - if (camera.Rotation > 40) - { - camera.Rotation = 40; - } - else if (camera.Rotation < -40) - { - camera.Rotation = -40; - } - - // Camera3D zoom controls - camera.Zoom += ((float)GetMouseWheelMove() * 0.05f); - - if (camera.Zoom > 3.0f) - { - camera.Zoom = 3.0f; - } - else if (camera.Zoom < 0.1f) - { - camera.Zoom = 0.1f; - } - - // Camera3D reset (zoom and rotation) - if (IsKeyPressed(KeyboardKey.R)) - { - camera.Zoom = 1.0f; - camera.Rotation = 0.0f; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode2D(camera); - - DrawRectangle(-6000, 320, 13000, 8000, Color.DarkGray); - - for (int i = 0; i < MaxBuildings; i++) - { - DrawRectangleRec(buildings[i], buildColors[i]); - } - - DrawRectangleRec(player, Color.Red); - - DrawRectangle((int)camera.Target.X, -500, 1, (int)(screenHeight * 4), Color.Green); - DrawLine( - (int)(-screenWidth * 10), - (int)camera.Target.Y, - (int)(screenWidth * 10), - (int)camera.Target.Y, - Color.Green - ); - - EndMode2D(); - - DrawText("SCREEN AREA", 640, 10, 20, Color.Red); - - DrawRectangle(0, 0, (int)screenWidth, 5, Color.Red); - DrawRectangle(0, 5, 5, (int)screenHeight - 10, Color.Red); - DrawRectangle((int)screenWidth - 5, 5, 5, (int)screenHeight - 10, Color.Red); - DrawRectangle(0, (int)screenHeight - 5, (int)screenWidth, 5, Color.Red); - - DrawRectangle(10, 10, 250, 113, ColorAlpha(Color.SkyBlue, 0.5f)); - DrawRectangleLines(10, 10, 250, 113, Color.Blue); - - DrawText("Free 2d camera controls:", 20, 20, 10, Color.Black); - DrawText("- Right/Left to move Offset", 40, 40, 10, Color.DarkGray); - DrawText("- Mouse Wheel to Zoom in-out", 40, 60, 10, Color.DarkGray); - DrawText("- A / S to Rotate", 40, 80, 10, Color.DarkGray); - DrawText("- R to reset Zoom and Rotation", 40, 100, 10, Color.DarkGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/Camera2dPlatformer.cs b/Examples/Core/Camera2dPlatformer.cs index c86d820..9f54911 100644 --- a/Examples/Core/Camera2dPlatformer.cs +++ b/Examples/Core/Camera2dPlatformer.cs @@ -2,12 +2,16 @@ * * raylib [core] example - 2d camera platformer * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 2.5, last time updated with raylib 3.0 * * Example contributed by arvyy (@arvyy) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2019 arvyy (@arvyy) +* 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) 2019-2025 arvyy (@arvyy) * ********************************************************************************************/ @@ -18,20 +22,27 @@ using static Raylib_cs.Raymath; namespace Examples.Core; -public class Camera2dPlatformer +public partial class Camera2dPlatformer : IExample { - const int G = 400; - const float PlayerJumpSpeed = 350.0f; - const float PlayerHorSpeed = 200.0f; + private const int screenWidth = 800; + private const int screenHeight = 450; - struct Player + private const int G = 400; + private const float PlayerJumpSpeed = 350.0f; + private const float PlayerHorSpeed = 200.0f; + + public string Name => "Core / 2D Camera Platformer"; + + public string Title => "raylib [core] example - 2d camera platformer"; + + private struct Player { public Vector2 Position; public float Speed; public bool CanJump; } - struct EnvItem + private struct EnvItem { public Rectangle Rect; public int Blocking; @@ -45,7 +56,7 @@ public class Camera2dPlatformer } } - delegate void CameraUpdaterCallback( + private delegate void CameraUpdaterCallback( ref Camera2D camera, ref Player player, EnvItem[] envItems, @@ -54,21 +65,22 @@ public class Camera2dPlatformer int height ); - public static int Main() + private Player player; + private EnvItem[] envItems; + private Camera2D camera; + private CameraUpdaterCallback[] cameraUpdaters; + private int cameraOption; + private int cameraUpdatersLength; + private string[] cameraDescriptions; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera"); - - Player player = new(); + player = new(); player.Position = new Vector2(400, 280); player.Speed = 0; player.CanJump = false; - EnvItem[] envItems = new EnvItem[] + envItems = new EnvItem[] { new EnvItem(new Rectangle(0, 0, 1000, 400), 0, Color.LightGray), new EnvItem(new Rectangle(0, 400, 1000, 200), 1, Color.Gray), @@ -77,14 +89,14 @@ public class Camera2dPlatformer new EnvItem(new Rectangle(650, 300, 100, 10), 1, Color.Gray) }; - Camera2D camera = new(); + camera = new(); camera.Target = player.Position; - camera.Offset = new Vector2(screenWidth / 2, screenHeight / 2); + camera.Offset = new Vector2(screenWidth / 2.0f, screenHeight / 2.0f); camera.Rotation = 0.0f; camera.Zoom = 1.0f; - // Store callbacks to the multiple update camera functions - CameraUpdaterCallback[] cameraUpdaters = new CameraUpdaterCallback[] + // Store pointers to the multiple update camera functions + cameraUpdaters = new CameraUpdaterCallback[] { UpdateCameraCenter, UpdateCameraCenterInsideMap, @@ -93,93 +105,89 @@ public class Camera2dPlatformer UpdateCameraPlayerBoundsPush }; - int cameraOption = 0; - int cameraUpdatersLength = cameraUpdaters.Length; + cameraOption = 0; + cameraUpdatersLength = cameraUpdaters.Length; - string[] cameraDescriptions = new string[]{ + cameraDescriptions = new string[]{ "Follow player center", "Follow player center, but clamp to map edges", "Follow player center; smoothed", "Follow player center horizontally; update player center vertically after landing", "Player push camera on getting too close to screen edge" }; - - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) - { - // Update - //---------------------------------------------------------------------------------- - float deltaTime = GetFrameTime(); - - UpdatePlayer(ref player, envItems, deltaTime); - - camera.Zoom += ((float)GetMouseWheelMove() * 0.05f); - - if (camera.Zoom > 3.0f) - { - camera.Zoom = 3.0f; - } - else if (camera.Zoom < 0.25f) - { - camera.Zoom = 0.25f; - } - - if (IsKeyPressed(KeyboardKey.R)) - { - camera.Zoom = 1.0f; - player.Position = new Vector2(400, 280); - } - - if (IsKeyPressed(KeyboardKey.C)) - { - cameraOption = (cameraOption + 1) % cameraUpdatersLength; - } - - // Call update camera function by its pointer - cameraUpdaters[cameraOption](ref camera, ref player, envItems, deltaTime, screenWidth, screenHeight); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.LightGray); - - BeginMode2D(camera); - - for (int i = 0; i < envItems.Length; i++) - { - DrawRectangleRec(envItems[i].Rect, envItems[i].Color); - } - - Rectangle playerRect = new(player.Position.X - 20, player.Position.Y - 40, 40, 40); - DrawRectangleRec(playerRect, Color.Red); - - EndMode2D(); - - DrawText("Controls:", 20, 20, 10, Color.Black); - DrawText("- Right/Left to move", 40, 40, 10, Color.DarkGray); - DrawText("- Space to jump", 40, 60, 10, Color.DarkGray); - DrawText("- Mouse Wheel to Zoom in-out, R to reset zoom", 40, 80, 10, Color.DarkGray); - DrawText("- C to change camera mode", 40, 100, 10, Color.DarkGray); - DrawText("Current camera mode:", 20, 120, 10, Color.Black); - DrawText(cameraDescriptions[cameraOption], 40, 140, 10, Color.DarkGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - CloseWindow(); - //-------------------------------------------------------------------------------------- - - return 0; } - static void UpdatePlayer(ref Player player, EnvItem[] envItems, float delta) + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + var deltaTime = GetFrameTime(); + + UpdatePlayer(ref player, envItems, deltaTime); + + camera.Zoom += ((float)GetMouseWheelMove() * 0.05f); + + if (camera.Zoom > 3.0f) + { + camera.Zoom = 3.0f; + } + else if (camera.Zoom < 0.25f) + { + camera.Zoom = 0.25f; + } + + if (IsKeyPressed(KeyboardKey.R)) + { + camera.Zoom = 1.0f; + player.Position = new Vector2(400, 280); + } + + if (IsKeyPressed(KeyboardKey.C)) + { + cameraOption = (cameraOption + 1) % cameraUpdatersLength; + } + + // Call update camera function by its pointer + cameraUpdaters[cameraOption](ref camera, ref player, envItems, deltaTime, screenWidth, screenHeight); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.LightGray); + + BeginMode2D(camera); + + for (var i = 0; i < envItems.Length; i++) + { + DrawRectangleRec(envItems[i].Rect, envItems[i].Color); + } + + Rectangle playerRect = new(player.Position.X - 20, player.Position.Y - 40, 40.0f, 40.0f); + DrawRectangleRec(playerRect, Color.Red); + + DrawCircleV(player.Position, 5.0f, Color.Gold); + + EndMode2D(); + + DrawText("Controls:", 20, 20, 10, Color.Black); + DrawText("- Right/Left to move", 40, 40, 10, Color.DarkGray); + DrawText("- Space to jump", 40, 60, 10, Color.DarkGray); + DrawText("- Mouse Wheel to Zoom in-out", 40, 80, 10, Color.DarkGray); + DrawText("- R to reset position + zoom", 40, 100, 10, Color.DarkGray); + DrawText("- C to change camera mode", 40, 120, 10, Color.DarkGray); + DrawText("Current camera mode:", 20, 140, 10, Color.Black); + DrawText(cameraDescriptions[cameraOption], 40, 160, 10, Color.DarkGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + + private static void UpdatePlayer(ref Player player, EnvItem[] envItems, float delta) { if (IsKeyDown(KeyboardKey.Left)) { @@ -197,11 +205,11 @@ public class Camera2dPlatformer player.CanJump = false; } - int hitObstacle = 0; - for (int i = 0; i < envItems.Length; i++) + var hitObstacle = 0; + for (var i = 0; i < envItems.Length; i++) { - EnvItem ei = envItems[i]; - Vector2 p = player.Position; + var ei = envItems[i]; + var p = player.Position; if (ei.Blocking != 0 && ei.Rect.X <= p.X && ei.Rect.X + ei.Rect.Width >= p.X && @@ -211,6 +219,7 @@ public class Camera2dPlatformer hitObstacle = 1; player.Speed = 0.0f; player.Position.Y = ei.Rect.Y; + break; } } @@ -226,7 +235,7 @@ public class Camera2dPlatformer } } - static void UpdateCameraCenter( + private static void UpdateCameraCenter( ref Camera2D camera, ref Player player, EnvItem[] envItems, @@ -235,11 +244,11 @@ public class Camera2dPlatformer int height ) { - camera.Offset = new Vector2(width / 2, height / 2); + camera.Offset = new Vector2(width / 2.0f, height / 2.0f); camera.Target = player.Position; } - static void UpdateCameraCenterInsideMap( + private static void UpdateCameraCenterInsideMap( ref Camera2D camera, ref Player player, EnvItem[] envItems, @@ -248,43 +257,43 @@ public class Camera2dPlatformer int height) { camera.Target = player.Position; - camera.Offset = new Vector2(width / 2, height / 2); + camera.Offset = new Vector2(width / 2.0f, height / 2.0f); float minX = 1000, minY = 1000, maxX = -1000, maxY = -1000; - for (int i = 0; i < envItems.Length; i++) + for (var i = 0; i < envItems.Length; i++) { - EnvItem ei = envItems[i]; + var ei = envItems[i]; minX = Math.Min(ei.Rect.X, minX); maxX = Math.Max(ei.Rect.X + ei.Rect.Width, maxX); minY = Math.Min(ei.Rect.Y, minY); maxY = Math.Max(ei.Rect.Y + ei.Rect.Height, maxY); } - Vector2 max = GetWorldToScreen2D(new Vector2(maxX, maxY), camera); - Vector2 min = GetWorldToScreen2D(new Vector2(minX, minY), camera); + var max = GetWorldToScreen2D(new Vector2(maxX, maxY), camera); + var min = GetWorldToScreen2D(new Vector2(minX, minY), camera); if (max.X < width) { - camera.Offset.X = width - (max.X - width / 2); + camera.Offset.X = width - (max.X - width / 2.0f); } if (max.Y < height) { - camera.Offset.Y = height - (max.Y - height / 2); + camera.Offset.Y = height - (max.Y - height / 2.0f); } if (min.X > 0) { - camera.Offset.X = width / 2 - min.X; + camera.Offset.X = width / 2.0f - min.X; } if (min.Y > 0) { - camera.Offset.Y = height / 2 - min.Y; + camera.Offset.Y = height / 2.0f - min.Y; } } - static void UpdateCameraCenterSmoothFollow( + private static void UpdateCameraCenterSmoothFollow( ref Camera2D camera, ref Player player, EnvItem[] envItems, @@ -297,18 +306,18 @@ public class Camera2dPlatformer const float minEffectLength = 10; const float fractionSpeed = 0.8f; - camera.Offset = new Vector2(width / 2, height / 2); - Vector2 diff = Vector2Subtract(player.Position, camera.Target); - float length = Vector2Length(diff); + camera.Offset = new Vector2(width / 2.0f, height / 2.0f); + var diff = Vector2Subtract(player.Position, camera.Target); + var length = Vector2Length(diff); if (length > minEffectLength) { - float speed = Math.Max(fractionSpeed * length, minSpeed); + var speed = Math.Max(fractionSpeed * length, minSpeed); camera.Target = Vector2Add(camera.Target, Vector2Scale(diff, speed * delta / length)); } } - static void UpdateCameraEvenOutOnLanding( + private static void UpdateCameraEvenOutOnLanding( ref Camera2D camera, ref Player player, EnvItem[] envItems, @@ -318,10 +327,10 @@ public class Camera2dPlatformer ) { float evenOutSpeed = 700; - int eveningOut = 0; - float evenOutTarget = 0.0f; + var eveningOut = 0; + var evenOutTarget = 0.0f; - camera.Offset = new Vector2(width / 2, height / 2); + camera.Offset = new Vector2(width / 2.0f, height / 2.0f); camera.Target.X = player.Position.X; if (eveningOut != 0) @@ -357,7 +366,7 @@ public class Camera2dPlatformer } } - static void UpdateCameraPlayerBoundsPush( + private static void UpdateCameraPlayerBoundsPush( ref Camera2D camera, ref Player player, EnvItem[] envItems, @@ -368,11 +377,11 @@ public class Camera2dPlatformer { Vector2 bbox = new(0.2f, 0.2f); - Vector2 bboxWorldMin = GetScreenToWorld2D( + var bboxWorldMin = GetScreenToWorld2D( new Vector2((1 - bbox.X) * 0.5f * width, (1 - bbox.Y) * 0.5f * height), camera ); - Vector2 bboxWorldMax = GetScreenToWorld2D( + var bboxWorldMax = GetScreenToWorld2D( new Vector2((1 + bbox.X) * 0.5f * width, (1 + bbox.Y) * 0.5f * height), camera @@ -399,4 +408,32 @@ public class Camera2dPlatformer camera.Target.Y = bboxWorldMin.Y + (player.Position.Y - bboxWorldMax.Y); } } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera platformer"); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + var game = new Camera2dPlatformer(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) + { + game.Update(); + } + + game.Unload(); + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; + } } diff --git a/Examples/Core/Camera3dFirstPerson.cs b/Examples/Core/Camera3dFirstPerson.cs index 5d91bdb..7f7b951 100644 --- a/Examples/Core/Camera3dFirstPerson.cs +++ b/Examples/Core/Camera3dFirstPerson.cs @@ -2,10 +2,14 @@ * * raylib [core] example - 3d camera first person * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.3, last time updated with raylib 1.3 +* +* 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,171 +18,202 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class Camera3dFirstPerson +public partial class Camera3dFirstPerson : IExample { public const int MaxColumns = 20; + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Core / Camera 3D First Person"; + + public string Title => "raylib [core] example - 3d camera first person"; + + public bool CursorDisabled => true; + + private Camera3D camera; + private CameraMode cameraMode; + private float[] heights; + private Vector3[] positions; + private Color[] colors; + + public void Init() + { + // Define the camera to look into our 3d world (position, target, up vector) + camera = new(); + camera.Position = new Vector3(0.0f, 2.0f, 4.0f); // Camera position + camera.Target = new Vector3(0.0f, 2.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 60.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type + + cameraMode = CameraMode.FirstPerson; + + // Generates some random columns + heights = new float[MaxColumns]; + positions = new Vector3[MaxColumns]; + colors = new Color[MaxColumns]; + + for (var i = 0; i < MaxColumns; i++) + { + heights[i] = (float)GetRandomValue(1, 12); + positions[i] = new Vector3(GetRandomValue(-15, 15), heights[i] / 2.0f, GetRandomValue(-15, 15)); + colors[i] = new Color(GetRandomValue(20, 255), GetRandomValue(10, 55), 30, 255); + } + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + // Switch camera mode + if (IsKeyPressed(KeyboardKey.One)) + { + cameraMode = CameraMode.Free; + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); + } + + if (IsKeyPressed(KeyboardKey.Two)) + { + cameraMode = CameraMode.FirstPerson; + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); + } + + if (IsKeyPressed(KeyboardKey.Three)) + { + cameraMode = CameraMode.ThirdPerson; + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); + } + + if (IsKeyPressed(KeyboardKey.Four)) + { + cameraMode = CameraMode.Orbital; + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); + } + + // Switch camera projection + if (IsKeyPressed(KeyboardKey.P)) + { + if (camera.Projection == CameraProjection.Perspective) + { + // Create isometric view + cameraMode = CameraMode.ThirdPerson; + // Note: The target distance is related to the render distance in the orthographic projection + camera.Position = new Vector3(0.0f, 2.0f, -100.0f); + camera.Target = new Vector3(0.0f, 2.0f, 0.0f); + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); + camera.Projection = CameraProjection.Orthographic; + camera.FovY = 20.0f; // near plane width in CAMERA_ORTHOGRAPHIC + // CameraYaw(&camera, -135 * DEG2RAD, true); + // CameraPitch(&camera, -45 * DEG2RAD, true, true, false); + } + else if (camera.Projection == CameraProjection.Orthographic) + { + // Reset to default view + cameraMode = CameraMode.ThirdPerson; + camera.Position = new Vector3(0.0f, 2.0f, 10.0f); + camera.Target = new Vector3(0.0f, 2.0f, 0.0f); + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); + camera.Projection = CameraProjection.Perspective; + camera.FovY = 60.0f; + } + } + + // Update camera computes movement internally depending on the camera mode + // Some default standard keyboard/mouse inputs are hardcoded to simplify use + // For advanced camera controls, it's recommended to compute camera movement manually + UpdateCamera(ref camera, cameraMode); // Update camera + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + // Draw ground + DrawPlane(new Vector3(0.0f, 0.0f, 0.0f), new Vector2(32.0f, 32.0f), Color.LightGray); + + // Draw a blue wall + DrawCube(new Vector3(-16.0f, 2.5f, 0.0f), 1.0f, 5.0f, 32.0f, Color.Blue); + + // Draw a green wall + DrawCube(new Vector3(16.0f, 2.5f, 0.0f), 1.0f, 5.0f, 32.0f, Color.Lime); + + // Draw a yellow wall + DrawCube(new Vector3(0.0f, 2.5f, 16.0f), 32.0f, 5.0f, 1.0f, Color.Gold); + + // Draw some cubes around + for (var i = 0; i < MaxColumns; i++) + { + DrawCube(positions[i], 2.0f, heights[i], 2.0f, colors[i]); + DrawCubeWires(positions[i], 2.0f, heights[i], 2.0f, Color.Maroon); + } + + // Draw player cube + if (cameraMode == CameraMode.ThirdPerson) + { + DrawCube(camera.Target, 0.5f, 0.5f, 0.5f, Color.Purple); + DrawCubeWires(camera.Target, 0.5f, 0.5f, 0.5f, Color.DarkPurple); + } + + EndMode3D(); + + // Draw info boxes + DrawRectangle(5, 5, 330, 100, Fade(Color.SkyBlue, 0.5f)); + DrawRectangleLines(5, 5, 330, 100, Color.Blue); + + DrawText("Camera controls:", 15, 15, 10, Color.Black); + DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 10, Color.Black); + DrawText("- Look around: arrow keys or mouse", 15, 45, 10, Color.Black); + DrawText("- Camera mode keys: 1, 2, 3, 4", 15, 60, 10, Color.Black); + DrawText("- Zoom keys: num-plus, num-minus or mouse scroll", 15, 75, 10, Color.Black); + DrawText("- Camera projection key: P", 15, 90, 10, Color.Black); + + DrawRectangle(600, 5, 195, 100, Fade(Color.SkyBlue, 0.5f)); + DrawRectangleLines(600, 5, 195, 100, Color.Blue); + + DrawText("Camera status:", 610, 15, 10, Color.Black); + DrawText($"- Mode: {cameraMode}", 610, 30, 10, Color.Black); + DrawText($"- Projection: {camera.Projection}", 610, 45, 10, Color.Black); + DrawText($"- Position: {camera.Position}", 610, 60, 10, Color.Black); + DrawText($"- Target: {camera.Target}", 610, 75, 10, Color.Black); + DrawText($"- Up: {camera.Up}", 610, 90, 10, Color.Black); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera first person"); - // Define the camera to look into our 3d world (position, target, up vector) - Camera3D camera = new(); - camera.Position = new Vector3(4.0f, 2.0f, 4.0f); - camera.Target = new Vector3(0.0f, 1.8f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 60.0f; - camera.Projection = CameraProjection.Perspective; + DisableCursor(); // Limit cursor to relative movement inside the window - // Generates some random columns - float[] heights = new float[MaxColumns]; - Vector3[] positions = new Vector3[MaxColumns]; - Color[] colors = new Color[MaxColumns]; - - for (int i = 0; i < MaxColumns; i++) - { - heights[i] = (float)GetRandomValue(1, 12); - positions[i] = new Vector3(GetRandomValue(-15, 15), heights[i] / 2, GetRandomValue(-15, 15)); - colors[i] = new Color(GetRandomValue(20, 255), GetRandomValue(10, 55), 30, 255); - } - - CameraMode cameraMode = CameraMode.FirstPerson; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new Camera3dFirstPerson(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // Switch camera mode - if (IsKeyPressed(KeyboardKey.One)) - { - cameraMode = CameraMode.Free; - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - } - - if (IsKeyPressed(KeyboardKey.Two)) - { - cameraMode = CameraMode.FirstPerson; - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - } - - if (IsKeyPressed(KeyboardKey.Three)) - { - cameraMode = CameraMode.ThirdPerson; - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - } - - if (IsKeyPressed(KeyboardKey.Four)) - { - cameraMode = CameraMode.Orbital; - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - } - - // Switch camera projection - if (IsKeyPressed(KeyboardKey.P)) - { - if (camera.Projection == CameraProjection.Perspective) - { - // Create isometric view - cameraMode = CameraMode.ThirdPerson; - // Note: The target distance is related to the render distance in the orthographic projection - camera.Position = new Vector3(0.0f, 2.0f, -100.0f); - camera.Target = new Vector3(0.0f, 2.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.Projection = CameraProjection.Orthographic; - camera.FovY = 20.0f; // near plane width in CAMERA_ORTHOGRAPHIC - // CameraYaw(&camera, -135 * DEG2RAD, true); - // CameraPitch(&camera, -45 * DEG2RAD, true, true, false); - } - else if (camera.Projection == CameraProjection.Orthographic) - { - // Reset to default view - cameraMode = CameraMode.ThirdPerson; - camera.Position = new Vector3(0.0f, 2.0f, 10.0f); - camera.Target = new Vector3(0.0f, 2.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.Projection = CameraProjection.Perspective; - camera.FovY = 60.0f; - } - } - - // Update camera computes movement internally depending on the camera mode - // Some default standard keyboard/mouse inputs are hardcoded to simplify use - // For advance camera controls, it's reecommended to compute camera movement manually - UpdateCamera(ref camera, cameraMode); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - // Draw ground - DrawPlane(new Vector3(0.0f, 0.0f, 0.0f), new Vector2(32.0f, 32.0f), Color.LightGray); - - // Draw a blue wall - DrawCube(new Vector3(-16.0f, 2.5f, 0.0f), 1.0f, 5.0f, 32.0f, Color.Blue); - - // Draw a green wall - DrawCube(new Vector3(16.0f, 2.5f, 0.0f), 1.0f, 5.0f, 32.0f, Color.Lime); - - // Draw a yellow wall - DrawCube(new Vector3(0.0f, 2.5f, 16.0f), 32.0f, 5.0f, 1.0f, Color.Gold); - - // Draw some cubes around - for (int i = 0; i < MaxColumns; i++) - { - DrawCube(positions[i], 2.0f, heights[i], 2.0f, colors[i]); - DrawCubeWires(positions[i], 2.0f, heights[i], 2.0f, Color.Maroon); - } - - // Draw player cube - if (cameraMode == CameraMode.ThirdPerson) - { - DrawCube(camera.Target, 0.5f, 0.5f, 0.5f, Color.Purple); - DrawCubeWires(camera.Target, 0.5f, 0.5f, 0.5f, Color.DarkPurple); - } - - EndMode3D(); - - // Draw info boxes - DrawRectangle(5, 5, 330, 100, ColorAlpha(Color.SkyBlue, 0.5f)); - DrawRectangleLines(10, 10, 330, 100, Color.Blue); - - DrawText("Camera controls:", 15, 15, 10, Color.Black); - DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 10, Color.Black); - DrawText("- Look around: arrow keys or mouse", 15, 45, 10, Color.Black); - DrawText("- Camera mode keys: 1, 2, 3, 4", 15, 60, 10, Color.Black); - DrawText("- Zoom keys: num-plus, num-minus or mouse scroll", 15, 75, 10, Color.Black); - DrawText("- Camera projection key: P", 15, 90, 10, Color.Black); - - DrawRectangle(600, 5, 195, 100, Fade(Color.SkyBlue, 0.5f)); - DrawRectangleLines(600, 5, 195, 100, Color.Blue); - - DrawText("Camera status:", 610, 15, 10, Color.Black); - DrawText($"- Mode: {cameraMode}", 610, 30, 10, Color.Black); - DrawText($"- Projection: {camera.Projection}", 610, 45, 10, Color.Black); - DrawText($"- Position: {camera.Position}", 610, 60, 10, Color.Black); - DrawText($"- Target: {camera.Target}", 610, 75, 10, Color.Black); - DrawText($"- Up: {camera.Up}", 610, 90, 10, Color.Black); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/Camera3dFree.cs b/Examples/Core/Camera3dFree.cs index cea2e8f..95cd179 100644 --- a/Examples/Core/Camera3dFree.cs +++ b/Examples/Core/Camera3dFree.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [core] example - Initialize 3d camera free +* raylib [core] example - 3d camera free * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 * -* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.3, last time updated with raylib 1.3 +* +* 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,74 +18,100 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class Camera3dFree +public partial class Camera3dFree : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Core / Camera 3D Free"; + + public string Title => "raylib [core] example - 3d camera free"; + + public bool CursorDisabled => true; + + private Camera3D camera; + private Vector3 cubePosition; + + public void Init() + { + // Define the camera to look into our 3d world + camera = new(); + camera.Position = new Vector3(10.0f, 10.0f, 10.0f); // Camera position + camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type + + cubePosition = new(0.0f, 0.0f, 0.0f); + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Free); + + if (IsKeyPressed(KeyboardKey.Z)) + { + camera.Target = new Vector3(0.0f, 0.0f, 0.0f); + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red); + DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon); + + DrawGrid(10, 1.0f); + + EndMode3D(); + + DrawRectangle(10, 10, 320, 93, Fade(Color.SkyBlue, 0.5f)); + DrawRectangleLines(10, 10, 320, 93, Color.Blue); + + DrawText("Free camera default controls:", 20, 20, 10, Color.Black); + DrawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, Color.DarkGray); + DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, Color.DarkGray); + DrawText("- Z to zoom to (0, 0, 0)", 40, 80, 10, Color.DarkGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera free"); - // Define the camera to look into our 3d world - Camera3D camera; - camera.Position = new Vector3(10.0f, 10.0f, 10.0f); - camera.Target = new Vector3(0.0f, 0.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; + DisableCursor(); // Limit cursor to relative movement inside the window - Vector3 cubePosition = new(0.0f, 0.0f, 0.0f); - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new Camera3dFree(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Free); - - if (IsKeyDown(KeyboardKey.Z)) - { - camera.Target = new Vector3(0.0f, 0.0f, 0.0f); - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red); - DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon); - - DrawGrid(10, 1.0f); - - EndMode3D(); - - DrawRectangle(10, 10, 320, 133, ColorAlpha(Color.SkyBlue, 0.5f)); - DrawRectangleLines(10, 10, 320, 133, Color.Blue); - - DrawText("Free camera default controls:", 20, 20, 10, Color.Black); - DrawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, Color.DarkGray); - DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, Color.DarkGray); - DrawText("- Alt + Mouse Wheel Pressed to Rotate", 40, 80, 10, Color.DarkGray); - DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 40, 100, 10, Color.DarkGray); - DrawText("- Z to zoom to (0, 0, 0)", 40, 120, 10, Color.DarkGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/Camera3dMode.cs b/Examples/Core/Camera3dMode.cs index 61510a2..14c58fa 100644 --- a/Examples/Core/Camera3dMode.cs +++ b/Examples/Core/Camera3dMode.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [core] example - Initialize 3d camera mode +* raylib [core] example - 3d camera mode * -* This example has been created using raylib 1.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 * -* Copyright (c) 2014 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.0, last time updated with raylib 1.0 +* +* 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,63 +18,82 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class Camera3dMode +public partial class Camera3dMode : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + private Camera3D camera; + private Vector3 cubePosition; + + public string Name => "Core / Camera 3D Mode"; + + public string Title => "raylib [core] example - 3d camera mode"; + + public void Init() + { + // Define the camera to look into our 3d world + camera = new(); + camera.Position = new Vector3(0.0f, 10.0f, 10.0f); // Camera position + camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera mode type + + cubePosition = new(0.0f, 0.0f, 0.0f); + } + + public void Update() + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red); + DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon); + + DrawGrid(10, 1.0f); + + EndMode3D(); + + DrawText("Welcome to the third dimension!", 10, 40, 20, Color.DarkGray); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera mode"); - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(0.0f, 10.0f, 10.0f); - camera.Target = new Vector3(0.0f, 0.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; - - Vector3 cubePosition = new(0.0f, 0.0f, 0.0f); - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new Camera3dMode(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update your variables here - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red); - DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon); - - DrawGrid(10, 1.0f); - - EndMode3D(); - - DrawText("Welcome to the third dimension!", 10, 40, 20, Color.DarkGray); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/Customlogging.cs b/Examples/Core/Customlogging.cs index 3fdf64a..e171cc4 100644 --- a/Examples/Core/Customlogging.cs +++ b/Examples/Core/Customlogging.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [core] example - Custom logging +* raylib [core] example - custom logging * -* This example has been created using raylib 2.1 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 2.5, last time updated with raylib 2.5 * * Example contributed by Pablo Marcos Oltra (@pamarcos) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2018 Pablo Marcos Oltra (@pamarcos) and 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) 2018-2025 Pablo Marcos Oltra (@pamarcos) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -17,11 +21,23 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public unsafe class CustomLogging +public unsafe partial class CustomLogging : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Core / Custom Logging"; + + public string Title => "raylib [core] example - custom logging"; + [UnmanagedCallersOnly(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] private static void LogCustom(int logLevel, sbyte* text, sbyte* args) { +#if BROWSER + // WebAssembly can't invoke the C varargs vsprintf/vsnprintf that Logging.GetLogMessage relies on + // (the wasm runtime traps with "function signature mismatch"), so log the raw, unformatted text. + string message = Marshal.PtrToStringUTF8(new IntPtr(text)) ?? string.Empty; +#else var message = Logging.GetLogMessage(new IntPtr(text), new IntPtr(args)); /*Console.ForegroundColor = (TraceLogLevel)logLevel switch @@ -36,50 +52,64 @@ public unsafe class CustomLogging TraceLogLevel.LOG_NONE => ConsoleColor.White, _ => throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null) };*/ +#endif Console.WriteLine($"Custom " + message); // Console.ResetColor(); } + public void Init() + { + // First thing we do is setting our custom logger to ensure everything raylib logs + // will use our own logger instead of its internal one + SetTraceLogCallback(&LogCustom); + } + + public void Update() + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("Check out the console output to see the custom logger in action!", 60, 200, 20, Color.LightGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { +#if !BROWSER + // Restore the default formatting logger on desktop. On WebAssembly we leave the (wasm-safe) + // custom logger in place — Logging.LogConsole formats via vsprintf, which traps on wasm. + SetTraceLogCallback(&Logging.LogConsole); +#endif + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - // First thing we do is setting our custom logger to ensure everything raylib logs - // will use our own logger instead of its internal one - SetTraceLogCallback(&LogCustom); - InitWindow(screenWidth, screenHeight, "raylib [core] example - custom logging"); - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new CustomLogging(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update your variables here - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("Check out the console output to see the custom logger in action!", 60, 200, 20, Color.LightGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); - SetTraceLogCallback(&Logging.LogConsole); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/DeltaTime.cs b/Examples/Core/DeltaTime.cs index 38dd077..f83ee51 100644 --- a/Examples/Core/DeltaTime.cs +++ b/Examples/Core/DeltaTime.cs @@ -21,108 +21,137 @@ namespace Examples.Core; using static Raylib_cs.Raylib; -public class DeltaTime +public partial class DeltaTime : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + // The speed applied to both circles + private const float speed = 10.0f; + private const float circleRadius = 32.0f; + + private int currentFps; + + // Store the position for the both of the circles + private Vector2 deltaCircle; + private Vector2 frameCircle; + + public string Name => "Core / Delta Time"; + + public string Title => "raylib [core] example - delta time"; + + public void Init() + { + currentFps = 60; + + // Store the position for the both of the circles + deltaCircle = new Vector2(0, (float)screenHeight / 3.0f); + frameCircle = new Vector2(0, (float)screenHeight * (2.0f / 3.0f)); + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + // Adjust the FPS target based on the mouse wheel + var mouseWheel = GetMouseWheelMove(); + if (mouseWheel != 0) + { + currentFps += (int)mouseWheel; + if (currentFps < 0) + { + currentFps = 0; + } + SetTargetFPS(currentFps); + } + + // GetFrameTime() returns the time it took to draw the last frame, in seconds (usually called delta time) + // Uses the delta time to make the circle look like it's moving at a "consistent" speed regardless of FPS + + // Multiply by 6.0 (an arbitrary value) in order to make the speed + // visually closer to the other circle (at 60 fps), for comparison + deltaCircle.X += GetFrameTime() * 6.0f * speed; + // This circle can move faster or slower visually depending on the FPS + frameCircle.X += 0.1f * speed; + + // If either circle is off the screen, reset it back to the start + if (deltaCircle.X > screenWidth) + { + deltaCircle.X = 0; + } + + if (frameCircle.X > screenWidth) + { + frameCircle.X = 0; + } + + // Reset both circles positions + if (IsKeyPressed(KeyboardKey.R)) + { + deltaCircle.X = 0; + frameCircle.X = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + // Draw both circles to the screen + DrawCircleV(deltaCircle, circleRadius, Color.Red); + DrawCircleV(frameCircle, circleRadius, Color.Blue); + + // Draw the help text + // Determine what help text to show depending on the current FPS target + var fpsText = ""; + if (currentFps <= 0) + { + fpsText = $"FPS: unlimited ({GetFPS()})"; + } + else + { + fpsText = $"FPS: {GetFPS()} (target: {currentFps})"; + } + DrawText(fpsText, 10, 10, 20, Color.DarkGray); + DrawText($"Frame time: {GetFrameTime():F2} ms", 10, 30, 20, Color.DarkGray); + DrawText("Use the scroll wheel to change the fps limit, r to reset", 10, 50, 20, Color.DarkGray); + + // Draw the text above the circles + DrawText("FUNC: x += GetFrameTime()*speed", 10, 90, 20, Color.Red); + DrawText("FUNC: x += speed", 10, 240, 20, Color.Blue); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - delta time"); - int currentFps = 60; - - Vector2 deltaCircle = new Vector2(0, (float)screenHeight / 3.0f); - Vector2 frameCircle = new Vector2(0, (float)screenHeight * (2.0f / 3.0f)); - SetTargetFPS(60); //-------------------------------------------------------------------------------------- - const float speed = 10.0f; - const float circleRadius = 32.0f; + var game = new DeltaTime(); + game.Init(); // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // Adjust the FPS target based on the mouse wheel - float mouseWheel = GetMouseWheelMove(); - if (mouseWheel != 0) - { - currentFps += (int)mouseWheel; - if (currentFps < 0) - { - currentFps = 0; - } - SetTargetFPS(currentFps); - } - - // GetFrameTime() returns the time it took to draw the last frame, in seconds (usually called delta time) - // Uses the delta time to make the circle look like it's moving at a "consistent" speed regardless of FPS - - // Multiply by 6.0 (an arbitrary value) in order to make the speed - // visually closer to the other circle (at 60 fps), for comparison - deltaCircle.X += GetFrameTime() * 6.0f * speed; - // This circle can move faster or slower visually depending on the FPS - frameCircle.X += 0.1f * speed; - - // If either circle is off the screen, reset it back to the start - if (deltaCircle.X > screenWidth) - { - deltaCircle.X = 0; - } - - if (frameCircle.X > screenWidth) - { - frameCircle.X = 0; - } - - // Reset both circles positions - if (IsKeyPressed(KeyboardKey.R)) - { - deltaCircle.X = 0; - frameCircle.X = 0; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // Draw both circles to the screen - DrawCircleV(deltaCircle, circleRadius, Color.Red); - DrawCircleV(frameCircle, circleRadius, Color.Blue); - - // Draw the help text - // Determine what help text to show depending on the current FPS target - var fpsText = ""; - if (currentFps <= 0) - { - fpsText = $"FPS: unlimited ({GetFPS()})"; - } - else - { - fpsText = $"FPS: {GetFPS()} (target: {currentFps})"; - } - DrawText(fpsText, 10, 10, 20, Color.DarkGray); - DrawText($"Frame time: {GetFrameTime():F2} ms", 10, 30, 20, Color.DarkGray); - DrawText("Use the scroll wheel to change the fps limit, r to reset", 10, 50, 20, Color.DarkGray); - - // Draw the text above the circles - DrawText("FUNC: x += GetFrameTime()*speed", 10, 90, 20, Color.Red); - DrawText("FUNC: x += speed", 10, 240, 20, Color.Blue); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/DropFiles.cs b/Examples/Core/DropFiles.cs index 0d236e8..c0b5a86 100644 --- a/Examples/Core/DropFiles.cs +++ b/Examples/Core/DropFiles.cs @@ -1,83 +1,124 @@ /******************************************************************************************* * -* raylib [core] example - Windows drop files +* raylib [core] example - drop files * -* This example only works on platforms that support drag ref drop (Windows, Linux, OSX, Html5?) +* Example complexity rating: [★★☆☆] 2/4 * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* NOTE: This example only works on platforms that support drag & drop (Windows, Linux, OSX, Html5?) * -* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.3, last time updated with raylib 4.2 +* +* 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ +using System.Collections.Generic; using static Raylib_cs.Raylib; namespace Examples.Core; -public class DropFiles +public partial class DropFiles : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public const int MaxFilepathRecorded = 4096; + + public string Name => "Core / Drop Files"; + + public string Title => "raylib [core] example - drop files"; + + private List filePaths; + + public void Init() + { + // We will register a maximum of filepaths + filePaths = new(); + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsFileDropped()) + { + var droppedFiles = GetDroppedFiles(); + + for (var i = 0; i < droppedFiles.Length; i++) + { + if (filePaths.Count < (MaxFilepathRecorded - 1)) + { + filePaths.Add(droppedFiles[i]); + } + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(Color.RayWhite); + + if (filePaths.Count == 0) + { + DrawText("Drop your files to this window!", 100, 40, 20, Color.DarkGray); + } + else + { + DrawText("Dropped files:", 100, 40, 20, Color.DarkGray); + + for (var i = 0; i < filePaths.Count; i++) + { + if (i % 2 == 0) + { + DrawRectangle(0, 85 + 40 * i, screenWidth, 40, Fade(Color.LightGray, 0.5f)); + } + else + { + DrawRectangle(0, 85 + 40 * i, screenWidth, 40, Fade(Color.LightGray, 0.3f)); + } + + DrawText(filePaths[i], 120, 100 + 40 * i, 10, Color.Gray); + } + + DrawText("Drop new files...", 100, 110 + 40 * filePaths.Count, 20, Color.DarkGray); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - drop files"); - string[] files = new string[0]; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new DropFiles(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (IsFileDropped()) - { - files = Raylib.GetDroppedFiles(); - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - if (files.Length == 0) - { - DrawText("Drop your files to this window!", 100, 40, 20, Color.DarkGray); - } - else - { - DrawText("Dropped files:", 100, 40, 20, Color.DarkGray); - - for (int i = 0; i < files.Length; i++) - { - if (i % 2 == 0) - { - DrawRectangle(0, 85 + 40 * i, screenWidth, 40, ColorAlpha(Color.LightGray, 0.5f)); - } - else - { - DrawRectangle(0, 85 + 40 * i, screenWidth, 40, ColorAlpha(Color.LightGray, 0.3f)); - } - DrawText(files[i], 120, 100 + 40 * i, 10, Color.Gray); - } - - DrawText("Drop new files...", 100, 110 + 40 * files.Length, 20, Color.DarkGray); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/InputGamepad.cs b/Examples/Core/InputGamepad.cs index efd2ffb..fc1b915 100644 --- a/Examples/Core/InputGamepad.cs +++ b/Examples/Core/InputGamepad.cs @@ -1,32 +1,34 @@ /******************************************************************************************* - * - * raylib [core] example - input gamepad - * - * Example complexity rating: [★☆☆☆] 1/4 - * - * NOTE: This example requires a Gamepad connected to the system - * raylib is configured to work with the following gamepads: - * - Xbox 360 Controller (Xbox 360, Xbox One) - * - PLAYSTATION(R)3 Controller - * Check raylib.h for buttons configuration - * - * Example originally created with raylib 1.1, last time updated with raylib 4.2 - * - * 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) 2013-2025 Ramon Santamaria (@raysan5) - * - ********************************************************************************************/ - +* +* raylib [core] example - input gamepad +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* NOTE: This example requires a Gamepad connected to the system +* raylib is configured to work with the following gamepads: +* - Xbox 360 Controller (Xbox 360, Xbox One) +* - PLAYSTATION(R)3 Controller +* Check raylib.h for buttons configuration +* +* Example originally created with raylib 1.1, last time updated with raylib 4.2 +* +* 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) 2013-2025 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ using System.Numerics; using static Raylib_cs.Raylib; namespace Examples.Core; -public class InputGamepad +public partial class InputGamepad : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + // NOTE: Gamepad name ID depends on drivers and OS // These are some possible names the gamepads could have. public const string XBOX_ALIAS_1 = "xbox"; @@ -34,320 +36,503 @@ public class InputGamepad public const string PS_ALIAS_1 = "playstation"; public const string PS_ALIAS_2 = "sony"; + // Set axis deadzones + private const float leftStickDeadzoneX = 0.1f; + private const float leftStickDeadzoneY = 0.1f; + private const float rightStickDeadzoneX = 0.1f; + private const float rightStickDeadzoneY = 0.1f; + private const float leftTriggerDeadzone = -0.9f; + private const float rightTriggerDeadzone = -0.9f; + + private Texture2D texPs3Pad; + private Texture2D texXboxPad; + + private Rectangle vibrateButton; + + private int gamepad; // which gamepad to display + + public string Name => "Core / Input Gamepad"; + + public string Title => "raylib [core] example - input gamepad"; + + public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint; + + public void Init() + { + texPs3Pad = LoadTexture("resources/ps3.png"); + texXboxPad = LoadTexture("resources/xbox.png"); + + vibrateButton = new Rectangle(); + + gamepad = 0; // which gamepad to display + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KeyboardKey.Left) && gamepad > 0) + { + gamepad--; + } + + if (IsKeyPressed(KeyboardKey.Right)) + { + gamepad++; + } + + var mousePosition = GetMousePosition(); + + vibrateButton = new Rectangle(10, 70.0f + 20 * GetGamepadAxisCount(gamepad) + 20, 75, 24); + if (IsMouseButtonPressed(MouseButton.Left) && CheckCollisionPointRec(mousePosition, vibrateButton)) + { + SetGamepadVibration(gamepad, 1.0f, 1.0f, 1.0f); + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + if (IsGamepadAvailable(gamepad)) + { + var gamepadName = GetGamepadName_(gamepad); + DrawText($"GP{gamepad}: {gamepadName}", 10, 10, 10, Color.Black); + + // Get axis values + var leftStickX = GetGamepadAxisMovement(gamepad, GamepadAxis.LeftX); + var leftStickY = GetGamepadAxisMovement(gamepad, GamepadAxis.LeftY); + var rightStickX = GetGamepadAxisMovement(gamepad, GamepadAxis.RightX); + var rightStickY = GetGamepadAxisMovement(gamepad, GamepadAxis.RightY); + var leftTrigger = GetGamepadAxisMovement(gamepad, GamepadAxis.LeftTrigger); + var rightTrigger = GetGamepadAxisMovement(gamepad, GamepadAxis.RightTrigger); + + // Calculate deadzones + if (leftStickX > -leftStickDeadzoneX && leftStickX < leftStickDeadzoneX) + { + leftStickX = 0.0f; + } + + if (leftStickY > -leftStickDeadzoneY && leftStickY < leftStickDeadzoneY) + { + leftStickY = 0.0f; + } + + if (rightStickX > -rightStickDeadzoneX && rightStickX < rightStickDeadzoneX) + { + rightStickX = 0.0f; + } + + if (rightStickY > -rightStickDeadzoneY && rightStickY < rightStickDeadzoneY) + { + rightStickY = 0.0f; + } + + if (leftTrigger < leftTriggerDeadzone) + { + leftTrigger = -1.0f; + } + + if (rightTrigger < rightTriggerDeadzone) + { + rightTrigger = -1.0f; + } + + if (gamepadName.Contains(XBOX_ALIAS_1, StringComparison.OrdinalIgnoreCase) || + gamepadName.Contains(XBOX_ALIAS_2, StringComparison.OrdinalIgnoreCase)) + { + DrawTexture(texXboxPad, 0, 0, Color.DarkGray); + + // Draw buttons: xbox home + if (IsGamepadButtonDown(gamepad, GamepadButton.Middle)) + { + DrawCircle(394, 89, 19, Color.Red); + } + + // Draw buttons: basic + if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleRight)) + { + DrawCircle(436, 150, 9, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleLeft)) + { + DrawCircle(352, 150, 9, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceLeft)) + { + DrawCircle(501, 151, 15, Color.Blue); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceDown)) + { + DrawCircle(536, 187, 15, Color.Lime); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceRight)) + { + DrawCircle(572, 151, 15, Color.Maroon); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceUp)) + { + DrawCircle(536, 115, 15, Color.Gold); + } + + // Draw buttons: d-pad + DrawRectangle(317, 202, 19, 71, Color.Black); + DrawRectangle(293, 228, 69, 19, Color.Black); + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceUp)) + { + DrawRectangle(317, 202, 19, 26, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceDown)) + { + DrawRectangle(317, 202 + 45, 19, 26, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceLeft)) + { + DrawRectangle(292, 228, 25, 19, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceRight)) + { + DrawRectangle(292 + 44, 228, 26, 19, Color.Red); + } + + // Draw buttons: left-right back + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftTrigger1)) + { + DrawCircle(259, 61, 20, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.RightTrigger1)) + { + DrawCircle(536, 61, 20, Color.Red); + } + + // Draw axis: left joystick + var leftGamepadColor = Color.Black; + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftThumb)) + { + leftGamepadColor = Color.Red; + } + + DrawCircle(259, 152, 39, Color.Black); + DrawCircle(259, 152, 34, Color.LightGray); + DrawCircle(259 + (int)(leftStickX * 20), 152 + (int)(leftStickY * 20), 25, leftGamepadColor); + + // Draw axis: right joystick + var rightGamepadColor = Color.Black; + if (IsGamepadButtonDown(gamepad, GamepadButton.RightThumb)) + { + rightGamepadColor = Color.Red; + } + + DrawCircle(461, 237, 38, Color.Black); + DrawCircle(461, 237, 33, Color.LightGray); + DrawCircle(461 + (int)(rightStickX * 20), 237 + (int)(rightStickY * 20), 25, rightGamepadColor); + + // Draw axis: left-right triggers + DrawRectangle(170, 30, 15, 70, Color.Gray); + DrawRectangle(604, 30, 15, 70, Color.Gray); + DrawRectangle(170, 30, 15, (int)(((1 + leftTrigger) / 2) * 70), Color.Red); + DrawRectangle(604, 30, 15, (int)(((1 + rightTrigger) / 2) * 70), Color.Red); + } + else if (gamepadName.Contains(PS_ALIAS_1, StringComparison.OrdinalIgnoreCase) || gamepadName.Contains(PS_ALIAS_2, StringComparison.OrdinalIgnoreCase)) + { + DrawTexture(texPs3Pad, 0, 0, Color.DarkGray); + + // Draw buttons: ps + if (IsGamepadButtonDown(gamepad, GamepadButton.Middle)) + { + DrawCircle(396, 222, 13, Color.Red); + } + + // Draw buttons: basic + if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleLeft)) + { + DrawRectangle(328, 170, 32, 13, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleRight)) + { + DrawTriangle( + new Vector2(436, 168), + new Vector2(436, 185), + new Vector2(464, 177), + Color.Red + ); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceUp)) + { + DrawCircle(557, 144, 13, Color.Lime); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceRight)) + { + DrawCircle(586, 173, 13, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceDown)) + { + DrawCircle(557, 203, 13, Color.Violet); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceLeft)) + { + DrawCircle(527, 173, 13, Color.Pink); + } + + // Draw buttons: d-pad + DrawRectangle(225, 132, 24, 84, Color.Black); + DrawRectangle(195, 161, 84, 25, Color.Black); + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceUp)) + { + DrawRectangle(225, 132, 24, 29, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceDown)) + { + DrawRectangle(225, 132 + 54, 24, 30, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceLeft)) + { + DrawRectangle(195, 161, 30, 25, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceRight)) + { + DrawRectangle(195 + 54, 161, 30, 25, Color.Red); + } + + // Draw buttons: left-right back buttons + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftTrigger1)) + { + DrawCircle(239, 82, 20, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.RightTrigger1)) + { + DrawCircle(557, 82, 20, Color.Red); + } + + // Draw axis: left joystick + var leftGamepadColor = Color.Black; + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftThumb)) + { + leftGamepadColor = Color.Red; + } + + DrawCircle(319, 255, 35, Color.Black); + DrawCircle(319, 255, 31, Color.LightGray); + DrawCircle(319 + (int)(leftStickX * 20), 255 + (int)(leftStickY * 20), 25, leftGamepadColor); + + // Draw axis: right joystick + var rightGamepadColor = Color.Black; + if (IsGamepadButtonDown(gamepad, GamepadButton.RightThumb)) + { + rightGamepadColor = Color.Red; + } + + DrawCircle(475, 255, 35, Color.Black); + DrawCircle(475, 255, 31, Color.LightGray); + DrawCircle(475 + (int)(rightStickX * 20), 255 + (int)(rightStickY * 20), 25, rightGamepadColor); + + // Draw axis: left-right triggers + DrawRectangle(169, 48, 15, 70, Color.Gray); + DrawRectangle(611, 48, 15, 70, Color.Gray); + DrawRectangle(169, 48, 15, (int)(((1 + leftTrigger) / 2) * 70), Color.Red); + DrawRectangle(611, 48, 15, (int)(((1 + rightTrigger) / 2) * 70), Color.Red); + } + else + { + // Draw background: generic + DrawRectangleRounded(new Rectangle(175, 110, 460, 220), 0.3f, 16, Color.DarkGray); + + // Draw buttons: basic + DrawCircle(365, 170, 12, Color.RayWhite); + DrawCircle(405, 170, 12, Color.RayWhite); + DrawCircle(445, 170, 12, Color.RayWhite); + DrawCircle(516, 191, 17, Color.RayWhite); + DrawCircle(551, 227, 17, Color.RayWhite); + DrawCircle(587, 191, 17, Color.RayWhite); + DrawCircle(551, 155, 17, Color.RayWhite); + if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleLeft)) + { + DrawCircle(365, 170, 10, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.Middle)) + { + DrawCircle(405, 170, 10, Color.Green); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleRight)) + { + DrawCircle(445, 170, 10, Color.Blue); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceLeft)) + { + DrawCircle(516, 191, 15, Color.Gold); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceDown)) + { + DrawCircle(551, 227, 15, Color.Blue); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceRight)) + { + DrawCircle(587, 191, 15, Color.Green); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceUp)) + { + DrawCircle(551, 155, 15, Color.Red); + } + + // Draw buttons: d-pad + DrawRectangle(245, 145, 28, 88, Color.RayWhite); + DrawRectangle(215, 174, 88, 29, Color.RayWhite); + DrawRectangle(247, 147, 24, 84, Color.Black); + DrawRectangle(217, 176, 84, 25, Color.Black); + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceUp)) + { + DrawRectangle(247, 147, 24, 29, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceDown)) + { + DrawRectangle(247, 147 + 54, 24, 30, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceLeft)) + { + DrawRectangle(217, 176, 30, 25, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceRight)) + { + DrawRectangle(217 + 54, 176, 30, 25, Color.Red); + } + + // Draw buttons: left-right back + DrawRectangleRounded(new Rectangle(215, 98, 100, 10), 0.5f, 16, Color.DarkGray); + DrawRectangleRounded(new Rectangle(495, 98, 100, 10), 0.5f, 16, Color.DarkGray); + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftTrigger1)) + { + DrawRectangleRounded(new Rectangle(215, 98, 100, 10), 0.5f, 16, Color.Red); + } + + if (IsGamepadButtonDown(gamepad, GamepadButton.RightTrigger1)) + { + DrawRectangleRounded(new Rectangle(495, 98, 100, 10), 0.5f, 16, Color.Red); + } + + // Draw axis: left joystick + var leftGamepadColor = Color.Black; + if (IsGamepadButtonDown(gamepad, GamepadButton.LeftThumb)) + { + leftGamepadColor = Color.Red; + } + + DrawCircle(345, 260, 40, Color.Black); + DrawCircle(345, 260, 35, Color.LightGray); + DrawCircle(345 + (int)(leftStickX * 20), 260 + (int)(leftStickY * 20), 25, leftGamepadColor); + + // Draw axis: right joystick + var rightGamepadColor = Color.Black; + if (IsGamepadButtonDown(gamepad, GamepadButton.RightThumb)) + { + rightGamepadColor = Color.Red; + } + + DrawCircle(465, 260, 40, Color.Black); + DrawCircle(465, 260, 35, Color.LightGray); + DrawCircle(465 + (int)(rightStickX * 20), 260 + (int)(rightStickY * 20), 25, rightGamepadColor); + + // Draw axis: left-right triggers + DrawRectangle(151, 110, 15, 70, Color.Gray); + DrawRectangle(644, 110, 15, 70, Color.Gray); + DrawRectangle(151, 110, 15, (int)(((1 + leftTrigger) / 2) * 70), Color.Red); + DrawRectangle(644, 110, 15, (int)(((1 + rightTrigger) / 2) * 70), Color.Red); + } + + DrawText($"DETECTED AXIS [{GetGamepadAxisCount(gamepad)}]:", 10, 50, 10, Color.Maroon); + + for (var i = 0; i < GetGamepadAxisCount(gamepad); i++) + { + DrawText( + $"AXIS {i}: {GetGamepadAxisMovement(gamepad, (GamepadAxis)i):F2}", + 20, + 70 + 20 * i, + 10, + Color.DarkGray + ); + } + + // Draw vibrate button + DrawRectangleRec(vibrateButton, Color.SkyBlue); + DrawText("VIBRATE", (int)(vibrateButton.X + 14), (int)(vibrateButton.Y + 1), 10, Color.DarkGray); + + if (GetGamepadButtonPressed() != (int)GamepadButton.Unknown) + { + DrawText($"DETECTED BUTTON: {GetGamepadButtonPressed()}", 10, 430, 10, Color.Red); + } + else + { + DrawText("DETECTED BUTTON: NONE", 10, 430, 10, Color.Gray); + } + } + else + { + DrawText($"GP{gamepad}: NOT DETECTED", 10, 10, 10, Color.Gray); + DrawTexture(texXboxPad, 0, 0, Color.LightGray); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texPs3Pad); + UnloadTexture(texXboxPad); + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + SetConfigFlags(ConfigFlags.Msaa4xHint); // Set MSAA 4X hint before windows creation - // Set MSAA 4X hint before windows creation - SetConfigFlags(ConfigFlags.Msaa4xHint); - InitWindow(screenWidth, screenHeight, "raylib [core] example - gamepad input"); + InitWindow(screenWidth, screenHeight, "raylib [core] example - input gamepad"); - Texture2D texPs3Pad = LoadTexture("resources/ps3.png"); - Texture2D texXboxPad = LoadTexture("resources/xbox.png"); - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- - int gamepad = 0; - Rectangle vibrateButton = new Rectangle(); + var game = new InputGamepad(); + game.Init(); // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyPressed(KeyboardKey.Left) && gamepad > 0) - { - gamepad--; - } - - if (IsKeyPressed(KeyboardKey.Right)) - { - gamepad++; - } - - Vector2 mousePosition = GetMousePosition(); - - vibrateButton = new Rectangle(10, 70.0f + 20 * GetGamepadAxisCount(gamepad) + 20, 75, 24); - if (IsMouseButtonPressed(MouseButton.Left) && CheckCollisionPointRec(mousePosition, vibrateButton)) - { - SetGamepadVibration(gamepad, 1.0f, 1.0f, 1.0f); - } - - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - if (IsGamepadAvailable(gamepad)) - { - string gamepadName = GetGamepadName_(gamepad); - DrawText($"GP{gamepad}: {gamepadName}", 10, 10, 10, Color.Black); - - if (gamepadName.Contains(XBOX_ALIAS_1, StringComparison.OrdinalIgnoreCase) || - gamepadName.Contains(XBOX_ALIAS_2, StringComparison.OrdinalIgnoreCase)) - { - DrawTexture(texXboxPad, 0, 0, Color.DarkGray); - - // Draw buttons: xbox home - if (IsGamepadButtonDown(gamepad, GamepadButton.Middle)) - { - DrawCircle(394, 89, 19, Color.Red); - } - - // Draw buttons: basic - if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleRight)) - { - DrawCircle(436, 150, 9, Color.Red); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleLeft)) - { - DrawCircle(352, 150, 9, Color.Red); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceLeft)) - { - DrawCircle(501, 151, 15, Color.Blue); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceDown)) - { - DrawCircle(536, 187, 15, Color.Lime); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceRight)) - { - DrawCircle(572, 151, 15, Color.Maroon); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceUp)) - { - DrawCircle(536, 115, 15, Color.Gold); - } - - // Draw buttons: d-pad - DrawRectangle(317, 202, 19, 71, Color.Black); - DrawRectangle(293, 228, 69, 19, Color.Black); - if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceUp)) - { - DrawRectangle(317, 202, 19, 26, Color.Red); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceDown)) - { - DrawRectangle(317, 202 + 45, 19, 26, Color.Red); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceLeft)) - { - DrawRectangle(292, 228, 25, 19, Color.Red); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceRight)) - { - DrawRectangle(292 + 44, 228, 26, 19, Color.Red); - } - - // Draw buttons: left-right back - if (IsGamepadButtonDown(gamepad, GamepadButton.LeftTrigger1)) - { - DrawCircle(259, 61, 20, Color.Red); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.RightTrigger1)) - { - DrawCircle(536, 61, 20, Color.Red); - } - - // Draw axis: left joystick - DrawCircle(259, 152, 39, Color.Black); - DrawCircle(259, 152, 34, Color.LightGray); - DrawCircle( - 259 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.LeftX) * 20), - 152 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.LeftY) * 20), - 25, - Color.Black - ); - - // Draw axis: right joystick - DrawCircle(461, 237, 38, Color.Black); - DrawCircle(461, 237, 33, Color.LightGray); - DrawCircle( - 461 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.RightX) * 20), - 237 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.RightY) * 20), - 25, Color.Black - ); - - // Draw axis: left-right triggers - float leftTriggerX = GetGamepadAxisMovement(gamepad, GamepadAxis.LeftTrigger); - float rightTriggerX = GetGamepadAxisMovement(gamepad, GamepadAxis.RightTrigger); - DrawRectangle(170, 30, 15, 70, Color.Gray); - DrawRectangle(604, 30, 15, 70, Color.Gray); - DrawRectangle(170, 30, 15, (int)(((1.0f + leftTriggerX) / 2.0f) * 70), Color.Red); - DrawRectangle(604, 30, 15, (int)(((1.0f + rightTriggerX) / 2.0f) * 70), Color.Red); - } - else if (gamepadName.Contains(PS_ALIAS_1, StringComparison.OrdinalIgnoreCase) || gamepadName.Contains(PS_ALIAS_2, StringComparison.OrdinalIgnoreCase)) - { - DrawTexture(texPs3Pad, 0, 0, Color.DarkGray); - - // Draw buttons: ps - if (IsGamepadButtonDown(gamepad, GamepadButton.Middle)) - { - DrawCircle(396, 222, 13, Color.Red); - } - - // Draw buttons: basic - if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleLeft)) - { - DrawRectangle(328, 170, 32, 13, Color.Red); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleRight)) - { - DrawTriangle( - new Vector2(436, 168), - new Vector2(436, 185), - new Vector2(464, 177), - Color.Red - ); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceUp)) - { - DrawCircle(557, 144, 13, Color.Lime); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceRight)) - { - DrawCircle(586, 173, 13, Color.Red); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceDown)) - { - DrawCircle(557, 203, 13, Color.Violet); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceLeft)) - { - DrawCircle(527, 173, 13, Color.Pink); - } - - // Draw buttons: d-pad - DrawRectangle(225, 132, 24, 84, Color.Black); - DrawRectangle(195, 161, 84, 25, Color.Black); - if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceUp)) - { - DrawRectangle(225, 132, 24, 29, Color.Red); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceDown)) - { - DrawRectangle(225, 132 + 54, 24, 30, Color.Red); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceLeft)) - { - DrawRectangle(195, 161, 30, 25, Color.Red); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceRight)) - { - DrawRectangle(195 + 54, 161, 30, 25, Color.Red); - } - - // Draw buttons: left-right back buttons - if (IsGamepadButtonDown(gamepad, GamepadButton.LeftTrigger1)) - { - DrawCircle(239, 82, 20, Color.Red); - } - - if (IsGamepadButtonDown(gamepad, GamepadButton.RightTrigger1)) - { - DrawCircle(557, 82, 20, Color.Red); - } - - // Draw axis: left joystick - DrawCircle(319, 255, 35, Color.Black); - DrawCircle(319, 255, 31, Color.LightGray); - DrawCircle( - 319 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.LeftX) * 20), - 255 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.LeftY) * 20), - 25, - Color.Black - ); - - // Draw axis: right joystick - DrawCircle(475, 255, 35, Color.Black); - DrawCircle(475, 255, 31, Color.LightGray); - DrawCircle( - 475 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.RightX) * 20), - 255 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.RightY) * 20), - 25, - Color.Black - ); - - // Draw axis: left-right triggers - float leftTriggerX = GetGamepadAxisMovement(gamepad, GamepadAxis.LeftTrigger); - float rightTriggerX = GetGamepadAxisMovement(gamepad, GamepadAxis.RightTrigger); - DrawRectangle(169, 48, 15, 70, Color.Gray); - DrawRectangle(611, 48, 15, 70, Color.Gray); - DrawRectangle(169, 48, 15, (int)(((1.0f - leftTriggerX) / 2.0f) * 70), Color.Red); - DrawRectangle(611, 48, 15, (int)(((1.0f - rightTriggerX) / 2.0f) * 70), Color.Red); - } - else - { - DrawText("- GENERIC GAMEPAD -", 280, 180, 20, Color.Gray); - // TODO: Draw generic gamepad - } - - DrawText($"DETECTED AXIS [{GetGamepadAxisCount(gamepad)}]:", 10, 50, 10, Color.Maroon); - - for (int i = 0; i < GetGamepadAxisCount(gamepad); i++) - { - DrawText( - $"AXIS {i}: {GetGamepadAxisMovement(gamepad, (GamepadAxis)i)}", - 20, - 70 + 20 * i, - 10, - Color.DarkGray - ); - } - - DrawRectangleRec(vibrateButton, Color.SkyBlue); - DrawText("VIBRATE", (int)(vibrateButton.X + 14), (int)(vibrateButton.Y + 1), 10, Color.DarkGray); - - - if (GetGamepadButtonPressed() != (int)GamepadButton.Unknown) - { - DrawText($"DETECTED BUTTON: {GetGamepadButtonPressed()}", 10, 430, 10, Color.Red); - } - else - { - DrawText("DETECTED BUTTON: NONE", 10, 430, 10, Color.Gray); - } - } - else - { - DrawText($"GP{gamepad}: NOT DETECTED", 10, 10, 10, Color.Gray); - DrawTexture(texXboxPad, 0, 0, Color.LightGray); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texPs3Pad); - UnloadTexture(texXboxPad); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/InputGestures.cs b/Examples/Core/InputGestures.cs index fda7d5c..50e7edf 100644 --- a/Examples/Core/InputGestures.cs +++ b/Examples/Core/InputGestures.cs @@ -18,145 +18,176 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class InputGestures +public partial class InputGestures : IExample { public const int MaxGestureStrings = 20; + private const int screenWidth = 800; + private const int screenHeight = 450; + + private Vector2 touchPosition; + private Rectangle touchArea; + + private int gesturesCount; + private string[] gestureStrings; + + private Gesture currentGesture; + private Gesture lastGesture; + + public string Name => "Core / Input Gestures"; + + public string Title => "raylib [core] example - input gestures"; + + public void Init() + { + touchPosition = new(0, 0); + touchArea = new(220, 10, screenWidth - 230, screenHeight - 20); + + gesturesCount = 0; + gestureStrings = new string[MaxGestureStrings]; + + currentGesture = Gesture.None; + lastGesture = Gesture.None; + + // SetGesturesEnabled(0b0000000000001001); // Enable only some gestures to be detected + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + lastGesture = currentGesture; + currentGesture = GetGestureDetected(); + touchPosition = GetTouchPosition(0); + + if (CheckCollisionPointRec(touchPosition, touchArea) && (currentGesture != Gesture.None)) + { + if (currentGesture != lastGesture) + { + // Store gesture string + switch ((Gesture)currentGesture) + { + case Gesture.Tap: + gestureStrings[gesturesCount] = "GESTURE TAP"; + break; + case Gesture.DoubleTap: + gestureStrings[gesturesCount] = "GESTURE DOUBLETAP"; + break; + case Gesture.Hold: + gestureStrings[gesturesCount] = "GESTURE HOLD"; + break; + case Gesture.Drag: + gestureStrings[gesturesCount] = "GESTURE DRAG"; + break; + case Gesture.SwipeRight: + gestureStrings[gesturesCount] = "GESTURE SWIPE RIGHT"; + break; + case Gesture.SwipeLeft: + gestureStrings[gesturesCount] = "GESTURE SWIPE LEFT"; + break; + case Gesture.SwipeUp: + gestureStrings[gesturesCount] = "GESTURE SWIPE UP"; + break; + case Gesture.SwipeDown: + gestureStrings[gesturesCount] = "GESTURE SWIPE DOWN"; + break; + case Gesture.PinchIn: + gestureStrings[gesturesCount] = "GESTURE PINCH IN"; + break; + case Gesture.PinchOut: + gestureStrings[gesturesCount] = "GESTURE PINCH OUT"; + break; + default: + break; + } + + gesturesCount++; + + // Reset gestures strings + if (gesturesCount >= MaxGestureStrings) + { + for (var i = 0; i < MaxGestureStrings; i++) + { + gestureStrings[i] = " "; + } + gesturesCount = 0; + } + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawRectangleRec(touchArea, Color.Gray); + DrawRectangle(225, 15, screenWidth - 240, screenHeight - 30, Color.RayWhite); + + DrawText("GESTURES TEST AREA", screenWidth - 270, screenHeight - 40, 20, Fade(Color.Gray, 0.5f)); + + for (var i = 0; i < gesturesCount; i++) + { + if (i % 2 == 0) + { + DrawRectangle(10, 30 + 20 * i, 200, 20, Fade(Color.LightGray, 0.5f)); + } + else + { + DrawRectangle(10, 30 + 20 * i, 200, 20, Fade(Color.LightGray, 0.3f)); + } + + if (i < gesturesCount - 1) + { + DrawText(gestureStrings[i], 35, 36 + 20 * i, 10, Color.DarkGray); + } + else + { + DrawText(gestureStrings[i], 35, 36 + 20 * i, 10, Color.Maroon); + } + } + + DrawRectangleLines(10, 29, 200, screenHeight - 50, Color.Gray); + DrawText("DETECTED GESTURES", 50, 15, 10, Color.Gray); + + if (currentGesture != Gesture.None) + { + DrawCircleV(touchPosition, 30, Color.Maroon); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - input gestures"); - Vector2 touchPosition = new(0, 0); - Rectangle touchArea = new(220, 10, screenWidth - 230, screenHeight - 20); - - int gesturesCount = 0; - string[] gestureStrings = new string[MaxGestureStrings]; - - Gesture currentGesture = Gesture.None; - Gesture lastGesture = Gesture.None; - - // SetGesturesEnabled(0b0000000000001001); // Enable only some gestures to be detected - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new InputGestures(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - lastGesture = currentGesture; - currentGesture = GetGestureDetected(); - touchPosition = GetTouchPosition(0); - - if (CheckCollisionPointRec(touchPosition, touchArea) && (currentGesture != Gesture.None)) - { - if (currentGesture != lastGesture) - { - // Store gesture string - switch ((Gesture)currentGesture) - { - case Gesture.Tap: - gestureStrings[gesturesCount] = "GESTURE TAP"; - break; - case Gesture.DoubleTap: - gestureStrings[gesturesCount] = "GESTURE DOUBLETAP"; - break; - case Gesture.Hold: - gestureStrings[gesturesCount] = "GESTURE HOLD"; - break; - case Gesture.Drag: - gestureStrings[gesturesCount] = "GESTURE DRAG"; - break; - case Gesture.SwipeRight: - gestureStrings[gesturesCount] = "GESTURE SWIPE RIGHT"; - break; - case Gesture.SwipeLeft: - gestureStrings[gesturesCount] = "GESTURE SWIPE LEFT"; - break; - case Gesture.SwipeUp: - gestureStrings[gesturesCount] = "GESTURE SWIPE UP"; - break; - case Gesture.SwipeDown: - gestureStrings[gesturesCount] = "GESTURE SWIPE DOWN"; - break; - case Gesture.PinchIn: - gestureStrings[gesturesCount] = "GESTURE PINCH IN"; - break; - case Gesture.PinchOut: - gestureStrings[gesturesCount] = "GESTURE PINCH OUT"; - break; - default: - break; - } - - gesturesCount++; - - // Reset gestures strings - if (gesturesCount >= MaxGestureStrings) - { - for (int i = 0; i < MaxGestureStrings; i++) - { - gestureStrings[i] = " "; - } - gesturesCount = 0; - } - } - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawRectangleRec(touchArea, Color.Gray); - DrawRectangle(225, 15, screenWidth - 240, screenHeight - 30, Color.RayWhite); - - DrawText("GESTURES TEST AREA", screenWidth - 270, screenHeight - 40, 20, ColorAlpha(Color.Gray, 0.5f)); - - for (int i = 0; i < gesturesCount; i++) - { - if (i % 2 == 0) - { - DrawRectangle(10, 30 + 20 * i, 200, 20, ColorAlpha(Color.LightGray, 0.5f)); - } - else - { - DrawRectangle(10, 30 + 20 * i, 200, 20, ColorAlpha(Color.LightGray, 0.3f)); - } - - if (i < gesturesCount - 1) - { - DrawText(gestureStrings[i], 35, 36 + 20 * i, 10, Color.DarkGray); - } - else - { - DrawText(gestureStrings[i], 35, 36 + 20 * i, 10, Color.Maroon); - } - } - - DrawRectangleLines(10, 29, 200, screenHeight - 50, Color.Gray); - DrawText("DETECTED GESTURES", 50, 15, 10, Color.Gray); - - if (currentGesture != Gesture.None) - { - DrawCircleV(touchPosition, 30, Color.Maroon); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- + return 0; } } diff --git a/Examples/Core/InputGesturesTestBed.cs b/Examples/Core/InputGesturesTestBed.cs index 893c5dd..ee3d883 100644 --- a/Examples/Core/InputGesturesTestBed.cs +++ b/Examples/Core/InputGesturesTestBed.cs @@ -1,406 +1,454 @@ /******************************************************************************************* - * - * raylib [core] example - input gestures testbed - * - * Example complexity rating: [★★★☆] 3/4 - * - * Example originally created with raylib 5.0, last time updated with raylib 6.0 - * - * Example contributed by ubkp (@ubkp) 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-2025 ubkp (@ubkp) - * - ********************************************************************************************/ +* +* raylib [core] example - input gestures testbed +* +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 5.0, last time updated with raylib 6.0 +* +* Example contributed by ubkp (@ubkp) 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-2025 ubkp (@ubkp) +* +********************************************************************************************/ +using System; using System.Numerics; +using static Raylib_cs.Raylib; namespace Examples.Core; -using static Raylib_cs.Raylib; - -public class InputGesturesTestBed +public partial class InputGesturesTestBed : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + public const int GESTURE_LOG_SIZE = 20; public const int MAX_TOUCH_COUNT = 32; - //------------------------------------------------------------------------------------ - // Program main entry point - //------------------------------------------------------------------------------------ + public string Name => "Core / Input Gestures Test Bed"; - public static int Main() + public string Title => "raylib [core] example - input gestures testbed"; + + private Vector2 messagePosition; + + // Last gesture variables definitions + private Gesture lastGesture; + private Vector2 lastGesturePosition; + + // Gesture log variables definitions + // NOTE: The gesture log uses an array (as an inverted circular queue) to store the performed gestures + private string[] gestureLog; + + // NOTE: The index for the inverted circular queue (moving from last to first direction, then looping around) + private int gestureLogIndex; + private Gesture previousGesture; + + // Log mode values: + // - 0 shows repeated events + // - 1 hides repeated events + // - 2 shows repeated events but hide hold events + // - 3 hides repeated events and hide hold events + private int logMode; + + private Color gestureColor; + private Rectangle logButton1; + private Rectangle logButton2; + private Vector2 gestureLogPosition; + + // Protractor variables definitions + private float angleLength; + private float currentAngleDegrees; + private Vector2 finalVector; + private Vector2 protractorPosition; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [core] example - input gestures testbed"); - - Vector2 messagePosition = new Vector2(160, 7); + messagePosition = new Vector2(160, 7); // Last gesture variables definitions - Gesture lastGesture = 0; - Vector2 lastGesturePosition = new Vector2(165, 130); + lastGesture = 0; + lastGesturePosition = new Vector2(165, 130); // Gesture log variables definitions // NOTE: The gesture log uses an array (as an inverted circular queue) to store the performed gestures - string[] gestureLog = new string[GESTURE_LOG_SIZE + 1]; - for (int i = 0; i < GESTURE_LOG_SIZE; i++) + gestureLog = new string[GESTURE_LOG_SIZE + 1]; + for (var i = 0; i < GESTURE_LOG_SIZE; i++) { gestureLog[i] = new string(new char[12]); } - ; + // NOTE: The index for the inverted circular queue (moving from last to first direction, then looping around) - int gestureLogIndex = GESTURE_LOG_SIZE; - Gesture previousGesture = 0; + gestureLogIndex = GESTURE_LOG_SIZE; + previousGesture = 0; // Log mode values: // - 0 shows repeated events // - 1 hides repeated events // - 2 shows repeated events but hide hold events // - 3 hides repeated events and hide hold events - int logMode = 1; + logMode = 1; - Color gestureColor = new Color(0, 0, 0, 255); - Rectangle logButton1 = new Rectangle(53, 7, 48, 26); - Rectangle logButton2 = new Rectangle(108, 7, 36, 26); - Vector2 gestureLogPosition = new Vector2(10, 10); + gestureColor = new Color(0, 0, 0, 255); + logButton1 = new Rectangle(53, 7, 48, 26); + logButton2 = new Rectangle(108, 7, 36, 26); + gestureLogPosition = new Vector2(10, 10); // Protractor variables definitions - float angleLength = 90.0f; - float currentAngleDegrees = 0.0f; - Vector2 finalVector = new Vector2(0.0f, 0.0f); - Vector2 protractorPosition = new Vector2(266.0f, 315.0f); + angleLength = 90.0f; + currentAngleDegrees = 0.0f; + finalVector = new Vector2(0.0f, 0.0f); + protractorPosition = new Vector2(266.0f, 315.0f); + } - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + public void Update() + { + // Update //-------------------------------------------------------------------------------------- + // Handle common gestures data + int i, ii; // Iterators that will be reused by all for loops + var currentGesture = GetGestureDetected(); + var currentDragDegrees = GetGestureDragAngle(); + var currentPitchDegrees = GetGesturePinchAngle(); + var touchCount = GetTouchPointCount(); - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key + // Handle last gesture + if ((currentGesture != 0) && ((int)currentGesture != 4) && (currentGesture != previousGesture)) { - // Update - //-------------------------------------------------------------------------------------- - // Handle common gestures data - int i, ii; // Iterators that will be reused by all for loops - Gesture currentGesture = GetGestureDetected(); - float currentDragDegrees = GetGestureDragAngle(); - float currentPitchDegrees = GetGesturePinchAngle(); - int touchCount = GetTouchPointCount(); + lastGesture = currentGesture; // Filter the meaningful gestures (1, 2, 8 to 512) for the display + } - // Handle last gesture - if ((currentGesture != 0) && ((int)currentGesture != 4) && (currentGesture != previousGesture)) + // Handle gesture log + if (IsMouseButtonReleased(MouseButton.Left)) + { + if (CheckCollisionPointRec(GetMousePosition(), logButton1)) { - lastGesture = currentGesture; // Filter the meaningful gestures (1, 2, 8 to 512) for the display - } - - // Handle gesture log - if (IsMouseButtonReleased(MouseButton.Left)) - { - if (CheckCollisionPointRec(GetMousePosition(), logButton1)) + switch (logMode) { - switch (logMode) - { - case 3: - logMode = 2; - break; - case 2: - logMode = 3; - break; - case 1: - logMode = 0; - break; - default: - logMode = 1; - break; - } - } - else if (CheckCollisionPointRec(GetMousePosition(), logButton2)) - { - switch (logMode) - { - case 3: - logMode = 1; - break; - case 2: - logMode = 0; - break; - case 1: - logMode = 3; - break; - default: - logMode = 2; - break; - } + case 3: + logMode = 2; + break; + case 2: + logMode = 3; + break; + case 1: + logMode = 0; + break; + default: + logMode = 1; + break; } } - - int fillLog = 0; // Gate variable to be used to allow or not the gesture log to be filled - if (currentGesture != 0) + else if (CheckCollisionPointRec(GetMousePosition(), logButton2)) { - if (logMode == 3) // 3 hides repeated events and hide hold events + switch (logMode) { - if ((((int)currentGesture != 4) && (currentGesture != previousGesture)) || ((int)currentGesture < 3)) - { - fillLog = 1; - } + case 3: + logMode = 1; + break; + case 2: + logMode = 0; + break; + case 1: + logMode = 3; + break; + default: + logMode = 2; + break; } - else if (logMode == 2) // 2 shows repeated events but hide hold events - { - if ((int)currentGesture != 4) - { - fillLog = 1; - } - } - else if (logMode == 1) // 1 hides repeated events - { - if (currentGesture != previousGesture) - { - fillLog = 1; - } - } - else // 0 shows repeated events + } + } + + var fillLog = 0; // Gate variable to be used to allow or not the gesture log to be filled + if (currentGesture != 0) + { + if (logMode == 3) // 3 hides repeated events and hide hold events + { + if ((((int)currentGesture != 4) && (currentGesture != previousGesture)) || ((int)currentGesture < 3)) { fillLog = 1; } } - - if (fillLog > 0) // If one of the conditions from logMode was met, fill the gesture log + else if (logMode == 2) // 2 shows repeated events but hide hold events { - previousGesture = currentGesture; - gestureColor = GetGestureColor((int)currentGesture); - if (gestureLogIndex <= 0) + if ((int)currentGesture != 4) { - gestureLogIndex = GESTURE_LOG_SIZE; - } - gestureLogIndex--; - - // Copy the gesture respective name to the gesture log array - gestureLog[gestureLogIndex] = GetGestureName((int)currentGesture); - } - - // Handle protractor - if ((int)currentGesture > 255) - { - currentAngleDegrees = currentPitchDegrees; // Pinch In and Pinch Out - } - else if ((int)currentGesture > 15) - { - currentAngleDegrees = currentDragDegrees; // Swipe Right, Swipe Left, Swipe Up and Swipe Down - } - else if (currentGesture > 0) - { - currentAngleDegrees = 0.0f; // Tap, Doubletap, Hold and Grab - } - - float currentAngleRadians = - ((currentAngleDegrees + 90.0f) * MathF.PI / 180); // Convert the current angle to Radians - // Calculate the final vector for display - finalVector = new Vector2( - (angleLength * MathF.Sin(currentAngleRadians)) + protractorPosition.X, - (angleLength * MathF.Cos(currentAngleRadians)) + protractorPosition.Y - ) - ; - - // Handle touch and mouse pointer points - Vector2[] touchPosition = new Vector2[MAX_TOUCH_COUNT]; - - Vector2 mousePosition = Vector2.Zero; - if (currentGesture != Gesture.None) - { - if (touchCount != 0) - { - for (i = 0; i < touchCount; i++) - { - touchPosition[i] = GetTouchPosition(i); // Fill the touch positions - } - } - else - { - mousePosition = GetMousePosition(); + fillLog = 1; } } - //-------------------------------------------------------------------------------------- - - // Draw - //-------------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // Draw common elements - DrawText("*", (int)messagePosition.X + 5, (int)messagePosition.Y + 5, 10, Color.Black); - DrawText("Example optimized for Web/HTML5\non Smartphones with Touch Screen.", (int)messagePosition.X + 15, - (int)messagePosition.Y + 5, 10, Color.Black); - DrawText("*", (int)messagePosition.X + 5, (int)messagePosition.Y + 35, 10, Color.Black); - DrawText("While running on Desktop Web Browsers,\ninspect and turn on Touch Emulation.", - (int)messagePosition.X + 15, (int)messagePosition.Y + 35, 10, Color.Black); - - // Draw last gesture - DrawText("Last gesture", (int)lastGesturePosition.X + 33, (int)lastGesturePosition.Y - 47, 20, Color.Black); - DrawText("Swipe Tap Pinch Touch", (int)lastGesturePosition.X + 17, - (int)lastGesturePosition.Y - 18, 10, Color.Black); - DrawRectangle((int)lastGesturePosition.X + 20, (int)lastGesturePosition.Y, 20, 20, - lastGesture == Gesture.SwipeUp ? Color.Red : Color.LightGray); - DrawRectangle((int)lastGesturePosition.X, (int)lastGesturePosition.Y + 20, 20, 20, - lastGesture == Gesture.SwipeLeft ? Color.Red : Color.LightGray); - DrawRectangle((int)lastGesturePosition.X + 40, (int)lastGesturePosition.Y + 20, 20, 20, - lastGesture == Gesture.SwipeRight ? Color.Red : Color.LightGray); - DrawRectangle((int)lastGesturePosition.X + 20, (int)lastGesturePosition.Y + 40, 20, 20, - lastGesture == Gesture.SwipeDown ? Color.Red : Color.LightGray); - DrawCircle((int)lastGesturePosition.X + 80, (int)lastGesturePosition.Y + 16, 10, - lastGesture == Gesture.Tap ? Color.Blue : Color.LightGray); - DrawRing(new Vector2( - lastGesturePosition.X + 103, lastGesturePosition.Y + 16 - ), 6.0f, 11.0f, 0.0f, 360.0f, 0, lastGesture == Gesture.Drag ? Color.Lime : Color.LightGray); - DrawCircle((int)lastGesturePosition.X + 80, (int)lastGesturePosition.Y + 43, 10, - lastGesture == Gesture.DoubleTap ? Color.SkyBlue : Color.LightGray); - DrawCircle((int)lastGesturePosition.X + 103, (int)lastGesturePosition.Y + 43, 10, - lastGesture == Gesture.DoubleTap ? Color.SkyBlue : Color.LightGray); - DrawTriangle(new Vector2( - lastGesturePosition.X + 122, lastGesturePosition.Y + 16 - ), new Vector2( - lastGesturePosition.X + 137, lastGesturePosition.Y + 26 - ), new Vector2( - lastGesturePosition.X + 137, lastGesturePosition.Y + 6 - ), lastGesture == Gesture.PinchOut ? Color.Orange : Color.LightGray); - DrawTriangle(new Vector2( - lastGesturePosition.X + 147, lastGesturePosition.Y + 6 - ), new Vector2( - lastGesturePosition.X + 147, lastGesturePosition.Y + 26 - ), new Vector2( - lastGesturePosition.X + 162, lastGesturePosition.Y + 16 - ), lastGesture == Gesture.PinchOut ? Color.Orange : Color.Gray); - DrawTriangle(new Vector2( - lastGesturePosition.X + 125, lastGesturePosition.Y + 33 - ), new Vector2( - lastGesturePosition.X + 125, lastGesturePosition.Y + 53 - ), new Vector2( - lastGesturePosition.X + 140, lastGesturePosition.Y + 43 - ), lastGesture == Gesture.PinchIn ? Color.Violet : Color.LightGray); - DrawTriangle(new Vector2( - lastGesturePosition.X + 144, lastGesturePosition.Y + 43 - ), new Vector2( - lastGesturePosition.X + 159, lastGesturePosition.Y + 53 - ), new Vector2( - lastGesturePosition.X + 159, lastGesturePosition.Y + 33 - ), lastGesture == Gesture.PinchIn ? Color.Violet : Color.LightGray); - for (i = 0; i < 4; i++) + else if (logMode == 1) // 1 hides repeated events { - DrawCircle((int)lastGesturePosition.X + 180, (int)lastGesturePosition.Y + 7 + i * 15, 5, - touchCount <= i ? Color.LightGray : gestureColor); - } - - // Draw gesture log - DrawText("Log", (int)gestureLogPosition.X, (int)gestureLogPosition.Y, 20, Color.Black); - - // Loop in both directions to print the gesture log array in the inverted order (and looping around if the index started somewhere in the middle) - for (i = 0, ii = gestureLogIndex; i < GESTURE_LOG_SIZE; i++, ii = (ii + 1) % GESTURE_LOG_SIZE) - { - DrawText(gestureLog[ii], (int)gestureLogPosition.X, (int)gestureLogPosition.Y + 410 - i * 20, 20, - (i == 0 ? gestureColor : Color.LightGray)); - } - - Color logButton1Color, logButton2Color; - switch (logMode) - { - case 3: - logButton1Color = Color.Maroon; - logButton2Color = Color.Maroon; - break; - case 2: - logButton1Color = Color.Gray; - logButton2Color = Color.Maroon; - break; - case 1: - logButton1Color = Color.Maroon; - logButton2Color = Color.Gray; - break; - default: - logButton1Color = Color.Gray; - logButton2Color = Color.Gray; - break; - } - - DrawRectangleRec(logButton1, logButton1Color); - DrawText("Hide", (int)logButton1.X + 7, (int)logButton1.Y + 3, 10, Color.White); - DrawText("Repeat", (int)logButton1.X + 7, (int)logButton1.Y + 13, 10, Color.White); - DrawRectangleRec(logButton2, logButton2Color); - DrawText("Hide", (int)logButton1.X + 62, (int)logButton1.Y + 3, 10, Color.White); - DrawText("Hold", (int)logButton1.X + 62, (int)logButton1.Y + 13, 10, Color.White); - - // Draw protractor - DrawText("Angle", (int)protractorPosition.X + 55, (int)protractorPosition.Y + 76, 10, Color.Black); - - // Note: Official it's using raylibs functions for string manipulation. But in C# it will end up in an unsafe handling. - string angleString = currentAngleDegrees.ToString("F3"); - int angleStringDot = angleString.IndexOf('.'); - string angleStringTrim = angleString.Substring(0, angleStringDot + 3); - - DrawText(angleStringTrim, (int)protractorPosition.X + 55, (int)protractorPosition.Y + 92, 20, gestureColor); - DrawCircleV(protractorPosition, 80.0f, Color.White); - DrawLineEx(new Vector2( - protractorPosition.X - 90, protractorPosition.Y - ), new Vector2( - protractorPosition.X + 90, protractorPosition.Y - ), 3.0f, Color.LightGray); - DrawLineEx(new Vector2( - protractorPosition.X, protractorPosition.Y - 90 - ), new Vector2( - protractorPosition.X, protractorPosition.Y + 90 - ), 3.0f, Color.LightGray); - DrawLineEx(new Vector2( - protractorPosition.X - 80, protractorPosition.Y - 45 - ), new Vector2( - protractorPosition.X + 80, protractorPosition.Y + 45 - ), 3.0f, Color.Green); - DrawLineEx(new Vector2( - protractorPosition.X - 80, protractorPosition.Y + 45 - ), new Vector2( - protractorPosition.X + 80, protractorPosition.Y - 45 - ), 3.0f, Color.Green); - DrawText("0", (int)protractorPosition.X + 96, (int)protractorPosition.Y - 9, 20, Color.Black); - DrawText("30", (int)protractorPosition.X + 74, (int)protractorPosition.Y - 68, 20, Color.Black); - DrawText("90", (int)protractorPosition.X - 11, (int)protractorPosition.Y - 110, 20, Color.Black); - DrawText("150", (int)protractorPosition.X - 100, (int)protractorPosition.Y - 68, 20, Color.Black); - DrawText("180", (int)protractorPosition.X - 124, (int)protractorPosition.Y - 9, 20, Color.Black); - DrawText("210", (int)protractorPosition.X - 100, (int)protractorPosition.Y + 50, 20, Color.Black); - DrawText("270", (int)protractorPosition.X - 18, (int)protractorPosition.Y + 92, 20, Color.Black); - DrawText("330", (int)protractorPosition.X + 72, (int)protractorPosition.Y + 50, 20, Color.Black); - if (currentAngleDegrees != 0.0f) - { - DrawLineEx(protractorPosition, finalVector, 3.0f, gestureColor); - } - - // Draw touch and mouse pointer points - if (currentGesture != Gesture.None) - { - if (touchCount != 0) + if (currentGesture != previousGesture) { - for (i = 0; i < touchCount; i++) - { - DrawCircleV(touchPosition[i], 50.0f, Fade(gestureColor, 0.5f)); - DrawCircleV(touchPosition[i], 5.0f, gestureColor); - } - - if (touchCount == 2) - { - DrawLineEx(touchPosition[0], touchPosition[1], (((int)currentGesture == 512) ? 8.0f : 12.0f), - gestureColor); - } - } - else - { - DrawCircleV(mousePosition, 35.0f, Fade(gestureColor, 0.5f)); - DrawCircleV(mousePosition, 5.0f, gestureColor); + fillLog = 1; } } - - EndDrawing(); - //-------------------------------------------------------------------------------------- + else // 0 shows repeated events + { + fillLog = 1; + } } + if (fillLog > 0) // If one of the conditions from logMode was met, fill the gesture log + { + previousGesture = currentGesture; + gestureColor = GetGestureColor((int)currentGesture); + if (gestureLogIndex <= 0) + { + gestureLogIndex = GESTURE_LOG_SIZE; + } + gestureLogIndex--; + + // Copy the gesture respective name to the gesture log array + gestureLog[gestureLogIndex] = GetGestureName((int)currentGesture); + } + + // Handle protractor + if ((int)currentGesture > 255) + { + currentAngleDegrees = currentPitchDegrees; // Pinch In and Pinch Out + } + else if ((int)currentGesture > 15) + { + currentAngleDegrees = currentDragDegrees; // Swipe Right, Swipe Left, Swipe Up and Swipe Down + } + else if (currentGesture > 0) + { + currentAngleDegrees = 0.0f; // Tap, Doubletap, Hold and Grab + } + + var currentAngleRadians = + ((currentAngleDegrees + 90.0f) * MathF.PI / 180); // Convert the current angle to Radians + // Calculate the final vector for display + finalVector = new Vector2( + (angleLength * MathF.Sin(currentAngleRadians)) + protractorPosition.X, + (angleLength * MathF.Cos(currentAngleRadians)) + protractorPosition.Y + ); + + // Handle touch and mouse pointer points + var touchPosition = new Vector2[MAX_TOUCH_COUNT]; + + var mousePosition = Vector2.Zero; + if (currentGesture != Gesture.None) + { + if (touchCount != 0) + { + for (i = 0; i < touchCount; i++) + { + touchPosition[i] = GetTouchPosition(i); // Fill the touch positions + } + } + else + { + mousePosition = GetMousePosition(); + } + } + //-------------------------------------------------------------------------------------- + + // Draw + //-------------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + // Draw common elements + DrawText("*", (int)messagePosition.X + 5, (int)messagePosition.Y + 5, 10, Color.Black); + DrawText("Example optimized for Web/HTML5\non Smartphones with Touch Screen.", (int)messagePosition.X + 15, + (int)messagePosition.Y + 5, 10, Color.Black); + DrawText("*", (int)messagePosition.X + 5, (int)messagePosition.Y + 35, 10, Color.Black); + DrawText("While running on Desktop Web Browsers,\ninspect and turn on Touch Emulation.", + (int)messagePosition.X + 15, (int)messagePosition.Y + 35, 10, Color.Black); + + // Draw last gesture + DrawText("Last gesture", (int)lastGesturePosition.X + 33, (int)lastGesturePosition.Y - 47, 20, Color.Black); + DrawText("Swipe Tap Pinch Touch", (int)lastGesturePosition.X + 17, + (int)lastGesturePosition.Y - 18, 10, Color.Black); + DrawRectangle((int)lastGesturePosition.X + 20, (int)lastGesturePosition.Y, 20, 20, + lastGesture == Gesture.SwipeUp ? Color.Red : Color.LightGray); + DrawRectangle((int)lastGesturePosition.X, (int)lastGesturePosition.Y + 20, 20, 20, + lastGesture == Gesture.SwipeLeft ? Color.Red : Color.LightGray); + DrawRectangle((int)lastGesturePosition.X + 40, (int)lastGesturePosition.Y + 20, 20, 20, + lastGesture == Gesture.SwipeRight ? Color.Red : Color.LightGray); + DrawRectangle((int)lastGesturePosition.X + 20, (int)lastGesturePosition.Y + 40, 20, 20, + lastGesture == Gesture.SwipeDown ? Color.Red : Color.LightGray); + DrawCircle((int)lastGesturePosition.X + 80, (int)lastGesturePosition.Y + 16, 10, + lastGesture == Gesture.Tap ? Color.Blue : Color.LightGray); + DrawRing(new Vector2( + lastGesturePosition.X + 103, lastGesturePosition.Y + 16 + ), 6.0f, 11.0f, 0.0f, 360.0f, 0, lastGesture == Gesture.Drag ? Color.Lime : Color.LightGray); + DrawCircle((int)lastGesturePosition.X + 80, (int)lastGesturePosition.Y + 43, 10, + lastGesture == Gesture.DoubleTap ? Color.SkyBlue : Color.LightGray); + DrawCircle((int)lastGesturePosition.X + 103, (int)lastGesturePosition.Y + 43, 10, + lastGesture == Gesture.DoubleTap ? Color.SkyBlue : Color.LightGray); + DrawTriangle(new Vector2( + lastGesturePosition.X + 122, lastGesturePosition.Y + 16 + ), new Vector2( + lastGesturePosition.X + 137, lastGesturePosition.Y + 26 + ), new Vector2( + lastGesturePosition.X + 137, lastGesturePosition.Y + 6 + ), lastGesture == Gesture.PinchOut ? Color.Orange : Color.LightGray); + DrawTriangle(new Vector2( + lastGesturePosition.X + 147, lastGesturePosition.Y + 6 + ), new Vector2( + lastGesturePosition.X + 147, lastGesturePosition.Y + 26 + ), new Vector2( + lastGesturePosition.X + 162, lastGesturePosition.Y + 16 + ), lastGesture == Gesture.PinchOut ? Color.Orange : Color.Gray); + DrawTriangle(new Vector2( + lastGesturePosition.X + 125, lastGesturePosition.Y + 33 + ), new Vector2( + lastGesturePosition.X + 125, lastGesturePosition.Y + 53 + ), new Vector2( + lastGesturePosition.X + 140, lastGesturePosition.Y + 43 + ), lastGesture == Gesture.PinchIn ? Color.Violet : Color.LightGray); + DrawTriangle(new Vector2( + lastGesturePosition.X + 144, lastGesturePosition.Y + 43 + ), new Vector2( + lastGesturePosition.X + 159, lastGesturePosition.Y + 53 + ), new Vector2( + lastGesturePosition.X + 159, lastGesturePosition.Y + 33 + ), lastGesture == Gesture.PinchIn ? Color.Violet : Color.LightGray); + for (i = 0; i < 4; i++) + { + DrawCircle((int)lastGesturePosition.X + 180, (int)lastGesturePosition.Y + 7 + i * 15, 5, + touchCount <= i ? Color.LightGray : gestureColor); + } + + // Draw gesture log + DrawText("Log", (int)gestureLogPosition.X, (int)gestureLogPosition.Y, 20, Color.Black); + + // Loop in both directions to print the gesture log array in the inverted order (and looping around if the index started somewhere in the middle) + for (i = 0, ii = gestureLogIndex; i < GESTURE_LOG_SIZE; i++, ii = (ii + 1) % GESTURE_LOG_SIZE) + { + DrawText(gestureLog[ii], (int)gestureLogPosition.X, (int)gestureLogPosition.Y + 410 - i * 20, 20, + (i == 0 ? gestureColor : Color.LightGray)); + } + + Color logButton1Color, logButton2Color; + switch (logMode) + { + case 3: + logButton1Color = Color.Maroon; + logButton2Color = Color.Maroon; + break; + case 2: + logButton1Color = Color.Gray; + logButton2Color = Color.Maroon; + break; + case 1: + logButton1Color = Color.Maroon; + logButton2Color = Color.Gray; + break; + default: + logButton1Color = Color.Gray; + logButton2Color = Color.Gray; + break; + } + + DrawRectangleRec(logButton1, logButton1Color); + DrawText("Hide", (int)logButton1.X + 7, (int)logButton1.Y + 3, 10, Color.White); + DrawText("Repeat", (int)logButton1.X + 7, (int)logButton1.Y + 13, 10, Color.White); + DrawRectangleRec(logButton2, logButton2Color); + DrawText("Hide", (int)logButton1.X + 62, (int)logButton1.Y + 3, 10, Color.White); + DrawText("Hold", (int)logButton1.X + 62, (int)logButton1.Y + 13, 10, Color.White); + + // Draw protractor + DrawText("Angle", (int)protractorPosition.X + 55, (int)protractorPosition.Y + 76, 10, Color.Black); + + // Note: Official it's using raylibs functions for string manipulation. But in C# it will end up in an unsafe handling. + var angleString = currentAngleDegrees.ToString("F3"); + var angleStringDot = angleString.IndexOf('.'); + var angleStringTrim = angleString[..(angleStringDot + 3)]; + + DrawText(angleStringTrim, (int)protractorPosition.X + 55, (int)protractorPosition.Y + 92, 20, gestureColor); + DrawCircleV(protractorPosition, 80.0f, Color.White); + DrawLineEx(new Vector2( + protractorPosition.X - 90, protractorPosition.Y + ), new Vector2( + protractorPosition.X + 90, protractorPosition.Y + ), 3.0f, Color.LightGray); + DrawLineEx(new Vector2( + protractorPosition.X, protractorPosition.Y - 90 + ), new Vector2( + protractorPosition.X, protractorPosition.Y + 90 + ), 3.0f, Color.LightGray); + DrawLineEx(new Vector2( + protractorPosition.X - 80, protractorPosition.Y - 45 + ), new Vector2( + protractorPosition.X + 80, protractorPosition.Y + 45 + ), 3.0f, Color.Green); + DrawLineEx(new Vector2( + protractorPosition.X - 80, protractorPosition.Y + 45 + ), new Vector2( + protractorPosition.X + 80, protractorPosition.Y - 45 + ), 3.0f, Color.Green); + DrawText("0", (int)protractorPosition.X + 96, (int)protractorPosition.Y - 9, 20, Color.Black); + DrawText("30", (int)protractorPosition.X + 74, (int)protractorPosition.Y - 68, 20, Color.Black); + DrawText("90", (int)protractorPosition.X - 11, (int)protractorPosition.Y - 110, 20, Color.Black); + DrawText("150", (int)protractorPosition.X - 100, (int)protractorPosition.Y - 68, 20, Color.Black); + DrawText("180", (int)protractorPosition.X - 124, (int)protractorPosition.Y - 9, 20, Color.Black); + DrawText("210", (int)protractorPosition.X - 100, (int)protractorPosition.Y + 50, 20, Color.Black); + DrawText("270", (int)protractorPosition.X - 18, (int)protractorPosition.Y + 92, 20, Color.Black); + DrawText("330", (int)protractorPosition.X + 72, (int)protractorPosition.Y + 50, 20, Color.Black); + if (currentAngleDegrees != 0.0f) + { + DrawLineEx(protractorPosition, finalVector, 3.0f, gestureColor); + } + + // Draw touch and mouse pointer points + if (currentGesture != Gesture.None) + { + if (touchCount != 0) + { + for (i = 0; i < touchCount; i++) + { + DrawCircleV(touchPosition[i], 50.0f, Fade(gestureColor, 0.5f)); + DrawCircleV(touchPosition[i], 5.0f, gestureColor); + } + + if (touchCount == 2) + { + DrawLineEx(touchPosition[0], touchPosition[1], (((int)currentGesture == 512) ? 8.0f : 12.0f), + gestureColor); + } + } + else + { + DrawCircleV(mousePosition, 35.0f, Fade(gestureColor, 0.5f)); + DrawCircleV(mousePosition, 5.0f, gestureColor); + } + } + + EndDrawing(); + //-------------------------------------------------------------------------------------- + } + + public void Unload() + { + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [core] example - input gestures testbed"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + var game = new InputGesturesTestBed(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + game.Update(); + } + + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- CloseWindow(); // Close window and OpenGL context @@ -409,7 +457,8 @@ public class InputGesturesTestBed return 0; } - static string GetGestureName(int gesture) + // Get text string for gesture value + private static string GetGestureName(int gesture) { switch (gesture) { @@ -441,7 +490,7 @@ public class InputGesturesTestBed } // Get color for gesture value - static Color GetGestureColor(int gesture) + private static Color GetGestureColor(int gesture) { switch (gesture) { diff --git a/Examples/Core/InputKeys.cs b/Examples/Core/InputKeys.cs index 56e7823..e15369b 100644 --- a/Examples/Core/InputKeys.cs +++ b/Examples/Core/InputKeys.cs @@ -18,64 +18,87 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class InputKeys +public partial class InputKeys : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Core / Input Keys"; + + public string Title => "raylib [core] example - input keys"; + + private Vector2 ballPosition; + + public void Init() + { + ballPosition = new((float)screenWidth / 2, (float)screenHeight / 2); + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyDown(KeyboardKey.Right)) + { + ballPosition.X += 2.0f; + } + + if (IsKeyDown(KeyboardKey.Left)) + { + ballPosition.X -= 2.0f; + } + + if (IsKeyDown(KeyboardKey.Up)) + { + ballPosition.Y -= 2.0f; + } + + if (IsKeyDown(KeyboardKey.Down)) + { + ballPosition.Y += 2.0f; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("move the ball with arrow keys", 10, 10, 20, Color.DarkGray); + + DrawCircleV(ballPosition, 50, Color.Maroon); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [core] example - input keys"); - InitWindow(screenWidth, screenHeight, "raylib [core] example - keyboard input"); - - Vector2 ballPosition = new((float)screenWidth / 2, (float)screenHeight / 2); - - SetTargetFPS(60); // Set target frames-per-second + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new InputKeys(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyDown(KeyboardKey.Right)) - { - ballPosition.X += 2.0f; - } - - if (IsKeyDown(KeyboardKey.Left)) - { - ballPosition.X -= 2.0f; - } - - if (IsKeyDown(KeyboardKey.Up)) - { - ballPosition.Y -= 2.0f; - } - - if (IsKeyDown(KeyboardKey.Down)) - { - ballPosition.Y += 2.0f; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("move the ball with arrow keys", 10, 10, 20, Color.DarkGray); - - DrawCircleV(ballPosition, 50, Color.Maroon); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/InputMouse.cs b/Examples/Core/InputMouse.cs index 6207883..a7b105a 100644 --- a/Examples/Core/InputMouse.cs +++ b/Examples/Core/InputMouse.cs @@ -19,95 +19,122 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class InputMouse +public partial class InputMouse : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Core / Input Mouse"; + + public string Title => "raylib [core] example - input mouse"; + + private Vector2 ballPosition; + private Color ballColor; + + public void Init() + { + ballPosition = new(-100.0f, -100.0f); + ballColor = Color.DarkBlue; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KeyboardKey.H)) + { + if (IsCursorHidden()) + { + ShowCursor(); + } + else + { + HideCursor(); + } + } + + ballPosition = GetMousePosition(); + + if (IsMouseButtonPressed(MouseButton.Left)) + { + ballColor = Color.Maroon; + } + else if (IsMouseButtonPressed(MouseButton.Middle)) + { + ballColor = Color.Lime; + } + else if (IsMouseButtonPressed(MouseButton.Right)) + { + ballColor = Color.DarkBlue; + } + else if (IsMouseButtonPressed(MouseButton.Side)) + { + ballColor = Color.Purple; + } + else if (IsMouseButtonPressed(MouseButton.Extra)) + { + ballColor = Color.Yellow; + } + else if (IsMouseButtonPressed(MouseButton.Forward)) + { + ballColor = Color.Orange; + } + else if (IsMouseButtonPressed(MouseButton.Back)) + { + ballColor = Color.Beige; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawCircleV(ballPosition, 40, ballColor); + + DrawText("move ball with mouse and click mouse button to change color", 10, 10, 20, Color.DarkGray); + DrawText("Press 'H' to toggle cursor visibility", 10, 30, 20, Color.DarkGray); + + if (IsCursorHidden()) + { + DrawText("CURSOR HIDDEN", 20, 60, 20, Color.Red); + } + else + { + DrawText("CURSOR VISIBLE", 20, 60, 20, Color.Lime); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [core] example - input mouse"); - InitWindow(screenWidth, screenHeight, "raylib [core] example - mouse input"); - - Vector2 ballPosition = new(-100.0f, -100.0f); - Color ballColor = Color.DarkBlue; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //--------------------------------------------------------------------------------------- + var game = new InputMouse(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - - if (IsKeyPressed(KeyboardKey.H)) - { - if (IsCursorHidden()) - { - ShowCursor(); - } - else - { - HideCursor(); - } - } - - ballPosition = GetMousePosition(); - - if (IsMouseButtonPressed(MouseButton.Left)) - { - ballColor = Color.Maroon; - } - else if (IsMouseButtonPressed(MouseButton.Middle)) - { - ballColor = Color.Lime; - } - else if (IsMouseButtonPressed(MouseButton.Right)) - { - ballColor = Color.DarkBlue; - } - else if (IsMouseButtonPressed(MouseButton.Extra)) - { - ballColor = Color.Yellow; - } - else if (IsMouseButtonPressed(MouseButton.Forward)) - { - ballColor = Color.Orange; - } - else if (IsMouseButtonPressed(MouseButton.Back)) - { - ballColor = Color.Beige; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawCircleV(ballPosition, 40, ballColor); - - DrawText("move ball with mouse and click mouse button to change color", 10, 10, 20, Color.DarkGray); - DrawText("Press 'H' to toggle cursor visibility", 10, 30, 20, Color.DarkGray); - - if (IsCursorHidden()) - { - DrawText("CURSOR HIDDEN", 20, 60, 20, Color.Red); - } - else - { - DrawText("CURSOR VISIBLE", 20, 60, 20, Color.Lime); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/InputMouseWheel.cs b/Examples/Core/InputMouseWheel.cs index 57900f4..92d21fe 100644 --- a/Examples/Core/InputMouseWheel.cs +++ b/Examples/Core/InputMouseWheel.cs @@ -17,48 +17,72 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class InputMouseWheel +public partial class InputMouseWheel : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Core / Mouse Wheel"; + + public string Title => "raylib [core] example - input mouse wheel"; + + private int boxPositionY; + private int scrollSpeed; // Scrolling speed in pixels + + public void Init() + { + boxPositionY = screenHeight / 2 - 40; + scrollSpeed = 4; // Scrolling speed in pixels + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + boxPositionY -= (int)(GetMouseWheelMove() * scrollSpeed); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawRectangle(screenWidth / 2 - 40, boxPositionY, 80, 80, Color.Maroon); + + DrawText("Use mouse wheel to move the cube up and down!", 10, 10, 20, Color.Gray); + DrawText($"Box position Y: {boxPositionY:000}", 10, 40, 20, Color.LightGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - input mouse wheel"); - int boxPositionY = screenHeight / 2 - 40; - int scrollSpeed = 4; // Scrolling speed in pixels - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new InputMouseWheel(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - boxPositionY -= (int)(GetMouseWheelMove() * scrollSpeed); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawRectangle(screenWidth / 2 - 40, boxPositionY, 80, 80, Color.Maroon); - - DrawText("Use mouse wheel to move the cube up and down!", 10, 10, 20, Color.Gray); - DrawText($"Box position Y: {boxPositionY}", 10, 40, 20, Color.LightGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/InputMultitouch.cs b/Examples/Core/InputMultitouch.cs index f075bd8..ac93aea 100644 --- a/Examples/Core/InputMultitouch.cs +++ b/Examples/Core/InputMultitouch.cs @@ -20,74 +20,98 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class InputMultitouch +public partial class InputMultitouch : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + private const int MaxTouchPoints = 10; + + public string Name => "Core / Input Multitouch"; + + public string Title => "raylib [core] example - input multitouch"; + + private Vector2[] touchPositions; + + public void Init() + { + touchPositions = new Vector2[MaxTouchPoints]; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + // Get the touch point count ( how many fingers are touching the screen ) + var tCount = GetTouchPointCount(); + + // Clamp touch points available ( set the maximum touch points allowed ) + if (tCount > MaxTouchPoints) + { + tCount = MaxTouchPoints; + } + + // Get touch points positions + for (var i = 0; i < tCount; i++) + { + touchPositions[i] = GetTouchPosition(i); + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + for (var i = 0; i < tCount; i++) + { + // Make sure point is not (0, 0) as this means there is no touch for it + if ((touchPositions[i].X > 0) && (touchPositions[i].Y > 0)) + { + // Draw circle and touch index number + DrawCircleV(touchPositions[i], 34, Color.Orange); + DrawText(i.ToString(), + (int)touchPositions[i].X - 10, + (int)touchPositions[i].Y - 70, + 40, + Color.Black + ); + } + } + + DrawText("touch the screen at multiple locations to get multiple balls", 10, 10, 20, Color.DarkGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - input multitouch"); - const int MaxTouchPoints = 10; - Vector2[] touchPositions = new Vector2[MaxTouchPoints]; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //--------------------------------------------------------------------------------------- + var game = new InputMultitouch(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // Get the touch point count (how many fingers are touching the screen ) - int tCount = GetTouchPointCount(); - - // Clamp touch points available (set the maximum touch points allowed ) - if (tCount > MaxTouchPoints) - { - tCount = MaxTouchPoints; - } - - // Get touch points positions - for (int i = 0; i < tCount; i++) - { - touchPositions[i] = GetTouchPosition(i); - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - for (int i = 0; i < tCount; i++) - { - // Make sure point is not (0, 0) as this means there is no touch for it - if ((touchPositions[i].X > 0) && (touchPositions[i].Y > 0)) - { - // Draw circle and touch index number - DrawCircleV(touchPositions[i], 34, Color.Orange); - DrawText(i.ToString(), - (int)touchPositions[i].X - 10, - (int)touchPositions[i].Y - 70, - 40, - Color.Black - ); - } - } - - DrawText("touch the screen at multiple locations to get multiple balls", 10, 10, 20, Color.DarkGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/InputVirtualControls.cs b/Examples/Core/InputVirtualControls.cs index 626d913..1477eb5 100644 --- a/Examples/Core/InputVirtualControls.cs +++ b/Examples/Core/InputVirtualControls.cs @@ -1,27 +1,27 @@ /******************************************************************************************* - * - * raylib [core] example - input virtual controls - * - * Example complexity rating: [★★☆☆] 2/4 - * - * Example originally created with raylib 5.0, last time updated with raylib 5.0 - * - * Example contributed by GreenSnakeLinux (@GreenSnakeLinux), - * reviewed by Ramon Santamaria (@raysan5), oblerion (@oblerion) and danilwhale (@danilwhale) - * - * 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) 2024-2025 GreenSnakeLinux (@GreenSnakeLinux) and Ramon Santamaria (@raysan5) - * - ********************************************************************************************/ +* +* raylib [core] example - input virtual controls +* +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 5.0, last time updated with raylib 5.0 +* +* Example contributed by GreenSnakeLinux (@GreenSnakeLinux), +* reviewed by Ramon Santamaria (@raysan5), oblerion (@oblerion) and danilwhale (@danilwhale) +* +* 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) 2024-2025 GreenSnakeLinux (@GreenSnakeLinux) and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ +using System; using System.Numerics; +using static Raylib_cs.Raylib; namespace Examples.Core; -using static Raylib_cs.Raylib; - public enum PadButton { BUTTON_NONE = -1, @@ -32,21 +32,31 @@ public enum PadButton BUTTON_MAX } -public class InputVirtualControls +public partial class InputVirtualControls : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Core / Input Virtual Controls"; + + public string Title => "raylib [core] example - input virtual controls"; + + private Vector2 padPosition; + private float buttonRadius; + private Vector2[] buttonPositions; + private Vector2[][] arrowTris; + private Color[] buttonLabelColors; + private int pressedButton; + private Vector2 inputPosition; + private Vector2 playerPosition; + private float playerSpeed; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + padPosition = new Vector2(100, 350); + buttonRadius = 30; - InitWindow(screenWidth, screenHeight, "raylib [core] example - input virtual controls"); - - Vector2 padPosition = new Vector2(100, 350); - float buttonRadius = 30; - - Vector2[] buttonPositions = + buttonPositions = [ new Vector2( padPosition.X,padPosition.Y - buttonRadius * 1.5f @@ -62,7 +72,7 @@ public class InputVirtualControls ) // Down ]; - Vector2[][] arrowTris = [ + arrowTris = [ // Up [ new Vector2( @@ -114,107 +124,126 @@ public class InputVirtualControls ] ; - Color[] buttonLabelColors = [ + buttonLabelColors = [ Color.Yellow, // Up Color.Blue, // Left Color.Red, // Right Color.Green // Down ]; - int pressedButton = (int)PadButton.BUTTON_NONE; - Vector2 inputPosition = new Vector2(0, 0); + pressedButton = (int)PadButton.BUTTON_NONE; + inputPosition = new Vector2(0, 0); - Vector2 playerPosition = new Vector2((float)screenWidth / 2, (float)screenHeight / 2); - float playerSpeed = 75f; + playerPosition = new Vector2((float)screenWidth / 2, (float)screenHeight / 2); + playerSpeed = 75f; + } + + public void Update() + { + // Update + //-------------------------------------------------------------------------- + if ((GetTouchPointCount() > 0)) + { + inputPosition = GetTouchPosition(0); // Use touch position + } + else + { + inputPosition = GetMousePosition(); // Use mouse position + } + + // Reset pressed button to none + pressedButton = (int)PadButton.BUTTON_NONE; + + // Make sure user is pressing left mouse button if they're from desktop + if ((GetTouchPointCount() > 0) || + ((GetTouchPointCount() == 0) && IsMouseButtonDown(MouseButton.Left))) + { + // Find nearest D-Pad button to the input position + for (var i = 0; i < (int)PadButton.BUTTON_MAX; i++) + { + var distX = MathF.Abs(buttonPositions[i].X - inputPosition.X); + var distY = MathF.Abs(buttonPositions[i].Y - inputPosition.Y); + + if ((distX + distY < buttonRadius)) + { + pressedButton = i; + break; + } + } + } + + // Move player according to pressed button + switch ((PadButton)pressedButton) + { + case PadButton.BUTTON_UP: + playerPosition.Y -= playerSpeed * GetFrameTime(); + break; + case PadButton.BUTTON_LEFT: + playerPosition.X -= playerSpeed * GetFrameTime(); + break; + case PadButton.BUTTON_RIGHT: + playerPosition.X += playerSpeed * GetFrameTime(); + break; + case PadButton.BUTTON_DOWN: + playerPosition.Y += playerSpeed * GetFrameTime(); + break; + default: + break; + } + //-------------------------------------------------------------------------- + + // Draw + //-------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(Color.RayWhite); + + // Draw world + DrawCircleV(playerPosition, 50, Color.Maroon); + + // Draw GUI + for (var i = 0; i < (int)PadButton.BUTTON_MAX; i++) + { + DrawCircleV(buttonPositions[i], buttonRadius, (i == pressedButton) ? Color.DarkGray : Color.Black); + + DrawTriangle( + arrowTris[i][0], + arrowTris[i][1], + arrowTris[i][2], + buttonLabelColors[i] + ); + } + + DrawText("move the player with D-Pad buttons", 10, 10, 20, Color.DarkGray); + + EndDrawing(); + //-------------------------------------------------------------------------- + } + + public void Unload() + { + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [core] example - input virtual controls"); SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new InputVirtualControls(); + game.Init(); + // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //-------------------------------------------------------------------------- - if ((GetTouchPointCount() > 0)) - { - inputPosition = GetTouchPosition(0); // Use touch position - } - else - { - inputPosition = GetMousePosition(); // Use mouse position - } - - // Reset pressed button to none - pressedButton = (int)PadButton.BUTTON_NONE; - - // Make sure user is pressing left mouse button if they're from desktop - if ((GetTouchPointCount() > 0) || - ((GetTouchPointCount() == 0) && IsMouseButtonDown(MouseButton.Left))) - { - // Find nearest D-Pad button to the input position - for (int i = 0; i < (int)PadButton.BUTTON_MAX; i++) - { - float distX = MathF.Abs(buttonPositions[i].X - inputPosition.X); - float distY = MathF.Abs(buttonPositions[i].Y - inputPosition.Y); - - if ((distX + distY < buttonRadius)) - { - pressedButton = i; - break; - } - } - } - - // Move player according to pressed button - switch ((PadButton)pressedButton) - { - case PadButton.BUTTON_UP: - playerPosition.Y -= playerSpeed * GetFrameTime(); - break; - case PadButton.BUTTON_LEFT: - playerPosition.X -= playerSpeed * GetFrameTime(); - break; - case PadButton.BUTTON_RIGHT: - playerPosition.X += playerSpeed * GetFrameTime(); - break; - case PadButton.BUTTON_DOWN: - playerPosition.Y += playerSpeed * GetFrameTime(); - break; - default: - break; - } - - ; - //-------------------------------------------------------------------------- - - // Draw - //-------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(Color.RayWhite); - - // Draw world - DrawCircleV(playerPosition, 50, Color.Maroon); - - // Draw GUI - for (int i = 0; i < (int)PadButton.BUTTON_MAX; i++) - { - DrawCircleV(buttonPositions[i], buttonRadius, (i == pressedButton) ? Color.DarkGray : Color.Black); - - DrawTriangle( - arrowTris[i][0], - arrowTris[i][1], - arrowTris[i][2], - buttonLabelColors[i] - ); - } - - DrawText("move the player with D-Pad buttons", 10, 10, 20, Color.DarkGray); - - EndDrawing(); - //-------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- CloseWindow(); // Close window and OpenGL context diff --git a/Examples/Core/LoadingThread.cs b/Examples/Core/LoadingThread.cs index 8d81b81..affdbb0 100644 --- a/Examples/Core/LoadingThread.cs +++ b/Examples/Core/LoadingThread.cs @@ -1,153 +1,184 @@ /******************************************************************************************* * -* raylib example - loading thread +* raylib [core] example - loading thread * -* This example has been created using raylib 2.5 (www.raylib.com) +* NOTE: raylib is NOT thread-safe: the loading thread only updates plain data +* (progress counter and loaded flag); all raylib calls happen on the main thread. +* +* Example originally created with raylib 2.5 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ +using System.Diagnostics; using System.Threading; using static Raylib_cs.Raylib; -using static Raylib_cs.Color; -using static Raylib_cs.KeyboardKey; namespace Examples.Core; -enum State +public partial class LoadingThread : IExample { - STATE_WAITING, - STATE_LOADING, - STATE_FINISHED -} + const int screenWidth = 800; + const int screenHeight = 450; -public class LoadingThread -{ - // C# bool is atomic. Used for synchronization - // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/variables#atomicity-of-variable-references - // Data Loaded completion indicator - static bool dataLoaded = false; + public string Name => "Core / Loading Thread"; - // Data progress accumulator - static int dataProgress = 0; + public string Title => "raylib [core] example - loading thread"; + + enum State + { + Waiting, + Loading, + Finished + } + + // Loading data thread; a Thread can only be started once, so a fresh one + // is created for every load + Thread loadingThread; + + // Data loaded completion indicator; volatile so the main thread sees the + // background thread's writes + volatile bool dataLoaded; + + // Data progress accumulator (0..500, the progress bar width in pixels) + volatile int dataProgress; + + State state; + int framesCounter; + + public void Init() + { + loadingThread = null; + dataLoaded = false; + dataProgress = 0; + + state = State.Waiting; + framesCounter = 0; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + switch (state) + { + case State.Waiting: + if (IsKeyPressed(KeyboardKey.Enter)) + { + loadingThread = new Thread(LoadDataThread) { IsBackground = true }; + loadingThread.Start(); + TraceLog(TraceLogLevel.Info, "Loading thread initialized successfully"); + + state = State.Loading; + } + break; + + case State.Loading: + framesCounter++; + if (dataLoaded) + { + framesCounter = 0; + loadingThread.Join(); + TraceLog(TraceLogLevel.Info, "Loading thread terminated"); + + state = State.Finished; + } + break; + + case State.Finished: + if (IsKeyPressed(KeyboardKey.Enter)) + { + // Reset everything to launch again + dataLoaded = false; + dataProgress = 0; + + state = State.Waiting; + } + break; + + default: + break; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + switch (state) + { + case State.Waiting: + DrawText("PRESS ENTER to START LOADING DATA", 150, 170, 20, Color.DarkGray); + break; + + case State.Loading: + DrawRectangle(150, 200, dataProgress, 60, Color.SkyBlue); + if ((framesCounter / 15) % 2 == 0) + { + DrawText("LOADING DATA...", 240, 210, 40, Color.DarkBlue); + } + break; + + case State.Finished: + DrawRectangle(150, 200, 500, 60, Color.Lime); + DrawText("DATA LOADED!", 250, 210, 40, Color.Green); + break; + + default: + break; + } + + DrawRectangleLines(150, 200, 500, 60, Color.DarkGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - loading thread"); - // Loading data thread id - Thread thread = new(new ThreadStart(LoadDataThread)); - - State state = State.STATE_WAITING; - int framesCounter = 0; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new LoadingThread(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - switch (state) - { - case State.STATE_WAITING: - { - if (IsKeyPressed(KEY_ENTER)) - { - thread.Start(); - //int error = pthread_create(ref, NULL, ref, NULL); - //if (error != 0) TraceLog(TraceLogLevel.LOG_ERROR, "Error creating loading thread"); - //else TraceLog(TraceLogLevel.LOG_INFO, "Loading thread initialized successfully"); - - state = State.STATE_LOADING; - } - } - break; - case State.STATE_LOADING: - { - framesCounter++; - if (dataLoaded) - { - framesCounter = 0; - state = State.STATE_FINISHED; - } - } - break; - case State.STATE_FINISHED: - { - if (IsKeyPressed(KEY_ENTER)) - { - // Reset everything to launch again - // atomic_store(ref, false); - dataProgress = 0; - state = State.STATE_WAITING; - } - } - break; - default: break; - } - - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(RAYWHITE); - - switch (state) - { - case State.STATE_WAITING: - DrawText("PRESS ENTER to START LOADING DATA", 150, 170, 20, DARKGRAY); - break; - case State.STATE_LOADING: - { - DrawRectangle(150, 200, dataProgress, 60, SKYBLUE); - if ((framesCounter / 15) % 2 == 0) DrawText("LOADING DATA...", 240, 210, 40, DARKBLUE); - } - break; - case State.STATE_FINISHED: - { - DrawRectangle(150, 200, 500, 60, LIME); - DrawText("DATA LOADED!", 250, 210, 40, GREEN); - } - break; - default: break; - } - - DrawRectangleLines(150, 200, 500, 60, DARKGRAY); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); // Close window and OpenGL context + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } // Loading data thread function definition - static void LoadDataThread() + void LoadDataThread() { - int timeCounter = 0; // Time counted in ms - // clock_t prevTime = clock(); // Previous time + int timeCounter = 0; // Time counted in ms + var stopwatch = Stopwatch.StartNew(); // We simulate data loading with a time counter for 5 seconds while (timeCounter < 5000) { - //clock_t currentTime = clock() - prevTime; - //timeCounter = currentTime*1000/CLOCKS_PER_SEC; - timeCounter += 1; + timeCounter = (int)stopwatch.ElapsedMilliseconds; // We accumulate time over a global variable to be used in // main thread as a progress bar @@ -158,4 +189,3 @@ public class LoadingThread dataLoaded = true; } } - diff --git a/Examples/Core/Picking3D.cs b/Examples/Core/Picking3D.cs index 41c2f86..9c7ecc3 100644 --- a/Examples/Core/Picking3D.cs +++ b/Examples/Core/Picking3D.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [core] example - Picking in 3d mode +* raylib [core] example - 3d picking * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.3, last time updated with raylib 4.0 +* +* 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,106 +18,150 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class Picking3d +public partial class Picking3d : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Core / Picking 3D"; + + public string Title => "raylib [core] example - 3d picking"; + + public bool CursorDisabled => true; + + private Camera3D camera; + private Vector3 cubePosition; + private Vector3 cubeSize; + private Ray ray; // Picking line ray + private RayCollision collision; // Ray collision hit info + + public void Init() + { + // Define the camera to look into our 3d world + camera = new Camera3D(); + camera.Position = new Vector3(10.0f, 10.0f, 10.0f); // Camera position + camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type + + cubePosition = new(0.0f, 1.0f, 0.0f); + cubeSize = new(2.0f, 2.0f, 2.0f); + + ray = new(); // Picking line ray + collision = new(); // Ray collision hit info + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsCursorHidden()) + { + UpdateCamera(ref camera, CameraMode.FirstPerson); + } + + // Toggle camera controls + if (IsMouseButtonPressed(MouseButton.Right)) + { + if (IsCursorHidden()) + { + EnableCursor(); + } + else + { + DisableCursor(); + } + } + + if (IsMouseButtonPressed(MouseButton.Left)) + { + if (!collision.Hit) + { + ray = GetScreenToWorldRay(GetMousePosition(), camera); + + // Check collision between ray and box + BoundingBox box = new( + cubePosition - cubeSize / 2, + cubePosition + cubeSize / 2 + ); + collision = GetRayCollisionBox(ray, box); + } + else + { + collision.Hit = false; + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + if (collision.Hit) + { + DrawCube(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.Red); + DrawCubeWires(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.Maroon); + + DrawCubeWires(cubePosition, cubeSize.X + 0.2f, cubeSize.Y + 0.2f, cubeSize.Z + 0.2f, Color.Green); + } + else + { + DrawCube(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.Gray); + DrawCubeWires(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.DarkGray); + } + + DrawRay(ray, Color.Maroon); + DrawGrid(10, 1.0f); + + EndMode3D(); + + DrawText("Try clicking on the box with your mouse!", 240, 10, 20, Color.DarkGray); + + if (collision.Hit) + { + var posX = (screenWidth - MeasureText("BOX SELECTED", 30)) / 2; + DrawText("BOX SELECTED", posX, (int)(screenHeight * 0.1f), 30, Color.Green); + } + + DrawText("Right click mouse to toggle camera controls", 10, 430, 10, Color.Gray); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d picking"); - // Define the camera to look into our 3d world - Camera3D camera; - camera.Position = new Vector3(10.0f, 10.0f, 10.0f); - camera.Target = new Vector3(0.0f, 0.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; - - Vector3 cubePosition = new(0.0f, 1.0f, 0.0f); - Vector3 cubeSize = new(2.0f, 2.0f, 2.0f); - - // Picking line ray - Ray ray = new(new Vector3(0.0f, 0.0f, 0.0f), Vector3.Zero); - RayCollision collision = new(); - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new Picking3d(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Free); - - if (IsMouseButtonPressed(MouseButton.Left)) - { - if (!collision.Hit) - { - ray = GetScreenToWorldRay(GetMousePosition(), camera); - - // Check collision between ray and box - BoundingBox box = new( - cubePosition - cubeSize / 2, - cubePosition + cubeSize / 2 - ); - collision = GetRayCollisionBox(ray, box); - } - else - { - collision.Hit = false; - } - - ray = GetScreenToWorldRay(GetMousePosition(), camera); - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - if (collision.Hit) - { - DrawCube(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.Red); - DrawCubeWires(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.Maroon); - - DrawCubeWires(cubePosition, cubeSize.X + 0.2f, cubeSize.Y + 0.2f, cubeSize.Z + 0.2f, Color.Green); - } - else - { - DrawCube(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.Gray); - DrawCubeWires(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.DarkGray); - } - - DrawRay(ray, Color.Maroon); - DrawGrid(10, 1.0f); - - EndMode3D(); - - DrawText("Try selecting the box with mouse!", 240, 10, 20, Color.DarkGray); - - if (collision.Hit) - { - int posX = (screenWidth - MeasureText("BOX SELECTED", 30)) / 2; - DrawText("BOX SELECTED", posX, (int)(screenHeight * 0.1f), 30, Color.Green); - } - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/RandomValues.cs b/Examples/Core/RandomValues.cs index 234a8b5..86152d2 100644 --- a/Examples/Core/RandomValues.cs +++ b/Examples/Core/RandomValues.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [core] example - Generate random values +* raylib [core] example - random values * -* This example has been created using raylib 1.1 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 * -* Copyright (c) 2014 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.1, last time updated with raylib 1.1 +* +* 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -13,57 +17,81 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class RandomValues +public partial class RandomValues : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Core / Random Values"; + + public string Title => "raylib [core] example - random values"; + + private int randValue; // Get a random integer number between -8 and 5 (both included) + private int framesCounter; // Variable used to count frames + + public void Init() + { + // SetRandomSeed(0xaabbccff); // Set a custom random seed if desired, by default: "time(NULL)" + + randValue = GetRandomValue(-8, 5); // Get a random integer number between -8 and 5 (both included) + + framesCounter = 0; // Variable used to count frames + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + framesCounter++; + + // Every two seconds (120 frames) a new random value is generated + if (((framesCounter / 120) % 2) == 1) + { + randValue = GetRandomValue(-8, 5); + framesCounter = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("Every 2 seconds a new random value is generated:", 130, 100, 20, Color.Maroon); + + DrawText($"{randValue}", 360, 180, 80, Color.LightGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [core] example - random values"); - InitWindow(screenWidth, screenHeight, "raylib [core] example - generate random values"); - - // Variable used to count frames - int framesCounter = 0; - - // Get a random integer number between -8 and 5 (both included) - int randValue = GetRandomValue(-8, 5); - - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new RandomValues(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - framesCounter++; - - // Every two seconds (120 frames) a new random value is generated - if (((framesCounter / 120) % 2) == 1) - { - randValue = GetRandomValue(-8, 5); - framesCounter = 0; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("Every 2 seconds a new random value is generated:", 130, 100, 20, Color.Maroon); - - DrawText($"{randValue}", 360, 180, 80, Color.LightGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/ScissorTest.cs b/Examples/Core/ScissorTest.cs index 6a5074c..4902e69 100644 --- a/Examples/Core/ScissorTest.cs +++ b/Examples/Core/ScissorTest.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [core] example - Scissor test +* raylib [core] example - scissor test * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 2.5, last time updated with raylib 3.0 * * Example contributed by Chris Dill (@MysteriousSpace) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2019 Chris Dill (@MysteriousSpace) +* 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) 2019-2025 Chris Dill (@MysteriousSpace) * ********************************************************************************************/ @@ -15,68 +19,92 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class ScissorTest +public partial class ScissorTest : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Core / Scissor Test"; + + public string Title => "raylib [core] example - scissor test"; + + private Rectangle scissorArea; + private bool scissorMode; + + public void Init() + { + scissorArea = new(0, 0, 300, 300); + scissorMode = true; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KeyboardKey.S)) + { + scissorMode = !scissorMode; + } + + // Centre the scissor area around the mouse position + scissorArea.X = GetMouseX() - scissorArea.Width / 2; + scissorArea.Y = GetMouseY() - scissorArea.Height / 2; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + if (scissorMode) + { + BeginScissorMode((int)scissorArea.X, (int)scissorArea.Y, (int)scissorArea.Width, (int)scissorArea.Height); + } + + // Draw full screen rectangle and some text + // NOTE: Only part defined by scissor area will be rendered + DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Color.Red); + DrawText("Move the mouse around to reveal this text!", 190, 200, 20, Color.LightGray); + + if (scissorMode) + { + EndScissorMode(); + } + + DrawRectangleLinesEx(scissorArea, 1, Color.Black); + DrawText("Press S to toggle scissor test", 10, 10, 20, Color.Black); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - scissor test"); - Rectangle scissorArea = new(0, 0, 300, 300); - bool scissorMode = true; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new ScissorTest(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyPressed(KeyboardKey.S)) - { - scissorMode = !scissorMode; - } - - // Centre the scissor area around the mouse position - scissorArea.X = GetMouseX() - scissorArea.Width / 2; - scissorArea.Y = GetMouseY() - scissorArea.Height / 2; - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - if (scissorMode) - { - BeginScissorMode((int)scissorArea.X, (int)scissorArea.Y, (int)scissorArea.Width, (int)scissorArea.Height); - } - - // Draw full screen rectangle and some text - // NOTE: Only part defined by scissor area will be rendered - DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Color.Red); - DrawText("Move the mouse around to reveal this text!", 190, 200, 20, Color.LightGray); - - if (scissorMode) - { - EndScissorMode(); - } - - DrawRectangleLinesEx(scissorArea, 1, Color.Black); - DrawText("Press S to toggle scissor test", 10, 10, 20, Color.Black); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/SmoothPixelperfect.cs b/Examples/Core/SmoothPixelperfect.cs index 80e5dae..d161d46 100644 --- a/Examples/Core/SmoothPixelperfect.cs +++ b/Examples/Core/SmoothPixelperfect.cs @@ -1,14 +1,18 @@ /******************************************************************************************* * -* raylib [core] example - smooth pixel-perfect camera +* raylib [core] example - smooth pixelperfect * -* This example has been created using raylib 3.7 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 3.7, last time updated with raylib 4.0 * * Example contributed by Giancamillo Alessandroni (@NotManyIdeasDev) and * reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2021 Giancamillo Alessandroni (@NotManyIdeasDev) and 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) 2021-2025 Giancamillo Alessandroni (@NotManyIdeasDev) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -18,117 +22,193 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public static class SmoothPixelPerfect +public partial class SmoothPixelPerfect : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + private const int virtualScreenWidth = 160; + private const int virtualScreenHeight = 90; + + private const float virtualRatio = (float)screenWidth / (float)virtualScreenWidth; + + public string Name => "Core / Smooth Pixelperfect"; + + public string Title => "raylib [core] example - smooth pixelperfect"; + + private Camera2D worldSpaceCamera; // Game world camera + private Camera2D screenSpaceCamera; // Smoothing camera + private RenderTexture2D target; + + private Rectangle rec01; + private Rectangle rec02; + private Rectangle rec03; + + private Rectangle sourceRec; + private Rectangle destRec; + + private Vector2 origin; + private float rotation; + private float cameraX; + private float cameraY; + private bool smoothOn; + private bool overscan; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - const int virtualScreenWidth = 160; - const int virtualScreenHeight = 90; - - const float virtualRatio = (float)screenWidth / (float)virtualScreenWidth; - - InitWindow(screenWidth, screenHeight, "raylib [core] example - smooth pixel-perfect camera"); - - // Game world camera - Camera2D worldSpaceCamera = new(); + worldSpaceCamera = new(); // Game world camera worldSpaceCamera.Zoom = 1.0f; - // Smoothing camera - Camera2D screenSpaceCamera = new(); + screenSpaceCamera = new(); // Smoothing camera screenSpaceCamera.Zoom = 1.0f; - // This is where we'll draw all our objects. - RenderTexture2D target = LoadRenderTexture(virtualScreenWidth, virtualScreenHeight); + // Load render texture to draw all our objects + target = LoadRenderTexture(virtualScreenWidth, virtualScreenHeight); - Rectangle rec01 = new(70.0f, 35.0f, 20.0f, 20.0f); - Rectangle rec02 = new(90.0f, 55.0f, 30.0f, 10.0f); - Rectangle rec03 = new(80.0f, 65.0f, 15.0f, 25.0f); + rec01 = new(70.0f, 35.0f, 20.0f, 20.0f); + rec02 = new(90.0f, 55.0f, 30.0f, 10.0f); + rec03 = new(80.0f, 65.0f, 15.0f, 25.0f); // The target's height is flipped (in the source Rectangle), due to OpenGL reasons - Rectangle sourceRec = new( + sourceRec = new( 0.0f, 0.0f, (float)target.Texture.Width, -(float)target.Texture.Height ); - Rectangle destRec = new( - -virtualRatio, - -virtualRatio, - screenWidth + (virtualRatio * 2), - screenHeight + (virtualRatio * 2) + destRec = new( + (screenWidth - screenWidth / 1.25f) / 2.0f, + (screenHeight - screenHeight / 1.25f) / 2.0f, + screenWidth / 1.25f, + screenHeight / 1.25f ); - Vector2 origin = new(0.0f, 0.0f); + origin = new(0.0f, 0.0f); - float rotation = 0.0f; + rotation = 0.0f; - float cameraX = 0.0f; - float cameraY = 0.0f; + cameraX = 0.0f; + cameraY = 0.0f; + + smoothOn = true; + overscan = false; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + rotation += 60.0f * GetFrameTime(); // Rotate the rectangles, 60 degrees per second + + // Make the camera move to demonstrate the effect + cameraX = (MathF.Sin((float)GetTime()) * 50.0f) - 10.0f; + cameraY = MathF.Cos((float)GetTime()) * 30.0f; + + // Set the camera's target to the values computed above + screenSpaceCamera.Target = new Vector2(cameraX, cameraY); + + // Round worldSpace coordinates, keep decimals into screenSpace coordinates + worldSpaceCamera.Target.X = MathF.Truncate(screenSpaceCamera.Target.X); + screenSpaceCamera.Target.X -= worldSpaceCamera.Target.X; + screenSpaceCamera.Target.X *= virtualRatio; + + worldSpaceCamera.Target.Y = MathF.Truncate(screenSpaceCamera.Target.Y); + screenSpaceCamera.Target.Y -= worldSpaceCamera.Target.Y; + screenSpaceCamera.Target.Y *= virtualRatio; + + if (IsKeyPressed(KeyboardKey.S)) + { + smoothOn = !smoothOn; + } + + if (IsKeyPressed(KeyboardKey.O)) + { + overscan = !overscan; + } + + if (overscan) + { + destRec = new Rectangle( + -virtualRatio, + -virtualRatio, + screenWidth + (virtualRatio * 2), + screenHeight + (virtualRatio * 2) + ); + } + else + { + destRec = new Rectangle( + (screenWidth - screenWidth / 1.25f) / 2.0f, + (screenHeight - screenHeight / 1.25f) / 2.0f, + screenWidth / 1.25f, + screenHeight / 1.25f + ); + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginTextureMode(target); + ClearBackground(Color.RayWhite); + + BeginMode2D(worldSpaceCamera); + DrawRectanglePro(rec01, origin, rotation, Color.Black); + DrawRectanglePro(rec02, origin, -rotation, Color.Red); + DrawRectanglePro(rec03, origin, rotation + 45.0f, Color.Blue); + EndMode2D(); + EndTextureMode(); + + BeginDrawing(); + ClearBackground(Color.LightGray); + + if (smoothOn) + { + BeginMode2D(screenSpaceCamera); + DrawTexturePro(target.Texture, sourceRec, destRec, origin, 0.0f, Color.White); + EndMode2D(); + } + else + { + DrawTexturePro(target.Texture, sourceRec, destRec, origin, 0.0f, Color.White); + } + + DrawText($"Screen resolution: {screenWidth}x{screenHeight}", 10, 10, 20, Color.DarkBlue); + DrawText($"World resolution: {virtualScreenWidth}x{virtualScreenHeight}", 10, 40, 20, Color.DarkGreen); + DrawText($"Smooth: {(smoothOn ? "ON" : "OFF")}", 10, screenHeight - 60, 20, Color.Red); + DrawText($"Overscan: {(overscan ? "ON" : "OFF")}", 10, screenHeight - 30, 20, Color.Red); + DrawFPS(GetScreenWidth() - 95, 10); + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadRenderTexture(target); // Unload render texture + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [core] example - smooth pixelperfect"); SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new SmoothPixelPerfect(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - rotation += 60.0f * GetFrameTime(); // Rotate the rectangles, 60 degrees per second - - // Make the camera move to demonstrate the effect - cameraX = (MathF.Sin((float)GetTime()) * 50.0f) - 10.0f; - cameraY = MathF.Cos((float)GetTime()) * 30.0f; - - // Set the camera's target to the values computed above - screenSpaceCamera.Target = new Vector2(cameraX, cameraY); - - // Round worldSpace coordinates, keep decimals into screenSpace coordinates - worldSpaceCamera.Target.X = (int)screenSpaceCamera.Target.X; - screenSpaceCamera.Target.X -= worldSpaceCamera.Target.X; - screenSpaceCamera.Target.X *= virtualRatio; - - worldSpaceCamera.Target.Y = (int)screenSpaceCamera.Target.Y; - screenSpaceCamera.Target.Y -= worldSpaceCamera.Target.Y; - screenSpaceCamera.Target.Y *= virtualRatio; - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginTextureMode(target); - ClearBackground(Color.RayWhite); - - BeginMode2D(worldSpaceCamera); - DrawRectanglePro(rec01, origin, rotation, Color.Black); - DrawRectanglePro(rec02, origin, -rotation, Color.Red); - DrawRectanglePro(rec03, origin, rotation + 45.0f, Color.Blue); - EndMode2D(); - - EndTextureMode(); - - BeginDrawing(); - ClearBackground(Color.Red); - - BeginMode2D(screenSpaceCamera); - DrawTexturePro(target.Texture, sourceRec, destRec, origin, 0.0f, Color.White); - EndMode2D(); - - DrawText($"Screen resolution: {screenWidth}x{screenHeight}", 10, 10, 20, Color.DarkBlue); - DrawText($"World resolution: {virtualScreenWidth}x{virtualScreenHeight}", 10, 40, 20, Color.DarkGreen); - DrawFPS(GetScreenWidth() - 95, 10); - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadRenderTexture(target); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/SplitScreen.cs b/Examples/Core/SplitScreen.cs index c28c7b6..828eac1 100644 --- a/Examples/Core/SplitScreen.cs +++ b/Examples/Core/SplitScreen.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [core] example - split screen +* raylib [core] example - 3d camera split screen * -* This example has been created using raylib 3.7 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 3.7, last time updated with raylib 4.0 * * Example contributed by Jeffery Myers (@JeffM2501) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2021 Jeffery Myers (@JeffM2501) +* 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) 2021-2025 Jeffery Myers (@JeffM2501) * ********************************************************************************************/ @@ -16,25 +20,34 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public unsafe class SplitScreen +public partial class SplitScreen : IExample { - static Texture2D TextureGrid; - static Camera3D CameraPlayer1; - static Camera3D CameraPlayer2; + private const int screenWidth = 800; + private const int screenHeight = 450; + + private Camera3D CameraPlayer1; + private Camera3D CameraPlayer2; + + private RenderTexture2D screenPlayer1; + private RenderTexture2D screenPlayer2; + private Rectangle splitScreenRect; + + public string Name => "Core / Split Screen"; + + public string Title => "raylib [core] example - 3d camera split screen"; // Scene drawing - static void DrawScene() + private void DrawScene() { - int count = 5; + var count = 5; float spacing = 4; - // Grid of cube trees on a plane to make a "world" - // Simple world plane - DrawPlane(new Vector3(0, 0, 0), new Vector2(50, 50), Color.Beige); + // Draw scene: grid of cube trees on a plane to make a "world" + DrawPlane(new Vector3(0, 0, 0), new Vector2(50, 50), Color.Beige); // Simple world plane - for (float x = -count * spacing; x <= count * spacing; x += spacing) + for (var x = -count * spacing; x <= count * spacing; x += spacing) { - for (float z = -count * spacing; z <= count * spacing; z += spacing) + for (var z = -count * spacing; z <= count * spacing; z += spacing) { DrawCube(new Vector3(x, 1.5f, z), 1, 1, 1, Color.Lime); DrawCube(new Vector3(x, 0.5f, z), 0.25f, 1, 0.25f, Color.Brown); @@ -46,22 +59,8 @@ public unsafe class SplitScreen DrawCube(CameraPlayer2.Position, 1, 1, 1, Color.Blue); } - public static int Main() + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [core] example - split screen"); - - // Generate a simple texture to use for trees - Image img = GenImageChecked(256, 256, 32, 32, Color.DarkGray, Color.White); - TextureGrid = LoadTextureFromImage(img); - UnloadImage(img); - SetTextureFilter(TextureGrid, TextureFilter.Anisotropic16X); - SetTextureWrap(TextureGrid, TextureWrap.Clamp); - // Setup player 1 camera and screen CameraPlayer1.FovY = 45.0f; CameraPlayer1.Up.Y = 1.0f; @@ -69,7 +68,7 @@ public unsafe class SplitScreen CameraPlayer1.Position.Z = -3.0f; CameraPlayer1.Position.Y = 1.0f; - RenderTexture2D screenPlayer1 = LoadRenderTexture(screenWidth / 2, screenHeight); + screenPlayer1 = LoadRenderTexture(screenWidth / 2, screenHeight); // Setup player two camera and screen CameraPlayer2.FovY = 45.0f; @@ -78,97 +77,119 @@ public unsafe class SplitScreen CameraPlayer2.Position.X = -3.0f; CameraPlayer2.Position.Y = 3.0f; - RenderTexture2D screenPlayer2 = LoadRenderTexture(screenWidth / 2, screenHeight); + screenPlayer2 = LoadRenderTexture(screenWidth / 2, screenHeight); // Build a flipped rectangle the size of the split view to use for drawing later - Rectangle splitScreenRect = new( + splitScreenRect = new( 0.0f, 0.0f, (float)screenPlayer1.Texture.Width, (float)-screenPlayer1.Texture.Height ); + } - SetTargetFPS(60); + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + // If anyone moves this frame, how far will they move based on the time since the last frame + // this moves things at 10 world units per second, regardless of the actual FPS + var offsetThisFrame = 10.0f * GetFrameTime(); + + // Move Player1 forward and backwards (no turning) + if (IsKeyDown(KeyboardKey.W)) + { + CameraPlayer1.Position.Z += offsetThisFrame; + CameraPlayer1.Target.Z += offsetThisFrame; + } + else if (IsKeyDown(KeyboardKey.S)) + { + CameraPlayer1.Position.Z -= offsetThisFrame; + CameraPlayer1.Target.Z -= offsetThisFrame; + } + + // Move Player2 forward and backwards (no turning) + if (IsKeyDown(KeyboardKey.Up)) + { + CameraPlayer2.Position.X += offsetThisFrame; + CameraPlayer2.Target.X += offsetThisFrame; + } + else if (IsKeyDown(KeyboardKey.Down)) + { + CameraPlayer2.Position.X -= offsetThisFrame; + CameraPlayer2.Target.X -= offsetThisFrame; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + // Draw Player1 view to the render texture + BeginTextureMode(screenPlayer1); + ClearBackground(Color.SkyBlue); + + BeginMode3D(CameraPlayer1); + DrawScene(); + EndMode3D(); + + DrawRectangle(0, 0, GetScreenWidth() / 2, 40, Fade(Color.RayWhite, 0.8f)); + DrawText("PLAYER1: W/S to move", 10, 10, 20, Color.Maroon); + EndTextureMode(); + + // Draw Player2 view to the render texture + BeginTextureMode(screenPlayer2); + ClearBackground(Color.SkyBlue); + + BeginMode3D(CameraPlayer2); + DrawScene(); + EndMode3D(); + + DrawRectangle(0, 0, GetScreenWidth() / 2, 40, Fade(Color.RayWhite, 0.8f)); + DrawText("PLAYER2: UP/DOWN to move", 10, 10, 20, Color.DarkBlue); + EndTextureMode(); + + // Draw both views render textures to the screen side by side + BeginDrawing(); + ClearBackground(Color.Black); + + DrawTextureRec(screenPlayer1.Texture, splitScreenRect, new Vector2(0, 0), Color.White); + DrawTextureRec(screenPlayer2.Texture, splitScreenRect, new Vector2(screenWidth / 2.0f, 0), Color.White); + + DrawRectangle(GetScreenWidth() / 2 - 2, 0, 4, GetScreenHeight(), Color.LightGray); + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadRenderTexture(screenPlayer1); // Unload render texture + UnloadRenderTexture(screenPlayer2); // Unload render texture + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera split screen"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new SplitScreen(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // If anyone moves this frame, how far will they move based on the time since the last frame - // this moves thigns at 10 world units per second, regardless of the actual FPS - float offsetThisFrame = 10.0f * GetFrameTime(); - - // Move Player1 forward and backwards (no turning) - if (IsKeyDown(KeyboardKey.W)) - { - CameraPlayer1.Position.Z += offsetThisFrame; - CameraPlayer1.Target.Z += offsetThisFrame; - } - else if (IsKeyDown(KeyboardKey.S)) - { - CameraPlayer1.Position.Z -= offsetThisFrame; - CameraPlayer1.Target.Z -= offsetThisFrame; - } - - // Move Player2 forward and backwards (no turning) - if (IsKeyDown(KeyboardKey.Up)) - { - CameraPlayer2.Position.X += offsetThisFrame; - CameraPlayer2.Target.X += offsetThisFrame; - } - else if (IsKeyDown(KeyboardKey.Down)) - { - CameraPlayer2.Position.X -= offsetThisFrame; - CameraPlayer2.Target.X -= offsetThisFrame; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - // Draw Player1 view to the render texture - BeginTextureMode(screenPlayer1); - ClearBackground(Color.SkyBlue); - - BeginMode3D(CameraPlayer1); - DrawScene(); - EndMode3D(); - - DrawText("PLAYER 1 W/S to move", 10, 10, 20, Color.Red); - EndTextureMode(); - - // Draw Player2 view to the render texture - BeginTextureMode(screenPlayer2); - ClearBackground(Color.SkyBlue); - - BeginMode3D(CameraPlayer2); - DrawScene(); - EndMode3D(); - - DrawText("PLAYER 2 UP/DOWN to move", 10, 10, 20, Color.Blue); - EndTextureMode(); - - // Draw both views render textures to the screen side by side - BeginDrawing(); - ClearBackground(Color.Black); - - DrawTextureRec(screenPlayer1.Texture, splitScreenRect, new Vector2(0, 0), Color.White); - DrawTextureRec(screenPlayer2.Texture, splitScreenRect, new Vector2(screenWidth / 2.0f, 0), Color.White); - - EndDrawing(); + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadRenderTexture(screenPlayer1); - UnloadRenderTexture(screenPlayer2); - UnloadTexture(TextureGrid); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } } - diff --git a/Examples/Core/StorageValues.cs b/Examples/Core/StorageValues.cs index 5aee738..d64c358 100644 --- a/Examples/Core/StorageValues.cs +++ b/Examples/Core/StorageValues.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [core] example - Storage save/load values +* raylib [core] example - storage values * -* This example has been created using raylib 1.4 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.4, last time updated with raylib 4.2 +* +* 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -13,79 +17,104 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class StorageValues +public partial class StorageValues : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + private const string storageDataFile = "storage.data"; + // NOTE: Storage positions must start with 0, directly related to file memory layout - enum StorageData + private enum StorageData { Score, HiScore } + public string Name => "Core / Storage Values"; + + public string Title => "raylib [core] example - storage values"; + + private int score = 0; + private int hiscore = 0; + private int framesCounter = 0; + + public void Init() + { + score = 0; + hiscore = 0; + framesCounter = 0; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KeyboardKey.R)) + { + score = GetRandomValue(1000, 2000); + hiscore = GetRandomValue(2000, 4000); + } + + if (IsKeyPressed(KeyboardKey.Enter)) + { + SaveStorageValue(storageDataFile, (int)StorageData.Score, score); + SaveStorageValue(storageDataFile, (int)StorageData.HiScore, hiscore); + } + else if (IsKeyPressed(KeyboardKey.Space)) + { + // NOTE: If requested position could not be found, value 0 is returned + score = LoadStorageValue(storageDataFile, (int)StorageData.Score); + hiscore = LoadStorageValue(storageDataFile, (int)StorageData.HiScore); + } + + framesCounter++; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText($"SCORE: {score}", 280, 130, 40, Color.Maroon); + DrawText($"HI-SCORE: {hiscore}", 210, 200, 50, Color.Black); + + DrawText($"frames: {framesCounter}", 10, 10, 20, Color.Lime); + + DrawText("Press R to generate random numbers", 220, 40, 20, Color.LightGray); + DrawText("Press ENTER to SAVE values", 250, 310, 20, Color.LightGray); + DrawText("Press SPACE to LOAD values", 252, 350, 20, Color.LightGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - const string storageDataFile = "storage.data"; + InitWindow(screenWidth, screenHeight, "raylib [core] example - storage values"); - InitWindow(screenWidth, screenHeight, "raylib [core] example - storage save/load values"); - - int score = 0; - int hiscore = 0; - int framesCounter = 0; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new StorageValues(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyPressed(KeyboardKey.R)) - { - score = GetRandomValue(1000, 2000); - hiscore = GetRandomValue(2000, 4000); - } - - if (IsKeyPressed(KeyboardKey.Enter)) - { - SaveStorageValue(storageDataFile, (int)StorageData.Score, score); - SaveStorageValue(storageDataFile, (int)StorageData.HiScore, hiscore); - } - else if (IsKeyPressed(KeyboardKey.Space)) - { - // NOTE: If requested position could not be found, value 0 is returned - score = LoadStorageValue(storageDataFile, (int)StorageData.Score); - hiscore = LoadStorageValue(storageDataFile, (int)StorageData.HiScore); - } - - framesCounter++; - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText($"SCORE: {score}", 280, 130, 40, Color.Maroon); - DrawText($"HI-SCORE: {hiscore}", 210, 200, 50, Color.Black); - - DrawText($"frames: {framesCounter}", 10, 10, 20, Color.Lime); - - DrawText("Press R to generate random numbers", 220, 40, 20, Color.LightGray); - DrawText("Press ENTER to SAVE values", 250, 310, 20, Color.LightGray); - DrawText("Press SPACE to LOAD values", 252, 350, 20, Color.LightGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; @@ -97,11 +126,11 @@ public class StorageValues { using var fileNameBuffer = fileName.ToUtf8Buffer(); - bool success = false; - int dataSize = 0; - int newDataSize = 0; + var success = false; + var dataSize = 0; + var newDataSize = 0; - byte* fileData = LoadFileData(fileNameBuffer.AsPointer(), &dataSize); + var fileData = LoadFileData(fileNameBuffer.AsPointer(), &dataSize); byte* newFileData = null; if (fileData != null) @@ -115,13 +144,13 @@ public class StorageValues if (newFileData != null) { // RL_REALLOC succeded - int* dataPtr = (int*)newFileData; + var dataPtr = (int*)newFileData; dataPtr[position] = value; } else { // RL_REALLOC failed - int positionInBytes = position * sizeof(int); + var positionInBytes = position * sizeof(int); TraceLog( TraceLogLevel.Warning, @$"FILEIO: [{fileName}] Failed to realloc data ({dataSize}), @@ -140,7 +169,7 @@ public class StorageValues newDataSize = dataSize; // Replace value on selected position - int* dataPtr = (int*)newFileData; + var dataPtr = (int*)newFileData; dataPtr[position] = value; } @@ -155,7 +184,7 @@ public class StorageValues dataSize = (position + 1) * sizeof(int); fileData = (byte*)MemAlloc((uint)dataSize); - int* dataPtr = (int*)fileData; + var dataPtr = (int*)fileData; dataPtr[position] = value; success = SaveFileData(fileNameBuffer.AsPointer(), fileData, dataSize); @@ -173,9 +202,9 @@ public class StorageValues { using var fileNameBuffer = fileName.ToUtf8Buffer(); - int value = 0; - int dataSize = 0; - byte* fileData = LoadFileData(fileNameBuffer.AsPointer(), &dataSize); + var value = 0; + var dataSize = 0; + var fileData = LoadFileData(fileNameBuffer.AsPointer(), &dataSize); if (fileData != null) { @@ -188,7 +217,7 @@ public class StorageValues } else { - int* dataPtr = (int*)fileData; + var dataPtr = (int*)fileData; value = dataPtr[position]; } diff --git a/Examples/Core/VrSimulator.cs b/Examples/Core/VrSimulator.cs index f2b3894..fe27700 100644 --- a/Examples/Core/VrSimulator.cs +++ b/Examples/Core/VrSimulator.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [core] example - VR Simulator (Oculus Rift CV1 parameters) +* raylib [core] example - vr simulator * -* This example has been created using raylib 1.7 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 * -* Copyright (c) 2017 Ramon Santamaria (@raysan5) +* Example originally created with raylib 2.5, last time updated with raylib 4.0 +* +* 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) 2017-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -15,51 +19,62 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class VrSimulator +public partial class VrSimulator : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + public string Name => "Core / VR Simulator"; + + public string Title => "raylib [core] example - vr simulator"; + + public bool CursorDisabled => true; + + private VrStereoConfig config; + private Shader distortion; + private RenderTexture2D target; + private Rectangle sourceRec; + private Rectangle destRec; + private Camera3D camera; + private Vector3 cubePosition; + + public unsafe void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 1080; - const int screenHeight = 600; - - SetConfigFlags(ConfigFlags.Msaa4xHint); - InitWindow(screenWidth, screenHeight, "raylib [core] example - vr simulator"); - // VR device parameters definition - VrDeviceInfo device = new VrDeviceInfo + var device = new VrDeviceInfo { // Oculus Rift CV1 parameters for simulator - HResolution = 2160, - VResolution = 1200, - HScreenSize = 0.133793f, - VScreenSize = 0.0669f, - EyeToScreenDistance = 0.041f, - LensSeparationDistance = 0.07f, - InterpupillaryDistance = 0.07f, + HResolution = 2160, // Horizontal resolution in pixels + VResolution = 1200, // Vertical resolution in pixels + HScreenSize = 0.133793f, // Horizontal size in meters + VScreenSize = 0.0669f, // Vertical size in meters + EyeToScreenDistance = 0.041f, // Distance between eye and display in meters + LensSeparationDistance = 0.07f, // Lens separation distance in meters + InterpupillaryDistance = 0.07f, // IPD (distance between pupils) in meters }; - // NOTE: CV1 uses a Fresnel-hybrid-asymmetric lenses with specific distortion compute shaders. - // Following parameters are an approximation to distortion stereo rendering but results differ from actual - // device. - unsafe - { - device.LensDistortionValues[0] = 1.0f; - device.LensDistortionValues[1] = 0.22f; - device.LensDistortionValues[2] = 0.24f; - device.LensDistortionValues[3] = 0.0f; - device.ChromaAbCorrection[0] = 0.996f; - device.ChromaAbCorrection[1] = -0.004f; - device.ChromaAbCorrection[2] = 1.014f; - device.ChromaAbCorrection[3] = 0.0f; - } + // NOTE: CV1 uses fresnel-hybrid-asymmetric lenses with specific compute shaders + // Following parameters are just an approximation to CV1 distortion stereo rendering + device.LensDistortionValues[0] = 1.0f; // Lens distortion constant parameter 0 + device.LensDistortionValues[1] = 0.22f; // Lens distortion constant parameter 1 + device.LensDistortionValues[2] = 0.24f; // Lens distortion constant parameter 2 + device.LensDistortionValues[3] = 0.0f; // Lens distortion constant parameter 3 + device.ChromaAbCorrection[0] = 0.996f; // Chromatic aberration correction parameter 0 + device.ChromaAbCorrection[1] = -0.004f; // Chromatic aberration correction parameter 1 + device.ChromaAbCorrection[2] = 1.014f; // Chromatic aberration correction parameter 2 + device.ChromaAbCorrection[3] = 0.0f; // Chromatic aberration correction parameter 3 // Load VR stereo config for VR device parameteres (Oculus Rift CV1 parameters) - VrStereoConfig config = LoadVrStereoConfig(device); + config = LoadVrStereoConfig(device); // Distortion shader (uses device lens distortion and chroma) - Shader distortion = LoadShader(null, "resources/distortion330.fs"); + distortion = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/distortion.fs"); // Update distortion shader with lens and distortion-scale parameters Raylib.SetShaderValue( @@ -100,88 +115,104 @@ public class VrSimulator ShaderUniformDataType.Vec2 ); - unsafe - { - SetShaderValue( - distortion, - GetShaderLocation(distortion, "deviceWarpParam"), - device.LensDistortionValues, - ShaderUniformDataType.Vec4 - ); - SetShaderValue( - distortion, - GetShaderLocation(distortion, "chromaAbParam"), - device.ChromaAbCorrection, - ShaderUniformDataType.Vec4 - ); - } + SetShaderValue( + distortion, + GetShaderLocation(distortion, "deviceWarpParam"), + device.LensDistortionValues, + ShaderUniformDataType.Vec4 + ); + SetShaderValue( + distortion, + GetShaderLocation(distortion, "chromaAbParam"), + device.ChromaAbCorrection, + ShaderUniformDataType.Vec4 + ); // Initialize framebuffer for stereo rendering // NOTE: Screen size should match HMD aspect ratio - RenderTexture2D target = LoadRenderTexture(GetScreenWidth(), GetScreenHeight()); + target = LoadRenderTexture(device.HResolution, device.VResolution); + + // The target's height is flipped (in the source Rectangle), due to OpenGL reasons + sourceRec = new(0.0f, 0.0f, (float)target.Texture.Width, -(float)target.Texture.Height); + destRec = new(0.0f, 0.0f, (float)GetScreenWidth(), (float)GetScreenHeight()); // Define the camera to look into our 3d world - Camera3D camera; - camera.Position = new Vector3(5.0f, 2.0f, 5.0f); - camera.Target = new Vector3(0.0f, 2.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 60.0f; - camera.Projection = CameraProjection.Perspective; + camera = new(); + camera.Position = new Vector3(5.0f, 2.0f, 5.0f); // Camera position + camera.Target = new Vector3(0.0f, 2.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector + camera.FovY = 60.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type - Vector3 cubePosition = new(0.0f, 0.0f, 0.0f); + cubePosition = new(0.0f, 0.0f, 0.0f); + } - SetTargetFPS(90); // Set our game to run at 90 frames-per-second + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.FirstPerson); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginTextureMode(target); + ClearBackground(Color.RayWhite); + BeginVrStereoMode(config); + BeginMode3D(camera); + + DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red); + DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon); + DrawGrid(40, 1.0f); + + EndMode3D(); + EndVrStereoMode(); + EndTextureMode(); + + BeginDrawing(); + ClearBackground(Color.RayWhite); + BeginShaderMode(distortion); + DrawTexturePro(target.Texture, sourceRec, destRec, new Vector2(0.0f, 0.0f), 0.0f, Color.White); + EndShaderMode(); + DrawFPS(10, 10); + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadVrStereoConfig(config); // Unload stereo config + + UnloadRenderTexture(target); // Unload stereo render fbo + UnloadShader(distortion); // Unload distortion shader + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + // NOTE: screenWidth/screenHeight should match VR device aspect ratio + InitWindow(screenWidth, screenHeight, "raylib [core] example - vr simulator"); + + DisableCursor(); // Limit cursor to relative movement inside the window + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new VrSimulator(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.FirstPerson); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginTextureMode(target); - ClearBackground(Color.RayWhite); - - BeginVrStereoMode(config); - BeginMode3D(camera); - - DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red); - DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon); - DrawGrid(40, 1.0f); - - EndMode3D(); - EndVrStereoMode(); - EndTextureMode(); - - BeginShaderMode(distortion); - DrawTextureRec( - target.Texture, - new Rectangle(0, 0, (float)target.Texture.Width, (float)-target.Texture.Height), - new Vector2(0.0f, 0.0f), - Color.White - ); - EndShaderMode(); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadVrStereoConfig(config); - UnloadRenderTexture(target); - UnloadShader(distortion); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Core/WindowFlags.cs b/Examples/Core/WindowFlags.cs index b963a17..172ef69 100644 --- a/Examples/Core/WindowFlags.cs +++ b/Examples/Core/WindowFlags.cs @@ -2,10 +2,14 @@ * * raylib [core] example - window flags * -* This example has been created using raylib 3.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 * -* Copyright (c) 2020 Ramon Santamaria (@raysan5) +* Example originally created with raylib 3.5, last time updated with raylib 3.5 +* +* 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) 2020-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -15,15 +19,238 @@ using static Raylib_cs.ConfigFlags; namespace Examples.Core; -public class WindowFlags +public partial class WindowFlags : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Core / Window Flags"; + + public string Title => "raylib [core] example - window flags"; + + private Vector2 ballPosition; + private Vector2 ballSpeed; + private float ballRadius; + + private int framesCounter = 0; + + public void Init() + { + ballPosition = new(GetScreenWidth() / 2.0f, GetScreenHeight() / 2.0f); + ballSpeed = new(5.0f, 4.0f); + ballRadius = 20; + + framesCounter = 0; + } + + public void Update() + { + // Update + //----------------------------------------------------- + if (IsKeyPressed(KeyboardKey.F)) + { + // modifies window size when scaling! + ToggleFullscreen(); + } + + if (IsKeyPressed(KeyboardKey.R)) + { + if (IsWindowState(ResizableWindow)) + { + ClearWindowState(ResizableWindow); + } + else + { + SetWindowState(ResizableWindow); + } + } + + if (IsKeyPressed(KeyboardKey.D)) + { + if (IsWindowState(UndecoratedWindow)) + { + ClearWindowState(UndecoratedWindow); + } + else + { + SetWindowState(UndecoratedWindow); + } + } + + if (IsKeyPressed(KeyboardKey.H)) + { + if (!IsWindowState(HiddenWindow)) + { + SetWindowState(HiddenWindow); + } + + framesCounter = 0; + } + + if (IsWindowState(HiddenWindow)) + { + framesCounter++; + if (framesCounter >= 240) + { + // Show window after 3 seconds + ClearWindowState(HiddenWindow); + } + } + + if (IsKeyPressed(KeyboardKey.N)) + { + if (!IsWindowState(MinimizedWindow)) + { + MinimizeWindow(); + } + + framesCounter = 0; + } + + if (IsWindowState(MinimizedWindow)) + { + framesCounter++; + if (framesCounter >= 240) + { + // Restore window after 3 seconds + RestoreWindow(); + } + } + + if (IsKeyPressed(KeyboardKey.M)) + { + // NOTE: Requires FLAG_WINDOW_RESIZABLE enabled! + if (IsWindowState(MaximizedWindow)) + { + RestoreWindow(); + } + else + { + MaximizeWindow(); + } + } + + if (IsKeyPressed(KeyboardKey.U)) + { + if (IsWindowState(UnfocusedWindow)) + { + ClearWindowState(UnfocusedWindow); + } + else + { + SetWindowState(UnfocusedWindow); + } + } + + if (IsKeyPressed(KeyboardKey.T)) + { + if (IsWindowState(TopmostWindow)) + { + ClearWindowState(TopmostWindow); + } + else + { + SetWindowState(TopmostWindow); + } + } + + if (IsKeyPressed(KeyboardKey.A)) + { + if (IsWindowState(AlwaysRunWindow)) + { + ClearWindowState(AlwaysRunWindow); + } + else + { + SetWindowState(AlwaysRunWindow); + } + } + + if (IsKeyPressed(KeyboardKey.V)) + { + if (IsWindowState(VSyncHint)) + { + ClearWindowState(VSyncHint); + } + else + { + SetWindowState(VSyncHint); + } + } + + if (IsKeyPressed(KeyboardKey.B)) + { + ToggleBorderlessWindowed(); + } + + // Bouncing ball logic + ballPosition.X += ballSpeed.X; + ballPosition.Y += ballSpeed.Y; + if ((ballPosition.X >= (GetScreenWidth() - ballRadius)) || (ballPosition.X <= ballRadius)) + { + ballSpeed.X *= -1.0f; + } + if ((ballPosition.Y >= (GetScreenHeight() - ballRadius)) || (ballPosition.Y <= ballRadius)) + { + ballSpeed.Y *= -1.0f; + } + //----------------------------------------------------- + + // Draw + //----------------------------------------------------- + BeginDrawing(); + + if (IsWindowState(TransparentWindow)) + { + ClearBackground(Color.Blank); + } + else + { + ClearBackground(Color.RayWhite); + } + + DrawCircleV(ballPosition, ballRadius, Color.Maroon); + DrawRectangleLinesEx(new Rectangle(0, 0, GetScreenWidth(), GetScreenHeight()), 4, Color.RayWhite); + + DrawCircleV(GetMousePosition(), 10, Color.DarkBlue); + + DrawFPS(10, 10); + + DrawText($"Screen Size: [{GetScreenWidth()}, {GetScreenHeight()}]", 10, 40, 10, Color.Green); + + // Draw window state info + DrawText("Following flags can be set after window creation:", 10, 60, 10, Color.Gray); + + DrawWindowState(FullscreenMode, "[F] FLAG_FULLSCREEN_MODE: ", 10, 80, 10); + DrawWindowState(ResizableWindow, "[R] FLAG_WINDOW_RESIZABLE: ", 10, 100, 10); + DrawWindowState(UndecoratedWindow, "[D] FLAG_WINDOW_UNDECORATED: ", 10, 120, 10); + DrawWindowState(HiddenWindow, "[H] FLAG_WINDOW_HIDDEN: ", 10, 140, 10); + DrawWindowState(MinimizedWindow, "[N] FLAG_WINDOW_MINIMIZED: ", 10, 160, 10); + DrawWindowState(MaximizedWindow, "[M] FLAG_WINDOW_MAXIMIZED: ", 10, 180, 10); + DrawWindowState(UnfocusedWindow, "[G] FLAG_WINDOW_UNFOCUSED: ", 10, 200, 10); + DrawWindowState(TopmostWindow, "[T] FLAG_WINDOW_TOPMOST: ", 10, 220, 10); + DrawWindowState(AlwaysRunWindow, "[A] FLAG_WINDOW_ALWAYS_RUN: ", 10, 240, 10); + DrawWindowState(VSyncHint, "[V] FLAG_VSYNC_HINT: ", 10, 260, 10); + DrawWindowState(BorderlessWindowMode, "[B] FLAG_BORDERLESS_WINDOWED_MODE: ", 10, 280, 10); + + DrawText("Following flags can only be set before window creation:", 10, 320, 10, Color.Gray); + + DrawWindowState(HighDpiWindow, "FLAG_WINDOW_HIGHDPI: ", 10, 340, 10); + DrawWindowState(TransparentWindow, "FLAG_WINDOW_TRANSPARENT: ", 10, 360, 10); + DrawWindowState(Msaa4xHint, "FLAG_MSAA_4X_HINT: ", 10, 380, 10); + + EndDrawing(); + //----------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //--------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - // Possible window flags /* FLAG_VSYNC_HINT @@ -42,235 +269,43 @@ public class WindowFlags */ // Set configuration flags for window creation - SetConfigFlags(VSyncHint | Msaa4xHint); + //SetConfigFlags(VSyncHint | Msaa4xHint | HighDpiWindow);// | TransparentWindow); InitWindow(screenWidth, screenHeight, "raylib [core] example - window flags"); - Vector2 ballPosition = new(GetScreenWidth() / 2, GetScreenHeight() / 2); - Vector2 ballSpeed = new(5.0f, 4.0f); - int ballRadius = 20; - - int framesCounter = 0; + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //---------------------------------------------------------- + var game = new WindowFlags(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //----------------------------------------------------- - if (IsKeyPressed(KeyboardKey.F)) - { - // modifies window size when scaling! - ToggleFullscreen(); - } - - if (IsKeyPressed(KeyboardKey.R)) - { - if (IsWindowState(ResizableWindow)) - { - ClearWindowState(ResizableWindow); - } - else - { - SetWindowState(ResizableWindow); - } - } - - if (IsKeyPressed(KeyboardKey.D)) - { - if (IsWindowState(UndecoratedWindow)) - { - ClearWindowState(UndecoratedWindow); - } - else - { - SetWindowState(UndecoratedWindow); - } - } - - if (IsKeyPressed(KeyboardKey.H)) - { - if (!IsWindowState(HiddenWindow)) - { - SetWindowState(HiddenWindow); - } - - framesCounter = 0; - } - - if (IsWindowState(HiddenWindow)) - { - framesCounter++; - if (framesCounter >= 240) - { - // Show window after 3 seconds - ClearWindowState(HiddenWindow); - } - } - - if (IsKeyPressed(KeyboardKey.N)) - { - if (!IsWindowState(MinimizedWindow)) - { - MinimizeWindow(); - } - - framesCounter = 0; - } - - if (IsWindowState(MinimizedWindow)) - { - framesCounter++; - if (framesCounter >= 240) - { - // Restore window after 3 seconds - RestoreWindow(); - } - } - - if (IsKeyPressed(KeyboardKey.M)) - { - // NOTE: Requires FLAG_WINDOW_RESIZABLE enabled! - if (IsWindowState(MaximizedWindow)) - { - RestoreWindow(); - } - else - { - MaximizeWindow(); - } - } - - if (IsKeyPressed(KeyboardKey.U)) - { - if (IsWindowState(UnfocusedWindow)) - { - ClearWindowState(UnfocusedWindow); - } - else - { - SetWindowState(UnfocusedWindow); - } - } - - if (IsKeyPressed(KeyboardKey.T)) - { - if (IsWindowState(TopmostWindow)) - { - ClearWindowState(TopmostWindow); - } - else - { - SetWindowState(TopmostWindow); - } - } - - if (IsKeyPressed(KeyboardKey.A)) - { - if (IsWindowState(AlwaysRunWindow)) - { - ClearWindowState(AlwaysRunWindow); - } - else - { - SetWindowState(AlwaysRunWindow); - } - } - - if (IsKeyPressed(KeyboardKey.V)) - { - if (IsWindowState(VSyncHint)) - { - ClearWindowState(VSyncHint); - } - else - { - SetWindowState(VSyncHint); - } - } - - // Bouncing ball logic - ballPosition.X += ballSpeed.X; - ballPosition.Y += ballSpeed.Y; - if ((ballPosition.X >= (GetScreenWidth() - ballRadius)) || (ballPosition.X <= ballRadius)) - { - ballSpeed.X *= -1.0f; - } - if ((ballPosition.Y >= (GetScreenHeight() - ballRadius)) || (ballPosition.Y <= ballRadius)) - { - ballSpeed.Y *= -1.0f; - } - //----------------------------------------------------- - - // Draw - //----------------------------------------------------- - BeginDrawing(); - - if (IsWindowState(TransparentWindow)) - { - ClearBackground(Color.Blank); - } - else - { - ClearBackground(Color.RayWhite); - } - - DrawCircleV(ballPosition, ballRadius, Color.Maroon); - DrawRectangleLinesEx(new Rectangle(0, 0, GetScreenWidth(), GetScreenHeight()), 4, Color.RayWhite); - - DrawCircleV(GetMousePosition(), 10, Color.DarkBlue); - - DrawFPS(10, 10); - - DrawText($"Screen Size: [{GetScreenWidth()}, {GetScreenHeight()}]", 10, 40, 10, Color.Green); - - // Draw window state info - Color on = Color.Lime; - Color off = Color.Maroon; - - DrawText("Following flags can be set after window creation:", 10, 60, 10, Color.Gray); - - DrawWindowState(FullscreenMode, "[F] FLAG_FULLSCREEN_MODE: ", 10, 80, 10); - DrawWindowState(ResizableWindow, "[R] FLAG_WINDOW_RESIZABLE: ", 10, 100, 10); - DrawWindowState(UndecoratedWindow, "[D] FLAG_WINDOW_UNDECORATED: ", 10, 120, 10); - DrawWindowState(HiddenWindow, "[H] FLAG_WINDOW_HIDDEN: ", 10, 140, 10); - DrawWindowState(MinimizedWindow, "[N] FLAG_WINDOW_MINIMIZED: ", 10, 160, 10); - DrawWindowState(MaximizedWindow, "[M] FLAG_WINDOW_MAXIMIZED: ", 10, 180, 10); - DrawWindowState(UnfocusedWindow, "[G] FLAG_WINDOW_UNFOCUSED: ", 10, 200, 10); - DrawWindowState(TopmostWindow, "[T] FLAG_WINDOW_TOPMOST: ", 10, 220, 10); - DrawWindowState(AlwaysRunWindow, "[A] FLAG_WINDOW_ALWAYS_RUN: ", 10, 240, 10); - DrawWindowState(VSyncHint, "[V] FLAG_VSYNC_HINT: ", 10, 260, 10); - - DrawText("Following flags can only be set before window creation:", 10, 300, 10, Color.Gray); - - DrawWindowState(HighDpiWindow, "[F] FLAG_WINDOW_HIGHDPI: ", 10, 320, 10); - DrawWindowState(TransparentWindow, "[F] FLAG_WINDOW_TRANSPARENT: ", 10, 340, 10); - DrawWindowState(Msaa4xHint, "[F] FLAG_MSAA_4X_HINT: ", 10, 360, 10); - - EndDrawing(); - //----------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //--------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //---------------------------------------------------------- return 0; } - static void DrawWindowState(ConfigFlags flag, string text, int posX, int posY, int fontSize) + private static void DrawWindowState(ConfigFlags flag, string text, int posX, int posY, int fontSize) { - Color onColor = Color.Lime; - Color offColor = Color.Maroon; + var onColor = Color.Lime; + var offColor = Color.Maroon; if (Raylib.IsWindowState(flag)) { - DrawText($"{text} on", posX, posY, fontSize, onColor); + DrawText($"{text}on", posX, posY, fontSize, onColor); } else { - DrawText($"{text} off", posX, posY, fontSize, offColor); + DrawText($"{text}off", posX, posY, fontSize, offColor); } } } - diff --git a/Examples/Core/WindowLetterbox.cs b/Examples/Core/WindowLetterbox.cs index 80b16f2..a9341de 100644 --- a/Examples/Core/WindowLetterbox.cs +++ b/Examples/Core/WindowLetterbox.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [core] example - window scale letterbox +* raylib [core] example - window letterbox * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 2.5, last time updated with raylib 4.0 * * Example contributed by Anata (@anatagawa) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2019 Anata (@anatagawa) and 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) 2019-2025 Anata (@anatagawa) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -17,123 +21,153 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class WindowLetterbox +public partial class WindowLetterbox : IExample { - public static int Main() + private const int windowWidth = 800; + private const int windowHeight = 450; + + private const int gamescreenWidth = 640; + private const int gamescreenHeight = 480; + + public string Name => "Core / Window Letterbox"; + + public string Title => "raylib [core] example - window letterbox"; + + public ConfigFlags ConfigFlags => ConfigFlags.ResizableWindow | ConfigFlags.VSyncHint; + + private RenderTexture2D target; + private Color[] colors; + + public void Init() { - const int windowWidth = 800; - const int windowHeight = 450; - - // Enable config flags for resizable window and vertical synchro - SetConfigFlags(ConfigFlags.ResizableWindow | ConfigFlags.VSyncHint); - InitWindow(windowWidth, windowHeight, "raylib [core] example - window scale letterbox"); - SetWindowMinSize(320, 240); - - int gameScreenWidth = 640; - int gameScreenHeight = 480; - // Render texture initialization, used to hold the rendering result so we can easily resize it - RenderTexture2D target = LoadRenderTexture(gameScreenWidth, gameScreenHeight); - SetTextureFilter(target.Texture, TextureFilter.Bilinear); + target = LoadRenderTexture(gamescreenWidth, gamescreenHeight); + SetTextureFilter(target.Texture, TextureFilter.Bilinear); // Texture scale filter to use - Color[] colors = new Color[10]; - for (int i = 0; i < 10; i++) + colors = new Color[10]; + for (var i = 0; i < 10; i++) { colors[i] = new Color(GetRandomValue(100, 250), GetRandomValue(50, 150), GetRandomValue(10, 100), 255); } + } - SetTargetFPS(60); + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + // Compute required framebuffer scaling + var scale = MathF.Min( + (float)GetScreenWidth() / gamescreenWidth, + (float)GetScreenHeight() / gamescreenHeight + ); + + if (IsKeyPressed(KeyboardKey.Space)) + { + // Recalculate random colors for the bars + for (var i = 0; i < 10; i++) + { + colors[i] = new Color( + GetRandomValue(100, 250), + GetRandomValue(50, 150), + GetRandomValue(10, 100), + 255 + ); + } + } + + // Update virtual mouse (clamped mouse value behind game screen) + var mouse = GetMousePosition(); + var virtualMouse = Vector2.Zero; + virtualMouse.X = (mouse.X - (GetScreenWidth() - (gamescreenWidth * scale)) * 0.5f) / scale; + virtualMouse.Y = (mouse.Y - (GetScreenHeight() - (gamescreenHeight * scale)) * 0.5f) / scale; + + Vector2 max = new((float)gamescreenWidth, (float)gamescreenHeight); + virtualMouse = Vector2.Clamp(virtualMouse, Vector2.Zero, max); + + // Apply the same transformation as the virtual mouse to the real mouse (i.e. to work with raygui) + //SetMouseOffset(-(GetScreenWidth() - (gamescreenWidth*scale))*0.5f, -(GetScreenHeight() - (gamescreenHeight*scale))*0.5f); + //SetMouseScale(1/scale, 1/scale); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + // Draw everything in the render texture, note this will not be rendered on screen, yet + BeginTextureMode(target); + ClearBackground(Color.RayWhite); // Clear render texture background color + + for (var i = 0; i < 10; i++) + { + DrawRectangle(0, (gamescreenHeight / 10) * i, gamescreenWidth, gamescreenHeight / 10, colors[i]); + } + + DrawText( + "If executed inside a window,\nyou can resize the window,\nand see the screen scaling!", + 10, + 25, + 20, + Color.White + ); + + DrawText($"Default Mouse: [{(int)mouse.X} , {(int)mouse.Y}]", 350, 25, 20, Color.Green); + DrawText($"Virtual Mouse: [{(int)virtualMouse.X} , {(int)virtualMouse.Y}]", 350, 55, 20, Color.Yellow); + + EndTextureMode(); + + BeginDrawing(); + ClearBackground(Color.Black); // Clear screen background + + // Draw render texture to screen, properly scaled + Rectangle sourceRec = new( + 0.0f, + 0.0f, + (float)target.Texture.Width, + (float)-target.Texture.Height + ); + Rectangle destRec = new( + (GetScreenWidth() - ((float)gamescreenWidth * scale)) * 0.5f, + (GetScreenHeight() - ((float)gamescreenHeight * scale)) * 0.5f, + (float)gamescreenWidth * scale, + (float)gamescreenHeight * scale + ); + DrawTexturePro(target.Texture, sourceRec, destRec, new Vector2(0, 0), 0.0f, Color.White); + + EndDrawing(); + //-------------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadRenderTexture(target); // Unload render texture + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + // Enable config flags for resizable window and vertical synchro + SetConfigFlags(ConfigFlags.ResizableWindow | ConfigFlags.VSyncHint); + InitWindow(windowWidth, windowHeight, "raylib [core] example - window letterbox"); + SetWindowMinSize(320, 240); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new WindowLetterbox(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // Compute required framebuffer scaling - float scale = MathF.Min( - (float)GetScreenWidth() / gameScreenWidth, - (float)GetScreenHeight() / gameScreenHeight - ); - - if (IsKeyPressed(KeyboardKey.Space)) - { - // Recalculate random colors for the bars - for (int i = 0; i < 10; i++) - { - colors[i] = new Color( - GetRandomValue(100, 250), - GetRandomValue(50, 150), - GetRandomValue(10, 100), - 255 - ); - } - } - - // Update virtual mouse (clamped mouse value behind game screen) - Vector2 mouse = GetMousePosition(); - Vector2 virtualMouse = Vector2.Zero; - virtualMouse.X = (mouse.X - (GetScreenWidth() - (gameScreenWidth * scale)) * 0.5f) / scale; - virtualMouse.Y = (mouse.Y - (GetScreenHeight() - (gameScreenHeight * scale)) * 0.5f) / scale; - - Vector2 max = new((float)gameScreenWidth, (float)gameScreenHeight); - virtualMouse = Vector2.Clamp(virtualMouse, Vector2.Zero, max); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.Black); - - // Draw everything in the render texture, note this will not be rendered on screen, yet - BeginTextureMode(target); - ClearBackground(Color.RayWhite); - - for (int i = 0; i < 10; i++) - { - DrawRectangle(0, (gameScreenHeight / 10) * i, gameScreenWidth, gameScreenHeight / 10, colors[i]); - } - - DrawText( - "If executed inside a window,\nyou can resize the window,\nand see the screen scaling!", - 10, - 25, - 20, - Color.White - ); - - DrawText($"Default Mouse: [{(int)mouse.X} {(int)mouse.Y}]", 350, 25, 20, Color.Green); - DrawText($"Virtual Mouse: [{(int)virtualMouse.X}, {(int)virtualMouse.Y}]", 350, 55, 20, Color.Yellow); - - EndTextureMode(); - - // Draw RenderTexture2D to window, properly scaled - Rectangle sourceRec = new( - 0.0f, - 0.0f, - (float)target.Texture.Width, - (float)-target.Texture.Height - ); - Rectangle destRec = new( - (GetScreenWidth() - ((float)gameScreenWidth * scale)) * 0.5f, - (GetScreenHeight() - ((float)gameScreenHeight * scale)) * 0.5f, - (float)gameScreenWidth * scale, - (float)gameScreenHeight * scale - ); - DrawTexturePro(target.Texture, sourceRec, destRec, new Vector2(0, 0), 0.0f, Color.White); - - EndDrawing(); - //-------------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadRenderTexture(target); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } } - diff --git a/Examples/Core/WorldScreen.cs b/Examples/Core/WorldScreen.cs index 72f9a20..57aa4e4 100644 --- a/Examples/Core/WorldScreen.cs +++ b/Examples/Core/WorldScreen.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [core] example - World to screen +* raylib [core] example - world screen * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.3, last time updated with raylib 1.4 +* +* 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,84 +18,113 @@ using static Raylib_cs.Raylib; namespace Examples.Core; -public class WorldScreen +public partial class WorldScreen : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Core / World to Screen"; + + public string Title => "raylib [core] example - world screen"; + + public bool CursorDisabled => true; + + private Camera3D camera; + private Vector3 cubePosition; + private Vector2 cubeScreenPosition = new(0.0f, 0.0f); + + public void Init() + { + // Define the camera to look into our 3d world + camera = new(); + camera.Position = new Vector3(10.0f, 10.0f, 10.0f); // Camera position + camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type + + cubePosition = new(0.0f, 0.0f, 0.0f); + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.ThirdPerson); + + // Calculate cube screen space position (with a little offset to be in top) + cubeScreenPosition = GetWorldToScreen( + new Vector3(cubePosition.X, cubePosition.Y + 2.5f, cubePosition.Z), + camera + ); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red); + DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon); + + DrawGrid(10, 1.0f); + + EndMode3D(); + + DrawText( + "Enemy: 100/100", + (int)cubeScreenPosition.X - MeasureText("Enemy: 100/100", 20) / 2, + (int)cubeScreenPosition.Y, + 20, + Color.Black + ); + + DrawText( + $"Cube position in screen space coordinates: [{(int)cubeScreenPosition.X}, {(int)cubeScreenPosition.Y}]", + 10, + 10, + 20, + Color.Lime + ); + DrawText("Text 2d should be always on top of the cube", 10, 40, 20, Color.Gray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [core] example - world screen"); - InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera free"); + DisableCursor(); // Limit cursor to relative movement inside the window - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(10.0f, 10.0f, 10.0f); - camera.Target = new Vector3(0.0f, 0.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; - - Vector3 cubePosition = new(0.0f, 0.0f, 0.0f); - Vector2 cubeScreenPosition; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new WorldScreen(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Free); - - // Calculate cube screen space position (with a little offset to be in top) - cubeScreenPosition = GetWorldToScreen( - new Vector3(cubePosition.X, cubePosition.Y + 2.5f, cubePosition.Z), - camera - ); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red); - DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon); - - DrawGrid(10, 1.0f); - - EndMode3D(); - - DrawText( - "Enemy: 100 / 100", - (int)cubeScreenPosition.X - MeasureText("Enemy: 100 / 100", 20) / 2, - (int)cubeScreenPosition.Y, - 20, - Color.Black - ); - DrawText( - "Text is always on top of the cube", - (screenWidth - MeasureText("Text is always on top of the cube", 20)) / 2, - 25, - 20, - Color.Gray - ); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } } - diff --git a/Examples/ExampleRegistry.cs b/Examples/ExampleRegistry.cs new file mode 100644 index 0000000..12ee307 --- /dev/null +++ b/Examples/ExampleRegistry.cs @@ -0,0 +1,73 @@ +using System.Diagnostics.CodeAnalysis; +using Examples.Core; +using Examples.Models; +using Examples.Shapes; + +namespace Examples; + +/// +/// Discovers every implementation in this assembly via reflection and +/// derives the per-platform lists: (Program.cs) and +/// (Web/Host.cs). Ordering is by category (browser dropdown +/// grouping), then display name. +/// +public static class ExampleRegistry +{ + /// Category order used for the browser dropdown and desktop run-all sequence. + private static readonly string[] CategoryOrder = + [ + "Core", + "Shapes", + "Models", + "Textures", + "Text", + "Audio", + "Shaders", + ]; + + /// Desktop examples omitted from the browser host (platform limitations). + private static readonly Type[] DesktopExcludedFromBrowser = + [ + typeof(DropFiles), + typeof(LoadingThread), // System.Threading.Thread is unsupported on single-threaded wasm + typeof(SkyboxDemo), + ]; + + /// Browser-only shape examples not registered for desktop CLI runs. + private static readonly Type[] BrowserOnly = + [ + typeof(DrawCircleSector), + typeof(DrawRectangleRounded), + typeof(DrawRing), + ]; + + private static readonly IExample[] AllExamples = DiscoverAll(); + + public static readonly IExample[] DesktopExamples = + Array.FindAll(AllExamples, e => Array.IndexOf(BrowserOnly, e.GetType()) < 0); + + public static readonly IExample[] BrowserExamples = + Array.FindAll(AllExamples, e => Array.IndexOf(DesktopExcludedFromBrowser, e.GetType()) < 0); + + [UnconditionalSuppressMessage("Trimming", "IL2026", + Justification = "The Examples assembly is rooted via TrimmerRootAssembly in Examples.csproj.")] + [UnconditionalSuppressMessage("Trimming", "IL2067", + Justification = "Same as IL2026: all example types and their parameterless constructors are rooted.")] + private static IExample[] DiscoverAll() + { + return typeof(IExample).Assembly + .GetTypes() + .Where(t => typeof(IExample).IsAssignableFrom(t) && t.IsClass && !t.IsAbstract) + .Select(t => (IExample)Activator.CreateInstance(t)) + .OrderBy(e => Array.IndexOf(CategoryOrder, Category(e))) + .ThenBy(e => e.Name, StringComparer.OrdinalIgnoreCase) + .ToArray(); + } + + // "Examples.Core" -> "Core" + private static string Category(IExample example) + { + var ns = example.GetType().Namespace ?? ""; + return ns[(ns.LastIndexOf('.') + 1)..]; + } +} diff --git a/Examples/Examples.csproj b/Examples/Examples.csproj index 56ea2d1..77212a1 100644 --- a/Examples/Examples.csproj +++ b/Examples/Examples.csproj @@ -3,23 +3,69 @@ Exe net8.0;net10.0 true - Examples.Program + Examples.Program + Examples.Web.Host $(MSBuildThisFileDirectory) 12 enable false + + Web/main.js + disable + + $(NoWarn);CA1416 + $(DefineConstants);BROWSER + + + true + true + true + $(EmccFlags) -sALLOW_MEMORY_GROWTH=1 -sINITIAL_MEMORY=64MB + + + -O0 + -O0 + false + false + + - + + + + + + + + + + + + - + + + + + + + + + + + + diff --git a/Examples/IExample.cs b/Examples/IExample.cs new file mode 100644 index 0000000..7c51b49 --- /dev/null +++ b/Examples/IExample.cs @@ -0,0 +1,46 @@ +namespace Examples; + +/// +/// A runnable raylib example. Each example class implements this interface directly: loop-spanning +/// state from the original example's Main lives in instance fields, (re)initialized in +/// so re-selecting an example resets it. +/// +/// +/// Desktop runs the example via its static Main(), a thin driver that owns the window +/// (InitWindow/CloseWindow, SetTargetFPS and friends) and drives , +/// , and around a blocking +/// while (!WindowShouldClose()) loop. In the browser, Web/Host.cs owns the single +/// window and calls one frame at a time from JavaScript, so examples never +/// block. Platform divergences (e.g. GLSL 100 vs 330 shaders) are guarded with #if BROWSER, +/// preferably around a single constant so both platforms share one code path. +/// +/// +public interface IExample +{ + /// Display name shown in the navigation dropdown. + string Name { get; } + + /// Window title, matching the example's standalone Main(). + string Title { get; } + + /// Config flags the desktop runner applies before window creation. + ConfigFlags ConfigFlags => 0; + + /// Target FPS the desktop runner sets after window creation. + int TargetFps => 60; + + /// Whether the desktop runner disables the cursor (relative mouse movement). + bool CursorDisabled => false; + + /// Whether the desktop runner hides the cursor. + bool CursorHidden => false; + + /// One-time setup. + void Init(); + + /// Render one frame, including BeginDrawing/EndDrawing. + void Update(); + + /// Free resources. + void Unload(); +} diff --git a/Examples/Models/BillboardDemo.cs b/Examples/Models/BillboardDemo.cs index 8b1359e..82c81a7 100644 --- a/Examples/Models/BillboardDemo.cs +++ b/Examples/Models/BillboardDemo.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [models] example - Drawing billboards +* raylib [models] example - billboard rendering * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 * -* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.3, last time updated with raylib 3.5 +* +* 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,121 +18,131 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class BillboardDemo +public partial class BillboardDemo : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Models / Billboard Demo"; + + public string Title => "raylib [models] example - billboard rendering"; + + private Camera3D camera; + private Texture2D bill; + private Vector3 billPositionStatic; + private Vector3 billPositionRotating; + private Rectangle source; + private Vector3 billUp; + private Vector2 size; + private Vector2 origin; + private float distanceStatic; + private float distanceRotating; + private float rotation; + + public void Init() + { + // Define the camera to look into our 3d world + camera = new(); + camera.Position = new Vector3(5.0f, 4.0f, 5.0f); // Camera position + camera.Target = new Vector3(0.0f, 2.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type + + bill = LoadTexture("resources/billboard.png"); // Our billboard texture + billPositionStatic = new(0.0f, 2.0f, 0.0f); // Position of static billboard + billPositionRotating = new(1.0f, 2.0f, 1.0f); // Position of rotating billboard + + // Entire billboard texture, source is used to take a segment from a larger texture + source = new(0.0f, 0.0f, (float)bill.Width, (float)bill.Height); + + // NOTE: Billboard locked on axis-Y + billUp = new(0.0f, 1.0f, 0.0f); + + // Set the height of the rotating billboard to 1.0 with the aspect ratio fixed + size = new(source.Width / source.Height, 1.0f); + + // Rotate around origin + // Here we choose to rotate around the image center + origin = size * 0.5f; + + // Distance is needed for the correct billboard draw order + // Larger distance (further away from the camera) should be drawn prior to smaller distance + distanceStatic = 0.0f; + distanceRotating = 0.0f; + rotation = 0.0f; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Orbital); + + rotation += 0.4f; + distanceStatic = Vector3.Distance(camera.Position, billPositionStatic); + distanceRotating = Vector3.Distance(camera.Position, billPositionRotating); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + DrawGrid(10, 1.0f); // Draw a grid + + // Draw order matters! + if (distanceStatic > distanceRotating) + { + DrawBillboard(camera, bill, billPositionStatic, 2.0f, Color.White); + DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, Color.White); + } + else + { + DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, Color.White); + DrawBillboard(camera, bill, billPositionStatic, 2.0f, Color.White); + } + + EndMode3D(); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(bill); // Unload texture + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [models] example - billboard rendering"); - InitWindow(screenWidth, screenHeight, "raylib [models] example - drawing billboards"); - - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(5.0f, 4.0f, 5.0f); - camera.Target = new Vector3(0.0f, 2.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; - - // Our texture billboard - Texture2D bill = LoadTexture("resources/billboard.png"); - - // Position of billboard billboard - Vector3 billPositionStatic = new(0.0f, 2.0f, 0.0f); - Vector3 billPositionRotating = new(1.0f, 2.0f, 1.0f); - - // Entire billboard texture, source is used to take a segment from a larger texture. - Rectangle source = new(0.0f, 0.0f, (float)bill.Width, (float)bill.Height); - - // NOTE: Billboard locked on axis-Y - Vector3 billUp = new(0.0f, 1.0f, 0.0f); - - // Rotate around origin - // Here we choose to rotate around the image center - // NOTE: (-1, 1) is the range where origin.X, origin.Y is inside the texture - Vector2 rotateOrigin = Vector2.Zero; - - // Distance is needed for the correct billboard draw order - // Larger distance (further away from the camera) should be drawn prior to smaller distance. - float distanceStatic = 0.0f; - float distanceRotating = 0.0f; - - float rotation = 0.0f; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new BillboardDemo(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Orbital); - rotation += 0.4f; - distanceStatic = Vector3.Distance(camera.Position, billPositionStatic); - distanceRotating = Vector3.Distance(camera.Position, billPositionRotating); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawGrid(10, 1.0f); - - // Draw order matters! - if (distanceStatic > distanceRotating) - { - DrawBillboard(camera, bill, billPositionStatic, 2.0f, Color.White); - DrawBillboardPro( - camera, - bill, - source, - billPositionRotating, - billUp, - new Vector2(1.0f, 1.0f), - rotateOrigin, - rotation, - Color.White - ); - } - else - { - DrawBillboardPro( - camera, - bill, - source, - billPositionRotating, - billUp, - new Vector2(1.0f, 1.0f), - rotateOrigin, - rotation, - Color.White - ); - DrawBillboard(camera, bill, billPositionStatic, 2.0f, Color.White); - } - - EndMode3D(); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(bill); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } } - diff --git a/Examples/Models/BoxCollisions.cs b/Examples/Models/BoxCollisions.cs index 6d4436c..65bb5bc 100644 --- a/Examples/Models/BoxCollisions.cs +++ b/Examples/Models/BoxCollisions.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [models] example - Detect basic 3d collisions (box vs sphere vs box) +* raylib [models] example - box collisions * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 * -* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.3, last time updated with raylib 3.5 +* +* 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,129 +18,165 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class BoxCollisions +public partial class BoxCollisions : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Models / Box Collisions"; + + public string Title => "raylib [models] example - box collisions"; + + private Camera3D camera; + + private Vector3 playerPosition; + private Vector3 playerSize; + private Color playerColor; + + private Vector3 enemyBoxPos; + private Vector3 enemyBoxSize; + + private Vector3 enemySpherePos; + private float enemySphereSize; + + private bool collision; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - box collisions"); - // Define the camera to look into our 3d world - Camera3D camera = new(); + camera = new(); camera.Position = new Vector3(0.0f, 10.0f, 10.0f); camera.Target = new Vector3(0.0f, 0.0f, 0.0f); camera.Up = new Vector3(0.0f, 1.0f, 0.0f); camera.FovY = 45.0f; camera.Projection = CameraProjection.Perspective; - Vector3 playerPosition = new(0.0f, 1.0f, 2.0f); - Vector3 playerSize = new(1.0f, 2.0f, 1.0f); - Color playerColor = Color.Green; + playerPosition = new(0.0f, 1.0f, 2.0f); + playerSize = new(1.0f, 2.0f, 1.0f); + playerColor = Color.Green; - Vector3 enemyBoxPos = new(-4.0f, 1.0f, 0.0f); - Vector3 enemyBoxSize = new(2.0f, 2.0f, 2.0f); + enemyBoxPos = new(-4.0f, 1.0f, 0.0f); + enemyBoxSize = new(2.0f, 2.0f, 2.0f); - Vector3 enemySpherePos = new(4.0f, 0.0f, 0.0f); - float enemySphereSize = 1.5f; + enemySpherePos = new(4.0f, 0.0f, 0.0f); + enemySphereSize = 1.5f; - bool collision = false; + collision = false; + } - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + + // Move player + if (IsKeyDown(KeyboardKey.Right)) + { + playerPosition.X += 0.2f; + } + else if (IsKeyDown(KeyboardKey.Left)) + { + playerPosition.X -= 0.2f; + } + else if (IsKeyDown(KeyboardKey.Down)) + { + playerPosition.Z += 0.2f; + } + else if (IsKeyDown(KeyboardKey.Up)) + { + playerPosition.Z -= 0.2f; + } + + collision = false; + + // Check collisions player vs enemy-box + BoundingBox box1 = new( + playerPosition - (playerSize / 2), + playerPosition + (playerSize / 2) + ); + BoundingBox box2 = new( + enemyBoxPos - (enemyBoxSize / 2), + enemyBoxPos + (enemyBoxSize / 2) + ); + + if (CheckCollisionBoxes(box1, box2)) + { + collision = true; + } + + // Check collisions player vs enemy-sphere + if (CheckCollisionBoxSphere(box1, enemySpherePos, enemySphereSize)) + { + collision = true; + } + + if (collision) + { + playerColor = Color.Red; + } + else + { + playerColor = Color.Green; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + // Draw enemy-box + DrawCube(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, Color.Gray); + DrawCubeWires(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, Color.DarkGray); + + // Draw enemy-sphere + DrawSphere(enemySpherePos, enemySphereSize, Color.Gray); + DrawSphereWires(enemySpherePos, enemySphereSize, 16, 16, Color.DarkGray); + + // Draw player + DrawCubeV(playerPosition, playerSize, playerColor); + + DrawGrid(10, 1.0f); // Draw a grid + + EndMode3D(); + + DrawText("Move player with arrow keys to collide", 220, 40, 20, Color.Gray); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [models] example - box collisions"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new BoxCollisions(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - - // Move player - if (IsKeyDown(KeyboardKey.Right)) - { - playerPosition.X += 0.2f; - } - else if (IsKeyDown(KeyboardKey.Left)) - { - playerPosition.X -= 0.2f; - } - else if (IsKeyDown(KeyboardKey.Down)) - { - playerPosition.Z += 0.2f; - } - else if (IsKeyDown(KeyboardKey.Up)) - { - playerPosition.Z -= 0.2f; - } - - collision = false; - - // Check collisions player vs enemy-box - BoundingBox box1 = new( - playerPosition - (playerSize / 2), - playerPosition + (playerSize / 2) - ); - BoundingBox box2 = new( - enemyBoxPos - (enemyBoxSize / 2), - enemyBoxPos + (enemyBoxSize / 2) - ); - - if (CheckCollisionBoxes(box1, box2)) - { - collision = true; - } - - // Check collisions player vs enemy-sphere - if (CheckCollisionBoxSphere(box1, enemySpherePos, enemySphereSize)) - { - collision = true; - } - - if (collision) - { - playerColor = Color.Red; - } - else - { - playerColor = Color.Green; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - // Draw enemy-box - DrawCube(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, Color.Gray); - DrawCubeWires(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, Color.DarkGray); - - // Draw enemy-sphere - DrawSphere(enemySpherePos, enemySphereSize, Color.Gray); - DrawSphereWires(enemySpherePos, enemySphereSize, 16, 16, Color.DarkGray); - - // Draw player - DrawCubeV(playerPosition, playerSize, playerColor); - - DrawGrid(10, 1.0f); - - EndMode3D(); - - DrawText("Move player with cursors to collide", 220, 40, 20, Color.Gray); - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Models/CubicmapDemo.cs b/Examples/Models/CubicmapDemo.cs index 1ddaefd..cb4f5c8 100644 --- a/Examples/Models/CubicmapDemo.cs +++ b/Examples/Models/CubicmapDemo.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [models] example - Cubicmap loading and drawing +* raylib [models] example - cubicmap rendering * -* This example has been created using raylib 1.8 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.8, last time updated with raylib 3.5 +* +* 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,91 +18,128 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class CubicmapDemo +public partial class CubicmapDemo : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Models / Cubicmap Demo"; + + public string Title => "raylib [models] example - cubicmap rendering"; + + private Camera3D camera; + private Texture2D cubicmap; + private Texture2D texture; + private Model model; + private Vector3 mapPosition; + private bool pause; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - cubesmap loading and drawing"); - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(16.0f, 14.0f, 16.0f); - camera.Target = new Vector3(0.0f, 0.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; + camera = new(); + camera.Position = new Vector3(16.0f, 14.0f, 16.0f); // Camera position + camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type - Image image = LoadImage("resources/cubicmap.png"); - Texture2D cubicmap = LoadTextureFromImage(image); + var image = LoadImage("resources/cubicmap.png"); // Load cubicmap image (RAM) + cubicmap = LoadTextureFromImage(image); // Convert image to texture to display (VRAM) - Mesh mesh = GenMeshCubicmap(image, new Vector3(1.0f, 1.0f, 1.0f)); - Model model = LoadModelFromMesh(mesh); + var mesh = GenMeshCubicmap(image, new Vector3(1.0f, 1.0f, 1.0f)); + model = LoadModelFromMesh(mesh); // NOTE: By default each cube is mapped to one part of texture atlas - Texture2D texture = LoadTexture("resources/cubicmap_atlas.png"); + texture = LoadTexture("resources/cubicmap_atlas.png"); // Load map texture // Set map diffuse texture Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture); - Vector3 mapPosition = new(-16.0f, 0.0f, -8.0f); - UnloadImage(image); + mapPosition = new(-16.0f, 0.0f, -8.0f); // Set model position - SetTargetFPS(60); + UnloadImage(image); // Unload cubesmap image from RAM, already uploaded to VRAM + + pause = false; // Pause camera orbital rotation (and zoom) + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KeyboardKey.P)) + { + pause = !pause; + } + + if (!pause) + { + UpdateCamera(ref camera, CameraMode.Orbital); + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + DrawModel(model, mapPosition, 1.0f, Color.White); + + EndMode3D(); + + Vector2 position = new(screenWidth - cubicmap.Width * 4 - 20, 20); + DrawTextureEx(cubicmap, position, 0.0f, 4.0f, Color.White); + DrawRectangleLines( + screenWidth - cubicmap.Width * 4 - 20, + 20, + cubicmap.Width * 4, + cubicmap.Height * 4, + Color.Green + ); + + DrawText("cubicmap image used to", 658, 90, 10, Color.Gray); + DrawText("generate map 3d model", 658, 104, 10, Color.Gray); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(cubicmap); // Unload cubicmap texture + UnloadTexture(texture); // Unload map texture + UnloadModel(model); // Unload map model + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [models] example - cubicmap rendering"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new CubicmapDemo(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Orbital); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawModel(model, mapPosition, 1.0f, Color.White); - - EndMode3D(); - - Vector2 position = new(screenWidth - cubicmap.Width * 4 - 20, 20); - DrawTextureEx(cubicmap, position, 0.0f, 4.0f, Color.White); - DrawRectangleLines( - screenWidth - cubicmap.Width * 4 - 20, - 20, - cubicmap.Width * 4, - cubicmap.Height * 4, - Color.Green - ); - - DrawText("cubicmap image used to", 658, 90, 10, Color.Gray); - DrawText("generate map 3d model", 658, 104, 10, Color.Gray); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(cubicmap); - UnloadTexture(texture); - UnloadModel(model); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } } - diff --git a/Examples/Models/DynamicMesh.cs b/Examples/Models/DynamicMesh.cs index fff2c5e..e9dae9f 100644 --- a/Examples/Models/DynamicMesh.cs +++ b/Examples/Models/DynamicMesh.cs @@ -3,19 +3,28 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class DynamicMesh +public partial class DynamicMesh : IExample { - public unsafe static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + private const int triangleRows = 48; + private const int vertexRows = triangleRows + 1; + + public string Name => "Models / Dynamic Mesh"; + + public string Title => "raylib [models] example - dynamic mesh"; + + private Camera3D camera; + private Mesh dynamicMesh; + private Texture2D texture; + private Color[] pixels; + private Material material; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - dynamic mesh"); - // Define the camera to look into our 3d world - Camera3D camera = new(); + camera = new(); camera.Position = Vector3.One * 1.5f; camera.Target = camera.Position + new Vector3(1f, -0.25f, 1f); camera.Up = Vector3.UnitY; @@ -23,18 +32,14 @@ public class DynamicMesh camera.Projection = CameraProjection.Perspective; // Generate a dynamic mesh using utils to allocate/access mesh attribute data - const int triangleRows = 48; - const int vertexRows = triangleRows + 1; - Mesh dynamicMesh = new(vertexRows * vertexRows, triangleRows * triangleRows * 2); + dynamicMesh = new(vertexRows * vertexRows, triangleRows * triangleRows * 2); dynamicMesh.AllocVertices(); dynamicMesh.AllocTexCoords(); dynamicMesh.AllocIndices(); - Span vertices = dynamicMesh.VerticesAs(); - Span texcoords = dynamicMesh.TexCoordsAs(); - Span indices = dynamicMesh.IndicesAs(); + var indices = dynamicMesh.IndicesAs(); for (int z = 0, i = 0; z < triangleRows; z++) { - for (int x = 0; x < triangleRows; x++, i += 6) + for (var x = 0; x < triangleRows; x++, i += 6) { indices[i + 0] = (ushort)(x + (z * vertexRows)); indices[i + 1] = (ushort)(indices[i] + vertexRows); @@ -47,73 +52,96 @@ public class DynamicMesh UploadMesh(ref dynamicMesh, true); // Allocate the texture - Image image = GenImageColor(triangleRows, triangleRows, Color.Blank); - Texture2D texture = LoadTextureFromImage(image); - Color[] pixels = new Color[texture.Width * texture.Height]; + var image = GenImageColor(triangleRows, triangleRows, Color.Blank); + texture = LoadTextureFromImage(image); + pixels = new Color[texture.Width * texture.Height]; UnloadImage(image); // Load the material - Material material = LoadMaterialDefault(); + material = LoadMaterialDefault(); SetMaterialTexture(ref material, MaterialMapIndex.Diffuse, texture); + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + var time = (float)GetTime(); + Random random = new(42); + + var vertices = dynamicMesh.VerticesAs(); + var texcoords = dynamicMesh.TexCoordsAs(); + + for (int z = 0, i = 0; z < vertexRows; z++) + { + for (var x = 0; x < vertexRows; x++, i++) + { + var noiseX = SmoothNoise(time + random.Next(10000)); + var noiseZ = SmoothNoise(time + random.Next(10000)); + vertices[i].X = x + noiseX - .5f; + vertices[i].Y = (noiseX + noiseZ) / 2; + vertices[i].Z = z + noiseZ - .5f; + texcoords[i].X = (x - noiseZ) / triangleRows; + texcoords[i].Y = (z - noiseX) / triangleRows; + } + } + UpdateMeshBuffer(dynamicMesh, Mesh.VboIdIndexVertices, vertices, 0); + UpdateMeshBuffer(dynamicMesh, Mesh.VboIdIndexTexCoords, texcoords, 0); + + for (int y = 0, i = 0; y < texture.Height; y++) + { + for (var x = 0; x < texture.Width; x++, i++) + { + pixels[i] = new(32, 178, 170, 255); + pixels[i] = ColorBrightness(pixels[i], (SmoothNoise(time + random.Next(10000)) / 8) - (1 / 16f)); + pixels[i] = ColorAlpha(pixels[i], (triangleRows - new Vector2(x, y).Length()) / triangleRows); + } + } + UpdateTexture(texture, pixels); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + DrawMesh(dynamicMesh, material, Matrix4x4.Identity); + EndMode3D(); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadMaterial(material); + // Raylib.UnloadTexture(texture); <- No need to unload the texture. UnloadMaterial(Material) already unloaded it for us + UnloadMesh(dynamicMesh); + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [models] example - dynamic mesh"); SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new DynamicMesh(); + game.Init(); + // Main game loop while (!WindowShouldClose()) { - // Update - //---------------------------------------------------------------------------------- - float time = (float)GetTime(); - Random random = new(42); - - for (int z = 0, i = 0; z < vertexRows; z++) - { - for (int x = 0; x < vertexRows; x++, i++) - { - float noiseX = SmoothNoise(time + random.Next(10000)); - float noiseZ = SmoothNoise(time + random.Next(10000)); - vertices[i].X = x + noiseX - .5f; - vertices[i].Y = (noiseX + noiseZ) / 2; - vertices[i].Z = z + noiseZ - .5f; - texcoords[i].X = (x - noiseZ) / triangleRows; - texcoords[i].Y = (z - noiseX) / triangleRows; - } - } - UpdateMeshBuffer(dynamicMesh, Mesh.VboIdIndexVertices, vertices, 0); - UpdateMeshBuffer(dynamicMesh, Mesh.VboIdIndexTexCoords, texcoords, 0); - - for (int y = 0, i = 0; y < texture.Height; y++) - { - for (int x = 0; x < texture.Width; x++, i++) - { - pixels[i] = new(32, 178, 170, 255); - pixels[i] = ColorBrightness(pixels[i], (SmoothNoise(time + random.Next(10000)) / 8) - (1 / 16f)); - pixels[i] = ColorAlpha(pixels[i], (triangleRows - new Vector2(x, y).Length()) / triangleRows); - } - } - UpdateTexture(texture, pixels); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - DrawMesh(dynamicMesh, material, Matrix4x4.Identity); - EndMode3D(); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadMaterial(material); - // Raylib.UnloadTexture(texture); <- No need to unload the texture. UnloadMaterial(Material) already unloaded it for us - UnloadMesh(dynamicMesh); - CloseWindow(); //-------------------------------------------------------------------------------------- diff --git a/Examples/Models/FirstPersonMaze.cs b/Examples/Models/FirstPersonMaze.cs index 1582b58..ac91600 100644 --- a/Examples/Models/FirstPersonMaze.cs +++ b/Examples/Models/FirstPersonMaze.cs @@ -2,10 +2,14 @@ * * raylib [models] example - first person maze * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2019 Ramon Santamaria (@raysan5) +* Example originally created with raylib 2.5, last time updated with raylib 3.5 +* +* 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) 2019-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,140 +18,163 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class FirstPersonMaze +public unsafe partial class FirstPersonMaze : IExample { - public unsafe static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Models / First Person Maze"; + + public string Title => "raylib [models] example - first person maze"; + + public bool CursorDisabled => true; + + private Camera3D camera; + private Texture2D cubicmap; + private Texture2D texture; + private Model model; + private Color* mapPixels; + private Vector3 mapPosition; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - first person maze"); - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(0.2f, 0.4f, 0.2f); - camera.Target = new Vector3(0.0f, 0.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; + camera = new(); + camera.Position = new Vector3(0.2f, 0.4f, 0.2f); // Camera position + camera.Target = new Vector3(0.185f, 0.4f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type - Image imMap = LoadImage("resources/cubicmap.png"); - Texture2D cubicmap = LoadTextureFromImage(imMap); - Mesh mesh = GenMeshCubicmap(imMap, new Vector3(1.0f, 1.0f, 1.0f)); - Model model = LoadModelFromMesh(mesh); + var imMap = LoadImage("resources/cubicmap.png"); // Load cubicmap image (RAM) + cubicmap = LoadTextureFromImage(imMap); // Convert image to texture to display (VRAM) + var mesh = GenMeshCubicmap(imMap, new Vector3(1.0f, 1.0f, 1.0f)); + model = LoadModelFromMesh(mesh); // NOTE: By default each cube is mapped to one part of texture atlas - Texture2D texture = LoadTexture("resources/cubicmap_atlas.png"); + texture = LoadTexture("resources/cubicmap_atlas.png"); // Load map texture // Set map diffuse texture Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture); // Get map image data to be used for collision detection - Color* mapPixels = LoadImageColors(imMap); - UnloadImage(imMap); + mapPixels = LoadImageColors(imMap); + UnloadImage(imMap); // Unload image from RAM - Vector3 mapPosition = new(-16.0f, 0.0f, -8.0f); - Vector3 playerPosition = camera.Position; + mapPosition = new(-16.0f, 0.0f, -8.0f); // Set model position + } - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + var oldCamPos = camera.Position; // Store old camera position - // Main game loop - while (!WindowShouldClose()) + UpdateCamera(ref camera, CameraMode.FirstPerson); + + // Check player collision (we simplify to 2D collision detection) + Vector2 playerPos = new(camera.Position.X, camera.Position.Z); + var playerRadius = 0.1f; // Collision radius (player is modelled as a cilinder for collision) + + var playerCellX = (int)(playerPos.X - mapPosition.X + 0.5f); + var playerCellY = (int)(playerPos.Y - mapPosition.Z + 0.5f); + + // Out-of-limits security check + if (playerCellX < 0) { - // Update - //---------------------------------------------------------------------------------- - Vector3 oldCamPos = camera.Position; + playerCellX = 0; + } + else if (playerCellX >= cubicmap.Width) + { + playerCellX = cubicmap.Width - 1; + } - UpdateCamera(ref camera, CameraMode.FirstPerson); + if (playerCellY < 0) + { + playerCellY = 0; + } + else if (playerCellY >= cubicmap.Height) + { + playerCellY = cubicmap.Height - 1; + } - // Check player collision (we simplify to 2D collision detection) - Vector2 playerPos = new(camera.Position.X, camera.Position.Z); - - // Collision radius (player is modelled as a cilinder for collision) - float playerRadius = 0.1f; - - int playerCellX = (int)(playerPos.X - mapPosition.X + 0.5f); - int playerCellY = (int)(playerPos.Y - mapPosition.Z + 0.5f); - - // Out-of-limits security check - if (playerCellX < 0) + // Check map collisions using image data and player position against surrounding cells only + for (var y = playerCellY - 1; y <= playerCellY + 1; y++) + { + // Avoid map accessing out of bounds + if ((y >= 0) && (y < cubicmap.Height)) { - playerCellX = 0; - } - else if (playerCellX >= cubicmap.Width) - { - playerCellX = cubicmap.Width - 1; - } - - if (playerCellY < 0) - { - playerCellY = 0; - } - else if (playerCellY >= cubicmap.Height) - { - playerCellY = cubicmap.Height - 1; - } - - // Check map collisions using image data and player position - // TODO: Improvement: Just check player surrounding cells for collision - for (int y = 0; y < cubicmap.Height; y++) - { - for (int x = 0; x < cubicmap.Width; x++) + for (var x = playerCellX - 1; x <= playerCellX + 1; x++) { - Color* mapPixelsData = mapPixels; - - // Collision: Color.white pixel, only check R channel - Rectangle rec = new( - mapPosition.X - 0.5f + x * 1.0f, - mapPosition.Z - 0.5f + y * 1.0f, - 1.0f, - 1.0f - ); - - bool collision = CheckCollisionCircleRec(playerPos, playerRadius, rec); - if ((mapPixelsData[y * cubicmap.Width + x].R == 255) && collision) + // NOTE: Collision: Only checking R channel for white pixel + if (((x >= 0) && (x < cubicmap.Width)) && + (mapPixels[y * cubicmap.Width + x].R == 255) && + (CheckCollisionCircleRec(playerPos, playerRadius, + new Rectangle(mapPosition.X - 0.5f + x * 1.0f, mapPosition.Z - 0.5f + y * 1.0f, 1.0f, 1.0f)))) { // Collision detected, reset camera position camera.Position = oldCamPos; } } } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // Draw maze map - BeginMode3D(camera); - DrawModel(model, mapPosition, 1.0f, Color.White); - EndMode3D(); - - DrawTextureEx(cubicmap, new Vector2(GetScreenWidth() - cubicmap.Width * 4 - 20, 20), 0.0f, 4.0f, Color.White); - DrawRectangleLines(GetScreenWidth() - cubicmap.Width * 4 - 20, 20, cubicmap.Width * 4, cubicmap.Height * 4, Color.Green); - - // Draw player position radar - DrawRectangle(GetScreenWidth() - cubicmap.Width * 4 - 20 + playerCellX * 4, 20 + playerCellY * 4, 4, 4, Color.Red); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + DrawModel(model, mapPosition, 1.0f, Color.White); // Draw maze map + EndMode3D(); + + DrawTextureEx(cubicmap, new Vector2(GetScreenWidth() - cubicmap.Width * 4 - 20, 20), 0.0f, 4.0f, Color.White); + DrawRectangleLines(GetScreenWidth() - cubicmap.Width * 4 - 20, 20, cubicmap.Width * 4, cubicmap.Height * 4, Color.Green); + + // Draw player position radar + DrawRectangle(GetScreenWidth() - cubicmap.Width * 4 - 20 + playerCellX * 4, 20 + playerCellY * 4, 4, 4, Color.Red); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadImageColors(mapPixels); // Unload color array + + UnloadTexture(cubicmap); // Unload cubicmap texture + UnloadTexture(texture); // Unload map texture + UnloadModel(model); // Unload map model + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [models] example - first person maze"); + + DisableCursor(); // Limit cursor to relative movement inside the window + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + var game = new FirstPersonMaze(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + game.Update(); + } + + game.Unload(); // De-Initialization //-------------------------------------------------------------------------------------- - UnloadImageColors(mapPixels); - - UnloadTexture(cubicmap); - UnloadTexture(texture); - UnloadModel(model); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Models/GeometricShapes.cs b/Examples/Models/GeometricShapes.cs index 3a59c59..465264c 100644 --- a/Examples/Models/GeometricShapes.cs +++ b/Examples/Models/GeometricShapes.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [models] example - Draw some basic geometric shapes (cube, sphere, cylinder...) +* raylib [models] example - geometric shapes * -* This example has been created using raylib 1.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 * -* Copyright (c) 2014 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.0, last time updated with raylib 3.5 +* +* 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,70 +18,91 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class GeometricShapes +public partial class GeometricShapes : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Models / Geometric Shapes"; + + public string Title => "raylib [models] example - geometric shapes"; + + private Camera3D camera; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - geometric shapes"); - // Define the camera to look into our 3d world - Camera3D camera = new(); + camera = new(); camera.Position = new Vector3(0.0f, 10.0f, 10.0f); camera.Target = new Vector3(0.0f, 0.0f, 0.0f); camera.Up = new Vector3(0.0f, 1.0f, 0.0f); camera.FovY = 45.0f; camera.Projection = CameraProjection.Perspective; + } - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + public void Update() + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + DrawCube(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Red); + DrawCubeWires(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Gold); + DrawCubeWires(new Vector3(-4.0f, 0.0f, -2.0f), 3.0f, 6.0f, 2.0f, Color.Maroon); + + DrawSphere(new Vector3(-1.0f, 0.0f, -2.0f), 1.0f, Color.Green); + DrawSphereWires(new Vector3(1.0f, 0.0f, 2.0f), 2.0f, 16, 16, Color.Lime); + + DrawCylinder(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.SkyBlue); + DrawCylinderWires(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.DarkBlue); + DrawCylinderWires(new Vector3(4.5f, -1.0f, 2.0f), 1.0f, 1.0f, 2.0f, 6, Color.Brown); + + DrawCylinder(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Gold); + DrawCylinderWires(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Pink); + + DrawCapsule(new Vector3(-3.0f, 1.5f, -4.0f), new Vector3(-4.0f, -1.0f, -4.0f), 1.2f, 8, 8, Color.Violet); + DrawCapsuleWires(new Vector3(-3.0f, 1.5f, -4.0f), new Vector3(-4.0f, -1.0f, -4.0f), 1.2f, 8, 8, Color.Purple); + + DrawGrid(10, 1.0f); // Draw a grid + + EndMode3D(); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [models] example - geometric shapes"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new GeometricShapes(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update your variables here - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawCube(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Red); - DrawCubeWires(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Gold); - DrawCubeWires(new Vector3(-4.0f, 0.0f, -2.0f), 3.0f, 6.0f, 2.0f, Color.Maroon); - - DrawSphere(new Vector3(-1.0f, 0.0f, -2.0f), 1.0f, Color.Green); - DrawSphereWires(new Vector3(1.0f, 0.0f, 2.0f), 2.0f, 16, 16, Color.Lime); - - DrawCylinder(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.SkyBlue); - DrawCylinderWires(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.DarkBlue); - DrawCylinderWires(new Vector3(4.5f, -1.0f, 2.0f), 1.0f, 1.0f, 2.0f, 6, Color.Brown); - - DrawCylinder(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Gold); - DrawCylinderWires(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Pink); - - DrawGrid(10, 1.0f); - - EndMode3D(); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Models/HeightmapDemo.cs b/Examples/Models/HeightmapDemo.cs index d0471f5..74efd36 100644 --- a/Examples/Models/HeightmapDemo.cs +++ b/Examples/Models/HeightmapDemo.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [models] example - Heightmap loading and drawing +* raylib [models] example - heightmap rendering * -* This example has been created using raylib 1.8 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 * -* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.8, last time updated with raylib 3.5 +* +* 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,77 +18,102 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class HeightmapDemo +public partial class HeightmapDemo : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Models / Heightmap Demo"; + + public string Title => "raylib [models] example - heightmap rendering"; + + private Camera3D camera; + private Texture2D texture; + private Model model; + private Vector3 mapPosition; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - heightmap loading and drawing"); - // Define our custom camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(18.0f, 16.0f, 18.0f); - camera.Target = new Vector3(0.0f, 0.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; + camera = new(); + camera.Position = new Vector3(18.0f, 21.0f, 18.0f); // Camera position + camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type - Image image = LoadImage("resources/heightmap.png"); - Texture2D texture = LoadTextureFromImage(image); + var image = LoadImage("resources/heightmap.png"); // Load heightmap image (RAM) + texture = LoadTextureFromImage(image); // Convert image to texture (VRAM) - Mesh mesh = GenMeshHeightmap(image, new Vector3(16, 8, 16)); - Model model = LoadModelFromMesh(mesh); + var mesh = GenMeshHeightmap(image, new Vector3(16, 8, 16)); // Generate heightmap mesh (RAM and VRAM) + model = LoadModelFromMesh(mesh); // Load model from generated mesh // Set map diffuse texture Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture); - Vector3 mapPosition = new(-8.0f, 0.0f, -8.0f); + mapPosition = new(-8.0f, 0.0f, -8.0f); // Define model position - UnloadImage(image); + UnloadImage(image); // Unload heightmap image from RAM, already uploaded to VRAM + } - SetTargetFPS(60); + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Orbital); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + DrawModel(model, mapPosition, 1.0f, Color.Red); + + DrawGrid(20, 1.0f); + + EndMode3D(); + + DrawTexture(texture, screenWidth - texture.Width - 20, 20, Color.White); + DrawRectangleLines(screenWidth - texture.Width - 20, 20, texture.Width, texture.Height, Color.Green); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texture); // Unload texture + UnloadModel(model); // Unload model + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [models] example - heightmap rendering"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new HeightmapDemo(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Orbital); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawModel(model, mapPosition, 1.0f, Color.Red); - - DrawGrid(20, 1.0f); - - EndMode3D(); - - DrawTexture(texture, screenWidth - texture.Width - 20, 20, Color.White); - DrawRectangleLines(screenWidth - texture.Width - 20, 20, texture.Width, texture.Height, Color.Green); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texture); - UnloadModel(model); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Models/LoadingGltf.cs b/Examples/Models/LoadingGltf.cs index 609bf41..865998a 100644 --- a/Examples/Models/LoadingGltf.cs +++ b/Examples/Models/LoadingGltf.cs @@ -25,93 +25,122 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class LoadingGltf +public partial class LoadingGltf : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Models / Loading GLTF"; + + public string Title => "raylib [models] example - loading gltf"; + + private Camera3D camera; + private Model model; + private Vector3 position; + private unsafe ModelAnimation* anims; + private int animCount; + private int animIndex; + private float animCurrentFrame; + + public unsafe void Init() + { + // Define the camera to look into our 3d world + camera = new(); + camera.Position = new Vector3(6.0f, 6.0f, 6.0f); // Camera position + camera.Target = new Vector3(0.0f, 2.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type + + // Load model + model = LoadModel("resources/models/gltf/robot.glb"); + position = new(0.0f, 0.0f, 0.0f); // Set model world position + + // Load model animations + anims = LoadModelAnimations("resources/models/gltf/robot.glb", ref animCount); + + // Animation playing variables + animIndex = 0; // Current animation playing + animCurrentFrame = 0.0f; // Current animation frame + } + + public unsafe void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Orbital); + + // Select current animation + if (IsKeyPressed(KeyboardKey.Right)) + { + animIndex = (animIndex + 1) % animCount; + } + else if (IsKeyPressed(KeyboardKey.Left)) + { + animIndex = (animIndex + animCount - 1) % animCount; + } + + + // Update model animation + animCurrentFrame = (animCurrentFrame + 1) % anims[animIndex].KeyFrameCount; + UpdateModelAnimation(model, anims[animIndex], (float)animCurrentFrame); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + DrawModel( + model, + position, + 1f, + Color.White + ); + + DrawGrid(10, 1.0f); + + EndMode3D(); + DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 40, 20, Color.Maroon); + DrawText("Use the LEFT/RIGHT keys to switch animation", 10, 10, 20, Color.Gray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public unsafe void Unload() + { + UnloadModelAnimations(anims, animCount); // Unload model animations data + UnloadModel(model); // Unload model + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [models] example - loading gltf"); - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(6.0f, 6.0f, 6.0f); - camera.Target = new Vector3(0.0f, 2.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; - - Model model = LoadModel("resources/models/gltf/robot.glb"); - Vector3 position = new(0.0f, 0.0f, 0.0f); - - // Load animation data - var anims = LoadModelAnimations("resources/models/gltf/robot.glb"); - - // Animation playing variables - int animIndex = 0; - float animCurrentFrame = 0.0f; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new LoadingGltf(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Orbital); - - if (IsKeyPressed(KeyboardKey.Right)) - { - animIndex = (animIndex + 1) % anims.Length; - } - else if (IsKeyPressed(KeyboardKey.Left)) - { - animIndex = (animIndex + anims.Length - 1) % anims.Length; - } - - - // Update model animation - animCurrentFrame = (animCurrentFrame + 1) % anims[animIndex].KeyFrameCount; - UpdateModelAnimation(model, anims[animIndex], (float)animCurrentFrame); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawModel( - model, - position, - 1f, - Color.White - ); - - DrawGrid(10, 1.0f); - - EndMode3D(); - DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 40, 20, Color.Maroon); - DrawText("Use the LEFT/RIGHT keys to switch animation", 10, 10, 20, Color.Gray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadModelAnimations(anims); - UnloadModel(model); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } } - diff --git a/Examples/Models/LoadingIqm.cs b/Examples/Models/LoadingIqm.cs index 6905544..7ccfc12 100644 --- a/Examples/Models/LoadingIqm.cs +++ b/Examples/Models/LoadingIqm.cs @@ -25,93 +25,121 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class LoadingIqm +public partial class LoadingIqm : IExample { - public unsafe static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Models / Loading IQM"; + + public string Title => "raylib [models] example - loading iqm"; + + private Camera3D camera; + private Model model; + private Texture2D texture; + private Vector3 position; + private unsafe ModelAnimation* anims; + private int animCount; + private int animIndex; + private float animCurrentFrame; + + public unsafe void Init() + { + // Define the camera to look into our 3d world + camera = new(); + camera.Position = new Vector3(10.0f, 10.0f, 10.0f); // Camera position + camera.Target = new Vector3(0.0f, 4.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera mode type + + model = LoadModel("resources/models/iqm/guy.iqm"); // Load the animated model mesh and basic data + texture = LoadTexture("resources/models/iqm/guytex.png"); // Load model texture and set material + Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Diffuse, ref texture); // Set model material map texture + position = new(0.0f, 0.0f, 0.0f); // Set model position + + // Load animation data + anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", ref animCount); + + // Animation playing variables + animIndex = 0; // Current animation playing + animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames) + } + + public unsafe void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Orbital); + + // Play animation when spacebar is held down + animCurrentFrame += 1.0f; + UpdateModelAnimation(model, anims[0], animCurrentFrame); + + if (animCurrentFrame >= anims[0].KeyFrameCount) + { + animCurrentFrame = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + DrawModelEx( + model, + position, + new Vector3(1.0f, 0.0f, 0.0f), + -90.0f, + new Vector3(1.0f, 1.0f, 1.0f), + Color.White + ); + + DrawGrid(10, 1.0f); + + EndMode3D(); + DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 10, 20, Color.Maroon); + DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, Color.Gray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public unsafe void Unload() + { + UnloadTexture(texture); // Unload texture + UnloadModelAnimations(anims, animCount); // Unload model animations data + UnloadModel(model); // Unload model + } + + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [models] example - loading iqm"); - InitWindow(screenWidth, screenHeight, "raylib [models] example - model animation"); - - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(10.0f, 10.0f, 10.0f); - camera.Target = new Vector3(0.0f, 4.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; - - Model model = LoadModel("resources/models/iqm/guy.iqm"); - Texture2D texture = LoadTexture("resources/models/iqm/guytex.png"); - Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Diffuse, ref texture); - Vector3 position = new(0.0f, 0.0f, 0.0f); - - // Load animation data - var anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm"); - - // Animation playing variables - int animIndex = 0; - float animCurrentFrame = 0.0f; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new LoadingIqm(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Orbital); - - // Play animation when spacebar is held down - animCurrentFrame += 1.0f; - UpdateModelAnimation(model, anims[0], animCurrentFrame); - - if (animCurrentFrame >= anims[0].KeyFrameCount) - { - animCurrentFrame = 0; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawModelEx( - model, - position, - new Vector3(1.0f, 0.0f, 0.0f), - -90.0f, - new Vector3(1.0f, 1.0f, 1.0f), - Color.White - ); - - DrawGrid(10, 1.0f); - - EndMode3D(); - DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 10, 20, Color.Maroon); - DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, Color.Gray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texture); - UnloadModelAnimations(anims); - UnloadModel(model); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } } - diff --git a/Examples/Models/MeshDemo.cs b/Examples/Models/MeshDemo.cs index 9f2be23..98ef2f2 100644 --- a/Examples/Models/MeshDemo.cs +++ b/Examples/Models/MeshDemo.cs @@ -3,19 +3,24 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class MeshDemo +public partial class MeshDemo : IExample { - public unsafe static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Models / Mesh Demo"; + + public string Title => "raylib [models] example - mesh demo"; + + private Camera3D camera; + private Model model; + private Texture2D texture; + private float rotationAngle; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh demo"); - // Define the camera to look into our 3d world - Camera3D camera = new(); + camera = new(); camera.Position = Vector3.One * 1.5f; camera.Target = Vector3.Zero; camera.Up = Vector3.UnitY; @@ -28,10 +33,10 @@ public class MeshDemo tetrahedron.AllocTexCoords(); tetrahedron.AllocColors(); tetrahedron.AllocIndices(); - Span vertices = tetrahedron.VerticesAs(); - Span texcoords = tetrahedron.TexCoordsAs(); - Span colors = tetrahedron.ColorsAs(); - Span indices = tetrahedron.IndicesAs(); + var vertices = tetrahedron.VerticesAs(); + var texcoords = tetrahedron.TexCoordsAs(); + var colors = tetrahedron.ColorsAs(); + var indices = tetrahedron.IndicesAs(); // Coordinates for a regular tetrahedron vertices[0] = new(MathF.Sqrt(8f / 9f), 0f, -1f / 3f); @@ -65,49 +70,69 @@ public class MeshDemo indices[10] = 3; indices[11] = 2; - float rotationAngle = 0f; + rotationAngle = 0f; Raylib.UploadMesh(ref tetrahedron, false); - Model model = Raylib.LoadModelFromMesh(tetrahedron); + model = Raylib.LoadModelFromMesh(tetrahedron); - Image image = Raylib.GenImagePerlinNoise(16, 16, 0, 0, 1000f); + var image = Raylib.GenImagePerlinNoise(16, 16, 0, 0, 1000f); Raylib.ImageBlurGaussian(ref image, 2); Raylib.ImageColorBrightness(ref image, 100); Raylib.ImageDither(ref image, 4, 4, 4, 4); - Texture2D texture = Raylib.LoadTextureFromImage(image); + texture = Raylib.LoadTextureFromImage(image); Raylib.UnloadImage(image); Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Diffuse, ref texture); + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Free); + rotationAngle = Raymath.Wrap(rotationAngle += 1f, 0f, 360f); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + Raylib.DrawModelEx(model, Vector3.Zero, Vector3.UnitX, rotationAngle, Vector3.One, Color.White); + EndMode3D(); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texture); + UnloadModel(model); + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh demo"); SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new MeshDemo(); + game.Init(); + // Main game loop while (!WindowShouldClose()) { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Free); - rotationAngle = Raymath.Wrap(rotationAngle += 1f, 0f, 360f); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - Raylib.DrawModelEx(model, Vector3.Zero, Vector3.UnitX, rotationAngle, Vector3.One, Color.White); - EndMode3D(); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texture); - UnloadModel(model); - CloseWindow(); //-------------------------------------------------------------------------------------- diff --git a/Examples/Models/MeshGeneration.cs b/Examples/Models/MeshGeneration.cs index e4b27c4..4415c90 100644 --- a/Examples/Models/MeshGeneration.cs +++ b/Examples/Models/MeshGeneration.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib example - procedural mesh generation +* raylib [models] example - mesh generation * -* This example has been created using raylib 1.8 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2017 Ramon Santamaria (Ray San) +* Example originally created with raylib 1.8, last time updated with raylib 4.0 +* +* 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) 2017-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,25 +18,31 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class MeshGeneration +public partial class MeshGeneration : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Models / Mesh Generation"; + + public string Title => "raylib [models] example - mesh generation"; + + private Texture2D texture; + private Model[] models; + private Camera3D camera; + private Vector3 position; + private int currentModel; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh generation"); - - // We generate a isChecked image for texturing - Image isChecked = GenImageChecked(2, 2, 1, 1, Color.Red, Color.Green); - Texture2D texture = LoadTextureFromImage(isChecked); + // We generate a checked image for texturing + var isChecked = GenImageChecked(2, 2, 1, 1, Color.Red, Color.Green); + texture = LoadTextureFromImage(isChecked); UnloadImage(isChecked); - Model[] models = new Model[9]; + models = new Model[9]; - models[0] = LoadModelFromMesh(GenMeshPlane(2, 2, 5, 5)); + models[0] = LoadModelFromMesh(GenMeshPlane(2, 2, 4, 3)); models[1] = LoadModelFromMesh(GenMeshCube(2.0f, 1.0f, 2.0f)); models[2] = LoadModelFromMesh(GenMeshSphere(2, 32, 32)); models[3] = LoadModelFromMesh(GenMeshHemiSphere(2, 16, 16)); @@ -42,15 +52,17 @@ public class MeshGeneration models[7] = LoadModelFromMesh(GenMeshPoly(5, 2.0f)); models[8] = LoadModelFromMesh(GenMeshCustom()); - // Set isChecked texture as default diffuse component for all models material - for (int i = 0; i < models.Length; i++) + // NOTE: Generated meshes could be exported using ExportMesh() + + // Set checked texture as default diffuse component for all models material + for (var i = 0; i < models.Length; i++) { // Set map diffuse texture Raylib.SetMaterialTexture(ref models[i], 0, MaterialMapIndex.Albedo, ref texture); } // Define the camera to look into our 3d world - Camera3D camera = new(); + camera = new(); camera.Position = new Vector3(5.0f, 5.0f, 5.0f); camera.Target = new Vector3(0.0f, 0.0f, 0.0f); camera.Up = new Vector3(0.0f, 1.0f, 0.0f); @@ -58,92 +70,102 @@ public class MeshGeneration camera.Projection = CameraProjection.Perspective; // Model drawing position - Vector3 position = new(0.0f, 0.0f, 0.0f); + position = new(0.0f, 0.0f, 0.0f); - int currentModel = 0; + currentModel = 0; + } - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Orbital); - // Main game loop - while (!WindowShouldClose()) + if (IsMouseButtonPressed(MouseButton.Left)) { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Orbital); - - if (IsMouseButtonPressed(MouseButton.Left)) - { - // Cycle between the textures - currentModel = (currentModel + 1) % models.Length; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawModel(models[currentModel], position, 1.0f, Color.White); - - DrawGrid(10, 1.0f); - - EndMode3D(); - - DrawRectangle(30, 400, 310, 30, ColorAlpha(Color.SkyBlue, 0.5f)); - DrawRectangleLines(30, 400, 310, 30, ColorAlpha(Color.DarkBlue, 0.5f)); - DrawText("MOUSE LEFT BUTTON to CYCLE PROCEDURAL MODELS", 40, 410, 10, Color.Blue); - - switch (currentModel) - { - case 0: - DrawText("PLANE", 680, 10, 20, Color.DarkBlue); - break; - case 1: - DrawText("CUBE", 680, 10, 20, Color.DarkBlue); - break; - case 2: - DrawText("SPHERE", 680, 10, 20, Color.DarkBlue); - break; - case 3: - DrawText("HEMISPHERE", 640, 10, 20, Color.DarkBlue); - break; - case 4: - DrawText("CYLINDER", 680, 10, 20, Color.DarkBlue); - break; - case 5: - DrawText("TORUS", 680, 10, 20, Color.DarkBlue); - break; - case 6: - DrawText("KNOT", 680, 10, 20, Color.DarkBlue); - break; - case 7: - DrawText("POLY", 680, 10, 20, Color.DarkBlue); - break; - case 8: - DrawText("Custom (triagnle)", 580, 10, 20, Color.DarkBlue); - break; - default: - break; - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + currentModel = (currentModel + 1) % models.Length; // Cycle between the textures } - // De-Initialization - //-------------------------------------------------------------------------------------- - for (int i = 0; i < models.Length; i++) + if (IsKeyPressed(KeyboardKey.Right)) + { + currentModel++; + if (currentModel >= models.Length) + { + currentModel = 0; + } + } + else if (IsKeyPressed(KeyboardKey.Left)) + { + currentModel--; + if (currentModel < 0) + { + currentModel = models.Length - 1; + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + DrawModel(models[currentModel], position, 1.0f, Color.White); + DrawGrid(10, 1.0f); + + EndMode3D(); + + DrawRectangle(30, 400, 310, 30, Fade(Color.SkyBlue, 0.5f)); + DrawRectangleLines(30, 400, 310, 30, Fade(Color.DarkBlue, 0.5f)); + DrawText("MOUSE LEFT BUTTON to CYCLE PROCEDURAL MODELS", 40, 410, 10, Color.Blue); + + switch (currentModel) + { + case 0: + DrawText("PLANE", 680, 10, 20, Color.DarkBlue); + break; + case 1: + DrawText("CUBE", 680, 10, 20, Color.DarkBlue); + break; + case 2: + DrawText("SPHERE", 680, 10, 20, Color.DarkBlue); + break; + case 3: + DrawText("HEMISPHERE", 640, 10, 20, Color.DarkBlue); + break; + case 4: + DrawText("CYLINDER", 680, 10, 20, Color.DarkBlue); + break; + case 5: + DrawText("TORUS", 680, 10, 20, Color.DarkBlue); + break; + case 6: + DrawText("KNOT", 680, 10, 20, Color.DarkBlue); + break; + case 7: + DrawText("POLY", 680, 10, 20, Color.DarkBlue); + break; + case 8: + DrawText("Custom (triangle)", 580, 10, 20, Color.DarkBlue); + break; + default: + break; + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texture); // Unload texture + + // Unload models data (GPU VRAM) + for (var i = 0; i < models.Length; i++) { UnloadModel(models[i]); } - - CloseWindow(); - //-------------------------------------------------------------------------------------- - - return 0; } // Generate a simple triangle mesh from code @@ -153,9 +175,9 @@ public class MeshGeneration mesh.AllocVertices(); mesh.AllocTexCoords(); mesh.AllocNormals(); - Span vertices = mesh.VerticesAs(); - Span texcoords = mesh.TexCoordsAs(); - Span normals = mesh.NormalsAs(); + var vertices = mesh.VerticesAs(); + var texcoords = mesh.TexCoordsAs(); + var normals = mesh.NormalsAs(); // Vertex at (0, 0, 0) vertices[0] = new(0, 0, 0); @@ -177,4 +199,32 @@ public class MeshGeneration return mesh; } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh generation"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + var game = new MeshGeneration(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + game.Update(); + } + + game.Unload(); + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; + } } diff --git a/Examples/Models/MeshPicking.cs b/Examples/Models/MeshPicking.cs index e2dbceb..7aaa549 100644 --- a/Examples/Models/MeshPicking.cs +++ b/Examples/Models/MeshPicking.cs @@ -1,12 +1,17 @@ /******************************************************************************************* * -* raylib [models] example - Mesh picking in 3d mode, ground plane, triangle, mesh +* raylib [models] example - mesh picking * -* This example has been created using raylib 1.7 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 * -* Copyright (c) 2015 Ramon Santamaria (@raysan5) -* Example contributed by Joel Davis (@joeld42) +* Example originally created with raylib 1.7, last time updated with raylib 4.0 +* +* Example contributed by Joel Davis (@joeld42) 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) 2017-2025 Joel Davis (@joeld42) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -16,19 +21,38 @@ using static Raylib_cs.Raymath; namespace Examples.Models; -public class MeshPicking +public partial class MeshPicking : IExample { - public unsafe static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Models / Mesh Picking"; + + public string Title => "raylib [models] example - mesh picking"; + + public bool CursorDisabled => true; + + private Camera3D camera; + private Ray ray; + private Model tower; + private Texture2D texture; + private Vector3 towerPos; + private BoundingBox towerBBox; + private Vector3 g0; + private Vector3 g1; + private Vector3 g2; + private Vector3 g3; + private Vector3 ta; + private Vector3 tb; + private Vector3 tc; + private Vector3 bary; + private Vector3 sp; + private float sr; + + public unsafe void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh picking"); - // Define the camera to look into our 3d world - Camera3D camera; + camera = new(); camera.Position = new Vector3(20.0f, 20.0f, 20.0f); camera.Target = new Vector3(0.0f, 8.0f, 0.0f); camera.Up = new Vector3(0.0f, 1.6f, 0.0f); @@ -36,210 +60,232 @@ public class MeshPicking camera.Projection = CameraProjection.Perspective; // Picking ray - Ray ray = new(); + ray = new(); - Model tower = LoadModel("resources/models/obj/turret.obj"); - Texture2D texture = LoadTexture("resources/models/obj/turret_diffuse.png"); + tower = LoadModel("resources/models/obj/turret.obj"); + texture = LoadTexture("resources/models/obj/turret_diffuse.png"); Raylib.SetMaterialTexture(ref tower, 0, MaterialMapIndex.Albedo, ref texture); - Vector3 towerPos = new(0.0f, 0.0f, 0.0f); - BoundingBox towerBBox = GetMeshBoundingBox(tower.Meshes[0]); + towerPos = new(0.0f, 0.0f, 0.0f); + towerBBox = GetMeshBoundingBox(tower.Meshes[0]); // Ground quad - Vector3 g0 = new(-50.0f, 0.0f, -50.0f); - Vector3 g1 = new(-50.0f, 0.0f, 50.0f); - Vector3 g2 = new(50.0f, 0.0f, 50.0f); - Vector3 g3 = new(50.0f, 0.0f, -50.0f); + g0 = new(-50.0f, 0.0f, -50.0f); + g1 = new(-50.0f, 0.0f, 50.0f); + g2 = new(50.0f, 0.0f, 50.0f); + g3 = new(50.0f, 0.0f, -50.0f); // Test triangle - Vector3 ta = new(-25.0f, 0.5f, 0.0f); - Vector3 tb = new(-4.0f, 2.5f, 1.0f); - Vector3 tc = new(-8.0f, 6.5f, 0.0f); + ta = new(-25.0f, 0.5f, 0.0f); + tb = new(-4.0f, 2.5f, 1.0f); + tc = new(-8.0f, 6.5f, 0.0f); - Vector3 bary = new(0.0f, 0.0f, 0.0f); + bary = new(0.0f, 0.0f, 0.0f); // Test sphere - Vector3 sp = new(-30.0f, 5.0f, 5.0f); - float sr = 4.0f; + sp = new(-30.0f, 5.0f, 5.0f); + sr = 4.0f; + } + + public unsafe void Update() + { + //---------------------------------------------------------------------------------- + // Update + //---------------------------------------------------------------------------------- + if (IsCursorHidden()) + { + UpdateCamera(ref camera, CameraMode.FirstPerson); + } + + // Toggle camera controls + if (IsMouseButtonPressed(MouseButton.Right)) + { + if (IsCursorHidden()) + { + EnableCursor(); + } + else + { + DisableCursor(); + } + } + + // Display information about closest hit + RayCollision collision = new(); + var hitObjectName = "None"; + collision.Distance = float.MaxValue; + collision.Hit = false; + var cursorColor = Color.White; + + // Get ray and test against objects + ray = GetScreenToWorldRay(GetMousePosition(), camera); + + // Check ray collision against ground quad + var groundHitInfo = GetRayCollisionQuad(ray, g0, g1, g2, g3); + if (groundHitInfo.Hit && (groundHitInfo.Distance < collision.Distance)) + { + collision = groundHitInfo; + cursorColor = Color.Green; + hitObjectName = "Ground"; + } + + // Check ray collision against test triangle + var triHitInfo = GetRayCollisionTriangle(ray, ta, tb, tc); + if (triHitInfo.Hit && (triHitInfo.Distance < collision.Distance)) + { + collision = triHitInfo; + cursorColor = Color.Purple; + hitObjectName = "Triangle"; + + bary = Vector3Barycenter(collision.Point, ta, tb, tc); + } + + // Check ray collision against test sphere + var sphereHitInfo = GetRayCollisionSphere(ray, sp, sr); + if ((sphereHitInfo.Hit) && (sphereHitInfo.Distance < collision.Distance)) + { + collision = sphereHitInfo; + cursorColor = Color.Orange; + hitObjectName = "Sphere"; + } + + // Check ray collision against bounding box first, before trying the full ray-mesh test + var boxHitInfo = GetRayCollisionBox(ray, towerBBox); + if (boxHitInfo.Hit && boxHitInfo.Distance < collision.Distance) + { + collision = boxHitInfo; + cursorColor = Color.Orange; + hitObjectName = "Box"; + + // Check ray collision against model meshes + RayCollision meshHitInfo = new(); + for (var m = 0; m < tower.MeshCount; m++) + { + // NOTE: We consider the model.Transform for the collision check but + // it can be checked against any transform matrix, used when checking against same + // model drawn multiple times with multiple transforms + meshHitInfo = GetRayCollisionMesh(ray, tower.Meshes[m], tower.Transform); + if (meshHitInfo.Hit) + { + // Save the closest hit mesh + if ((!collision.Hit) || (collision.Distance > meshHitInfo.Distance)) + { + collision = meshHitInfo; + } + break; + } + } + + if (meshHitInfo.Hit) + { + collision = meshHitInfo; + cursorColor = Color.Orange; + hitObjectName = "Mesh"; + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + // Draw the tower + // WARNING: If scale is different than 1.0f, + // not considered by GetRayCollisionModel() + DrawModel(tower, towerPos, 1.0f, Color.White); + + // Draw the test triangle + DrawLine3D(ta, tb, Color.Purple); + DrawLine3D(tb, tc, Color.Purple); + DrawLine3D(tc, ta, Color.Purple); + + // Draw the test sphere + DrawSphereWires(sp, sr, 8, 8, Color.Purple); + + // Draw the mesh bbox if we hit it + if (boxHitInfo.Hit) + { + DrawBoundingBox(towerBBox, Color.Lime); + } + + // If we hit something, draw the cursor at the hit point + if (collision.Hit) + { + DrawCube(collision.Point, 0.3f, 0.3f, 0.3f, cursorColor); + DrawCubeWires(collision.Point, 0.3f, 0.3f, 0.3f, Color.Red); + + var normalEnd = collision.Point + collision.Normal; + DrawLine3D(collision.Point, normalEnd, Color.Red); + } + + DrawRay(ray, Color.Maroon); + + DrawGrid(10, 10.0f); + + EndMode3D(); + + // Draw some debug GUI text + DrawText($"Hit Object: {hitObjectName}", 10, 50, 10, Color.Black); + + if (collision.Hit) + { + var ypos = 70; + + DrawText($"Distance: {collision.Distance}", 10, ypos, 10, Color.Black); + + DrawText($"Hit Pos: {collision.Point}", 10, ypos + 15, 10, Color.Black); + + DrawText($"Hit Norm: {collision.Normal}", 10, ypos + 30, 10, Color.Black); + + if (triHitInfo.Hit && hitObjectName == "Triangle") + { + DrawText($"Barycenter: {bary}", 10, ypos + 45, 10, Color.Black); + } + } + + DrawText("Right click mouse to toggle camera controls", 10, 430, 10, Color.Gray); + + DrawText("(c) Turret 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, Color.Gray); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadModel(tower); + UnloadTexture(texture); + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh picking"); SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new MeshPicking(); + game.Init(); + //---------------------------------------------------------------------------------- // Main game loop //-------------------------------------------------------------------------------------- while (!WindowShouldClose()) { - //---------------------------------------------------------------------------------- - // Update - //---------------------------------------------------------------------------------- - if (IsCursorHidden()) - { - UpdateCamera(ref camera, CameraMode.FirstPerson); - } - - // Toggle camera controls - if (IsMouseButtonPressed(MouseButton.Right)) - { - if (IsCursorHidden()) - { - EnableCursor(); - } - else - { - DisableCursor(); - } - } - - // Display information about closest hit - RayCollision collision = new(); - string hitObjectName = "None"; - collision.Distance = float.MaxValue; - collision.Hit = false; - Color cursorColor = Color.White; - - // Get ray and test against objects - ray = GetScreenToWorldRay(GetMousePosition(), camera); - - // Check ray collision aginst ground quad - RayCollision groundHitInfo = GetRayCollisionQuad(ray, g0, g1, g2, g3); - if (groundHitInfo.Hit && (groundHitInfo.Distance < collision.Distance)) - { - collision = groundHitInfo; - cursorColor = Color.Green; - hitObjectName = "Ground"; - } - - // Check ray collision against test triangle - RayCollision triHitInfo = GetRayCollisionTriangle(ray, ta, tb, tc); - if (triHitInfo.Hit && (triHitInfo.Distance < collision.Distance)) - { - collision = triHitInfo; - cursorColor = Color.Purple; - hitObjectName = "Triangle"; - - bary = Vector3Barycenter(collision.Point, ta, tb, tc); - } - - // Check ray collision against test sphere - RayCollision sphereHitInfo = GetRayCollisionSphere(ray, sp, sr); - if ((sphereHitInfo.Hit) && (sphereHitInfo.Distance < collision.Distance)) - { - collision = sphereHitInfo; - cursorColor = Color.Orange; - hitObjectName = "Sphere"; - } - - // Check ray collision against bounding box first, before trying the full ray-mesh test - RayCollision boxHitInfo = GetRayCollisionBox(ray, towerBBox); - if (boxHitInfo.Hit && boxHitInfo.Distance < collision.Distance) - { - collision = boxHitInfo; - cursorColor = Color.Orange; - hitObjectName = "Box"; - - // Check ray collision against model meshes - RayCollision meshHitInfo = new(); - for (int m = 0; m < tower.MeshCount; m++) - { - // NOTE: We consider the model.Transform for the collision check but - // it can be checked against any transform matrix, used when checking against same - // model drawn multiple times with multiple transforms - meshHitInfo = GetRayCollisionMesh(ray, tower.Meshes[m], tower.Transform); - if (meshHitInfo.Hit) - { - // Save the closest hit mesh - if ((!collision.Hit) || (collision.Distance > meshHitInfo.Distance)) - { - collision = meshHitInfo; - } - break; - } - } - - if (meshHitInfo.Hit) - { - collision = meshHitInfo; - cursorColor = Color.Orange; - hitObjectName = "Mesh"; - } - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - // Draw the tower - DrawModel(tower, towerPos, 1.0f, Color.White); - - // Draw the test triangle - DrawLine3D(ta, tb, Color.Purple); - DrawLine3D(tb, tc, Color.Purple); - DrawLine3D(tc, ta, Color.Purple); - - // Draw the test sphere - DrawSphereWires(sp, sr, 8, 8, Color.Purple); - - // Draw the mesh bbox if we hit it - if (boxHitInfo.Hit) - { - DrawBoundingBox(towerBBox, Color.Lime); - } - - // If we hit something, draw the cursor at the hit point - if (collision.Hit) - { - DrawCube(collision.Point, 0.3f, 0.3f, 0.3f, cursorColor); - DrawCubeWires(collision.Point, 0.3f, 0.3f, 0.3f, Color.Red); - - Vector3 normalEnd = collision.Point + collision.Normal; - DrawLine3D(collision.Point, normalEnd, Color.Red); - } - - DrawRay(ray, Color.Maroon); - - DrawGrid(10, 10.0f); - - EndMode3D(); - - // Draw some debug GUI text - DrawText($"Hit Object: {hitObjectName}", 10, 50, 10, Color.Black); - - if (collision.Hit) - { - int ypos = 70; - - DrawText($"Distance: {collision.Distance}", 10, ypos, 10, Color.Black); - - DrawText($"Hit Pos: {collision.Point}", 10, ypos + 15, 10, Color.Black); - - DrawText($"Hit Norm: {collision.Normal}", 10, ypos + 30, 10, Color.Black); - - if (triHitInfo.Hit) - { - DrawText($"Barycenter: {bary}", 10, ypos + 45, 10, Color.Black); - } - } - - DrawText("Right click mouse to toggle camera controls", 10, 430, 10, Color.Gray); - - DrawText("(c) Turret 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, Color.Gray); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadModel(tower); - UnloadTexture(texture); - CloseWindow(); //-------------------------------------------------------------------------------------- diff --git a/Examples/Models/ModelCubeTexture.cs b/Examples/Models/ModelCubeTexture.cs index 7af9219..c2c702f 100644 --- a/Examples/Models/ModelCubeTexture.cs +++ b/Examples/Models/ModelCubeTexture.cs @@ -1,13 +1,15 @@ /******************************************************************************************* * -* raylib [models] example - Draw textured cube +* raylib [models] example - textured cube +* +* Example complexity rating: [★★☆☆] 2/4 * * Example originally created with raylib 4.5, last time updated with raylib 4.5 * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * -* Copyright (c) 2022-2023 Ramon Santamaria (@raysan5) +* Copyright (c) 2022-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -16,19 +18,22 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class ModelCubeTexture +public partial class ModelCubeTexture : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Models / Model Cube Texture"; + + public string Title => "raylib [models] example - textured cube"; + + private Camera3D camera; + private Texture2D texture; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - draw cube texture"); - // Define the camera to look into our 3d world - Camera3D camera; + camera = new(); camera.Position = new Vector3(0.0f, 10.0f, 10.0f); camera.Target = new Vector3(0.0f, 0.0f, 0.0f); camera.Up = new Vector3(0.0f, 1.0f, 0.0f); @@ -36,60 +41,48 @@ public class ModelCubeTexture camera.Projection = CameraProjection.Perspective; // Load texture to be applied to the cubes sides - Texture2D texture = LoadTexture("resources/cubicmap_atlas.png"); + texture = LoadTexture("resources/cubicmap_atlas.png"); + } - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- + public void Update() + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); - // Main game loop - while (!WindowShouldClose()) - { - // Update - //---------------------------------------------------------------------------------- - //---------------------------------------------------------------------------------- + BeginMode3D(camera); - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); + // Draw cube with an applied texture + DrawCubeTexture(texture, new Vector3(-2.0f, 2.0f, 0.0f), 2.0f, 4.0f, 2.0f, Color.White); - BeginMode3D(camera); + // Draw cube with an applied texture, but only a defined rectangle piece of the texture + DrawCubeTextureRec( + texture, + new Rectangle(0, texture.Height / 2, texture.Width / 2, texture.Height / 2), + new Vector3(2.0f, 1.0f, 0.0f), + 2.0f, + 2.0f, + 2.0f, + Color.White + ); - // Draw cube with an applied texture - DrawCubeTexture(texture, new Vector3(-2.0f, 2.0f, 0.0f), 2.0f, 4.0f, 2.0f, Color.White); + DrawGrid(10, 1.0f); - // Draw cube with an applied texture, but only a defined rectangle piece of the texture - DrawCubeTextureRec( - texture, - new Rectangle(0, texture.Height / 2, texture.Width / 2, texture.Height / 2), - new Vector3(2.0f, 1.0f, 0.0f), - 2.0f, - 2.0f, - 2.0f, - Color.White - ); + EndMode3D(); - DrawGrid(10, 1.0f); + EndDrawing(); + //---------------------------------------------------------------------------------- + } - EndMode3D(); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- + public void Unload() + { UnloadTexture(texture); - - CloseWindow(); - //-------------------------------------------------------------------------------------- - - return 0; } // Draw cube textured // NOTE: Cube position is the center position - static void DrawCubeTexture( + private static void DrawCubeTexture( Texture2D texture, Vector3 position, float width, @@ -98,9 +91,9 @@ public class ModelCubeTexture Color color ) { - float x = position.X; - float y = position.Y; - float z = position.Z; + var x = position.X; + var y = position.Y; + var z = position.Z; // Set desired texture to be enabled while drawing following vertex data Rlgl.SetTexture(texture.Id); @@ -218,7 +211,7 @@ public class ModelCubeTexture } // Draw cube with texture piece applied to all faces - static void DrawCubeTextureRec( + private static void DrawCubeTextureRec( Texture2D texture, Rectangle source, Vector3 position, @@ -228,11 +221,11 @@ public class ModelCubeTexture Color color ) { - float x = position.X; - float y = position.Y; - float z = position.Z; - float texWidth = (float)texture.Width; - float texHeight = (float)texture.Height; + var x = position.X; + var y = position.Y; + var z = position.Z; + var texWidth = (float)texture.Width; + var texHeight = (float)texture.Height; // Set desired texture to be enabled while drawing following vertex data Rlgl.SetTexture(texture.Id); @@ -312,5 +305,32 @@ public class ModelCubeTexture Rlgl.SetTexture(0); } -} + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [models] example - textured cube"); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + var game = new ModelCubeTexture(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) + { + game.Update(); + } + + game.Unload(); + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); + //-------------------------------------------------------------------------------------- + + return 0; + } +} diff --git a/Examples/Models/ModelLoading.cs b/Examples/Models/ModelLoading.cs index 9a84d06..21f7862 100644 --- a/Examples/Models/ModelLoading.cs +++ b/Examples/Models/ModelLoading.cs @@ -1,20 +1,28 @@ /******************************************************************************************* * -* raylib [models] example - Models loading +* raylib [models] example - loading * -* raylib supports multiple models file formats: +* Example complexity rating: [★☆☆☆] 1/4 * -* - OBJ > Text file, must include vertex position-texcoords-normals information, -* if files references some .mtl materials file, it will be loaded (or try to) -* - GLTF > Modern text/binary file format, includes lot of information and it could -* also reference external files, raylib will try loading mesh and materials data -* - IQM > Binary file format including mesh vertex data but also animation data, -* raylib can load .iqm animations. +* NOTE: raylib supports multiple models file formats: * -* This example has been created using raylib 2.6 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* - OBJ > Text file format. Must include vertex position-texcoords-normals information, +* if .obj references some .mtl materials file, it will be tried to be loaded +* - GLTF/GLB > Text/binary file formats. Includes lot of information and it could +* also reference external files, mesh and materials data will be tried to be loaded +* - IQM > Binary file format. Includes mesh vertex data but also animation data, +* meshes and animation data can be loaded +* - VOX > Binary file format. MagikaVoxel mesh format: +* https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt +* - M3D > Binary file format. Model 3D format: +* https://bztsrc.gitlab.io/model3d * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* Example originally created with raylib 2.0, last time updated with raylib 4.2 +* +* 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -23,135 +31,171 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class ModelLoading +public partial class ModelLoading : IExample { - public unsafe static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Models / Model Loading"; + + public string Title => "raylib [models] example - loading"; + + private Camera3D camera; + private Model model; + private Texture2D texture; + private Vector3 position; + private BoundingBox bounds; + private bool selected; + + public unsafe void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - models loading"); - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(50.0f, 50.0f, 50.0f); - camera.Target = new Vector3(0.0f, 10.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; + camera = new(); + camera.Position = new Vector3(50.0f, 50.0f, 50.0f); // Camera position + camera.Target = new Vector3(0.0f, 12.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera mode type - Model model = LoadModel("resources/models/obj/castle.obj"); - Texture2D texture = LoadTexture("resources/models/obj/castle_diffuse.png"); + model = LoadModel("resources/models/obj/castle.obj"); // Load model + texture = LoadTexture("resources/models/obj/castle_diffuse.png"); // Load model texture // Set map diffuse texture Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture); - Vector3 position = new(0.0f, 0.0f, 0.0f); - BoundingBox bounds = GetMeshBoundingBox(model.Meshes[0]); + position = new(0.0f, 0.0f, 0.0f); // Set model position + bounds = GetMeshBoundingBox(model.Meshes[0]); // Set model bounds // NOTE: bounds are calculated from the original size of the model, // if model is scaled on drawing, bounds must be also scaled - bool selected = false; + selected = false; + } + + public unsafe void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Orbital); + +#if BROWSER + // NOTE: Drag & drop file loading (IsFileDropped) is not available in the browser + // host, so it is skipped here. The default model stays loaded. +#else + // Load new models/textures on drag&drop + if (IsFileDropped()) + { + var droppedFiles = Raylib.GetDroppedFiles(); + + if (droppedFiles.Length == 1) // Only support one file dropped + { + if (IsFileExtension(droppedFiles[0], ".obj") || + IsFileExtension(droppedFiles[0], ".gltf") || + IsFileExtension(droppedFiles[0], ".glb") || + IsFileExtension(droppedFiles[0], ".vox") || + IsFileExtension(droppedFiles[0], ".iqm") || + IsFileExtension(droppedFiles[0], ".m3d") // Model file formats supported + ) + { + UnloadModel(model); // Unload previous model + model = LoadModel(droppedFiles[0]); // Load new model + + // Set current map diffuse texture + Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture); + + bounds = GetMeshBoundingBox(model.Meshes[0]); + + // Move camera position from target enough distance to visualize model properly + camera.Position.X = bounds.Max.X + 10.0f; + camera.Position.Y = bounds.Max.Y + 10.0f; + camera.Position.Z = bounds.Max.Z + 10.0f; + } + else if (IsFileExtension(droppedFiles[0], ".png")) // Texture file formats supported + { + // Unload current model texture and load new one + UnloadTexture(texture); + texture = LoadTexture(droppedFiles[0]); + Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture); + } + } + } +#endif + + // Select model on mouse click + if (IsMouseButtonPressed(MouseButton.Left)) + { + // Check collision between ray and box + if (GetRayCollisionBox(GetScreenToWorldRay(GetMousePosition(), camera), bounds).Hit) + { + selected = !selected; + } + else + { + selected = false; + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + DrawModel(model, position, 1.0f, Color.White); // Draw 3d model with texture + + DrawGrid(20, 10.0f); // Draw a grid + + if (selected) + { + DrawBoundingBox(bounds, Color.Green); // Draw selection box + } + + EndMode3D(); + + DrawText("Drag & drop model to load mesh/texture.", 10, GetScreenHeight() - 20, 10, Color.DarkGray); + if (selected) + { + DrawText("MODEL SELECTED", GetScreenWidth() - 110, 10, 10, Color.Green); + } + + DrawText("(c) Castle 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, Color.Gray); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texture); + UnloadModel(model); + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [models] example - loading"); SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new ModelLoading(); + game.Init(); + // Main game loop while (!WindowShouldClose()) { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Orbital); - - if (IsFileDropped()) - { - string[] droppedFiles = Raylib.GetDroppedFiles(); - - if (droppedFiles.Length == 1) - { - if (IsFileExtension(droppedFiles[0], ".obj") || - IsFileExtension(droppedFiles[0], ".gltf") || - IsFileExtension(droppedFiles[0], ".glb") || - IsFileExtension(droppedFiles[0], ".vox") || - IsFileExtension(droppedFiles[0], ".iqm") || - IsFileExtension(droppedFiles[0], ".m3d") - ) - { - UnloadModel(model); - model = LoadModel(droppedFiles[0]); - - // Set current map diffuse texture - Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture); - - bounds = GetMeshBoundingBox(model.Meshes[0]); - - // TODO: Move camera position from target enough distance to visualize model properly - } - else if (IsFileExtension(droppedFiles[0], ".png")) - { - // Unload model texture and load new one - UnloadTexture(texture); - texture = LoadTexture(droppedFiles[0]); - Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture); - } - } - } - - // Select model on mouse click - if (IsMouseButtonPressed(MouseButton.Left)) - { - // Check collision between ray and box - if (GetRayCollisionBox(GetScreenToWorldRay(GetMousePosition(), camera), bounds).Hit) - { - selected = !selected; - } - else - { - selected = false; - } - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawModel(model, position, 1.0f, Color.White); - - DrawGrid(20, 10.0f); - - if (selected) - { - DrawBoundingBox(bounds, Color.Green); - } - - EndMode3D(); - - DrawText("Drag & drop model to load mesh/texture.", 10, GetScreenHeight() - 20, 10, Color.DarkGray); - if (selected) - { - DrawText("MODEL SELECTED", GetScreenWidth() - 110, 10, 10, Color.Green); - } - - DrawText("(c) Castle 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, Color.Gray); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texture); - UnloadModel(model); - CloseWindow(); //-------------------------------------------------------------------------------------- diff --git a/Examples/Models/OrthographicProjection.cs b/Examples/Models/OrthographicProjection.cs index bf9e302..1cde4b5 100644 --- a/Examples/Models/OrthographicProjection.cs +++ b/Examples/Models/OrthographicProjection.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [models] example - Show the difference between perspective and orthographic projection +* raylib [models] example - orthographic projection * -* This program is heavily based on the geometric objects example +* Example complexity rating: [★☆☆☆] 1/4 * -* This example has been created using raylib 1.9.7 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 2.0, last time updated with raylib 3.7 * -* Copyright (c) 2018 Max Danielsson ref Ramon Santamaria (@raysan5) +* Example contributed by Max Danielsson (@autious) 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) 2018-2025 Max Danielsson (@autious) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -16,96 +20,119 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class OrthographicProjection +public partial class OrthographicProjection : IExample { public const float FOVY_PERSPECTIVE = 45.0f; public const float WIDTH_ORTHOGRAPHIC = 10.0f; - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Models / Orthographic Projection"; + + public string Title => "raylib [models] example - orthographic projection"; + + private Camera3D camera; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - geometric shapes"); - // Define the camera to look into our 3d world - Camera3D camera = new(); + camera = new(); camera.Position = new Vector3(0.0f, 10.0f, 10.0f); camera.Target = new Vector3(0.0f, 0.0f, 0.0f); camera.Up = new Vector3(0.0f, 1.0f, 0.0f); camera.FovY = FOVY_PERSPECTIVE; camera.Projection = CameraProjection.Perspective; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KeyboardKey.Space)) + { + if (camera.Projection == CameraProjection.Perspective) + { + camera.FovY = WIDTH_ORTHOGRAPHIC; + camera.Projection = CameraProjection.Orthographic; + } + else + { + camera.FovY = FOVY_PERSPECTIVE; + camera.Projection = CameraProjection.Perspective; + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + DrawCube(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Red); + DrawCubeWires(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Gold); + DrawCubeWires(new Vector3(-4.0f, 0.0f, -2.0f), 3.0f, 6.0f, 2.0f, Color.Maroon); + + DrawSphere(new Vector3(-1.0f, 0.0f, -2.0f), 1.0f, Color.Green); + DrawSphereWires(new Vector3(1.0f, 0.0f, 2.0f), 2.0f, 16, 16, Color.Lime); + + DrawCylinder(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.SkyBlue); + DrawCylinderWires(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.DarkBlue); + DrawCylinderWires(new Vector3(4.5f, -1.0f, 2.0f), 1.0f, 1.0f, 2.0f, 6, Color.Brown); + + DrawCylinder(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Gold); + DrawCylinderWires(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Pink); + + DrawGrid(10, 1.0f); // Draw a grid + + EndMode3D(); + + DrawText("Press Spacebar to switch camera type", 10, GetScreenHeight() - 30, 20, Color.DarkGray); + + if (camera.Projection == CameraProjection.Orthographic) + { + DrawText("ORTHOGRAPHIC", 10, 40, 20, Color.Black); + } + else if (camera.Projection == CameraProjection.Perspective) + { + DrawText("PERSPECTIVE", 10, 40, 20, Color.Black); + } + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [models] example - orthographic projection"); SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new OrthographicProjection(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyPressed(KeyboardKey.Space)) - { - if (camera.Projection == CameraProjection.Perspective) - { - camera.FovY = WIDTH_ORTHOGRAPHIC; - camera.Projection = CameraProjection.Orthographic; - } - else - { - camera.FovY = FOVY_PERSPECTIVE; - camera.Projection = CameraProjection.Perspective; - } - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawCube(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Red); - DrawCubeWires(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Gold); - DrawCubeWires(new Vector3(-4.0f, 0.0f, -2.0f), 3.0f, 6.0f, 2.0f, Color.Maroon); - - DrawSphere(new Vector3(-1.0f, 0.0f, -2.0f), 1.0f, Color.Green); - DrawSphereWires(new Vector3(1.0f, 0.0f, 2.0f), 2.0f, 16, 16, Color.Lime); - - DrawCylinder(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.SkyBlue); - DrawCylinderWires(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.DarkBlue); - DrawCylinderWires(new Vector3(4.5f, -1.0f, 2.0f), 1.0f, 1.0f, 2.0f, 6, Color.Brown); - - DrawCylinder(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Gold); - DrawCylinderWires(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Pink); - - DrawGrid(10, 1.0f); - - EndMode3D(); - - DrawText("Press Spacebar to switch camera type", 10, GetScreenHeight() - 30, 20, Color.DarkGray); - - if (camera.Projection == CameraProjection.Orthographic) - { - DrawText("ORTHOGRAPHIC", 10, 40, 20, Color.Black); - } - else if (camera.Projection == CameraProjection.Perspective) - { - DrawText("PERSPECTIVE", 10, 40, 20, Color.Black); - } - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Models/SkyboxDemo.cs b/Examples/Models/SkyboxDemo.cs index 4964af8..69761dc 100644 --- a/Examples/Models/SkyboxDemo.cs +++ b/Examples/Models/SkyboxDemo.cs @@ -1,32 +1,49 @@ /******************************************************************************************* * -* raylib [models] example - Skybox loading and drawing +* raylib [models] example - skybox rendering * -* This example has been created using raylib 1.8 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2017 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.8, last time updated with raylib 4.0 +* +* 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) 2017-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ +using System; using System.Numerics; using static Raylib_cs.Raylib; namespace Examples.Models; -public class SkyboxDemo +public partial class SkyboxDemo : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + // GLSL version used for shaders (330 desktop, 100 web/GLES) + public const int GlslVersion = 330; + + public string Name => "Models / Skybox Demo"; + + public string Title => "raylib [models] example - skybox rendering"; + + public bool CursorDisabled => true; + + private Camera3D camera; + private Model skybox; + private bool useHdr; + private Shader shdrCubemap; + private string skyboxFileName; + private Texture2D panorama; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - skybox loading and drawing"); - // Define the camera to look into our 3d world - Camera3D camera = new(); + camera = new(); camera.Position = new Vector3(1.0f, 1.0f, 1.0f); camera.Target = new Vector3(4.0f, 1.0f, 4.0f); camera.Up = new Vector3(0.0f, 1.0f, 0.0f); @@ -34,14 +51,19 @@ public class SkyboxDemo camera.Projection = CameraProjection.Perspective; // Load skybox model - Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f); - Model skybox = LoadModelFromMesh(cube); + var cube = GenMeshCube(1.0f, 1.0f, 1.0f); + skybox = LoadModelFromMesh(cube); - bool useHdr = false; + // Set this to true to use an HDR Texture + // NOTE: raylib must be built with HDR Support for this to work: SUPPORT_FILEFORMAT_HDR + useHdr = false; // Load skybox shader and set required locations // NOTE: Some locations are automatically set at shader loading - Shader shdrSkybox = LoadShader("resources/shaders/glsl330/skybox.vs", "resources/shaders/glsl330/skybox.fs"); + var shdrSkybox = LoadShader( + $"resources/shaders/glsl{GlslVersion}/skybox.vs", + $"resources/shaders/glsl{GlslVersion}/skybox.fs" + ); Raylib.SetShaderValue( shdrSkybox, @@ -67,9 +89,9 @@ public class SkyboxDemo Raylib.SetMaterialShader(ref skybox, 0, ref shdrSkybox); // Load cubemap shader and setup required shader locations - Shader shdrCubemap = LoadShader( - "resources/shaders/glsl330/cubemap.vs", - "resources/shaders/glsl330/cubemap.fs" + shdrCubemap = LoadShader( + $"resources/shaders/glsl{GlslVersion}/cubemap.vs", + $"resources/shaders/glsl{GlslVersion}/cubemap.fs" ); Raylib.SetShaderValue( shdrCubemap, @@ -79,14 +101,12 @@ public class SkyboxDemo ); // Load skybox - string skyboxFileName = "resources/dresden_square_2k.hdr"; - - Texture2D panorama; + skyboxFileName = "resources/dresden_square_2k.hdr"; if (useHdr) { panorama = LoadTexture(skyboxFileName); - Texture2D cubemap = GenTextureCubemap( + var cubemap = GenTextureCubemap( shdrCubemap, panorama, 1024, @@ -97,108 +117,130 @@ public class SkyboxDemo } else { - Image img = LoadImage("resources/skybox.png"); - Texture2D cubemap = LoadTextureCubemap(img, CubemapLayout.AutoDetect); + // TODO: WARNING: On PLATFORM_WEB it requires a big amount of memory to process input image + // and generate the required cubemap image to be passed to rlLoadTextureCubemap() + var img = LoadImage("resources/skybox.png"); + var cubemap = LoadTextureCubemap(img, CubemapLayout.AutoDetect); SetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap, ref cubemap); UnloadImage(img); } + } - DisableCursor(); + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.FirstPerson); - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) + // Load new cubemap texture on drag & drop + if (IsFileDropped()) { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.FirstPerson); + var files = Raylib.GetDroppedFiles(); - // Load new cubemap texture on drag & drop - if (IsFileDropped()) + if (files.Length == 1) { - string[] files = Raylib.GetDroppedFiles(); - - if (files.Length == 1) + if (IsFileExtension(files[0], ".png;.jpg;.hdr;.bmp;.tga")) { - if (IsFileExtension(files[0], ".png;.jpg;.hdr;.bmp;.tga")) + // Unload cubemap texture and load new one + UnloadTexture(Raylib.GetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap)); + + if (useHdr) { - // Unload cubemap texture and load new one - UnloadTexture(Raylib.GetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap)); - - if (useHdr) - { - panorama = LoadTexture(files[0]); - Texture2D cubemap = GenTextureCubemap( - shdrCubemap, - panorama, - 1024, - PixelFormat.UncompressedR8G8B8A8 - ); - SetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap, ref cubemap); - UnloadTexture(panorama); - } - else - { - Image img = LoadImage(files[0]); - Texture2D cubemap = LoadTextureCubemap(img, CubemapLayout.AutoDetect); - SetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap, ref cubemap); - UnloadImage(img); - } - - skyboxFileName = files[0]; + panorama = LoadTexture(files[0]); + var cubemap = GenTextureCubemap( + shdrCubemap, + panorama, + 1024, + PixelFormat.UncompressedR8G8B8A8 + ); + SetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap, ref cubemap); + UnloadTexture(panorama); } + else + { + var img = LoadImage(files[0]); + var cubemap = LoadTextureCubemap(img, CubemapLayout.AutoDetect); + SetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap, ref cubemap); + UnloadImage(img); + } + + skyboxFileName = files[0]; } } - //---------------------------------------------------------------------------------- + } + //---------------------------------------------------------------------------------- - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); - BeginMode3D(camera); + BeginMode3D(camera); - // We are inside the cube, we need to disable backface culling! - Rlgl.DisableBackfaceCulling(); - Rlgl.DisableDepthMask(); - DrawModel(skybox, Vector3.Zero, 1.0f, Color.White); - Rlgl.EnableBackfaceCulling(); - Rlgl.EnableDepthMask(); + // We are inside the cube, we need to disable backface culling! + Rlgl.DisableBackfaceCulling(); + Rlgl.DisableDepthMask(); + DrawModel(skybox, Vector3.Zero, 1.0f, Color.White); + Rlgl.EnableBackfaceCulling(); + Rlgl.EnableDepthMask(); - DrawGrid(10, 1.0f); + DrawGrid(10, 1.0f); - EndMode3D(); + EndMode3D(); - if (useHdr) - { - DrawText( - $"Panorama image from hdrihaven.com: {skyboxFileName}", - 10, - GetScreenHeight() - 20, - 10, - Color.Black - ); - } - else - { - DrawText($": {skyboxFileName}", 10, GetScreenHeight() - 20, 10, Color.Black); - } - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + if (useHdr) + { + DrawText( + $"Panorama image from hdrihaven.com: {skyboxFileName}", + 10, + GetScreenHeight() - 20, + 10, + Color.Black + ); + } + else + { + DrawText($": {skyboxFileName}", 10, GetScreenHeight() - 20, 10, Color.Black); } - // De-Initialization - //-------------------------------------------------------------------------------------- + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { UnloadShader(Raylib.GetMaterial(ref skybox, 0).Shader); UnloadTexture(Raylib.GetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap)); UnloadModel(skybox); + } + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [models] example - skybox rendering"); + + DisableCursor(); // Limit cursor to relative movement inside the window + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + var game = new SkyboxDemo(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) + { + game.Update(); + } + + game.Unload(); + + // De-Initialization + //-------------------------------------------------------------------------------------- CloseWindow(); //-------------------------------------------------------------------------------------- @@ -215,10 +257,10 @@ public class SkyboxDemo // STEP 1: Setup framebuffer //------------------------------------------------------------------------------------------ - uint rbo = Rlgl.LoadTextureDepth(size, size, true); + var rbo = Rlgl.LoadTextureDepth(size, size, true); cubemap.Id = Rlgl.LoadTextureCubemap(null, size, format, 1); - uint fbo = Rlgl.LoadFramebuffer(); + var fbo = Rlgl.LoadFramebuffer(); Rlgl.FramebufferAttach( fbo, rbo, @@ -235,7 +277,7 @@ public class SkyboxDemo ); // Check if framebuffer is complete with attachments (valid) - if (Rlgl.FramebufferComplete(fbo)) + if (Rlgl.FramebufferComplete(fbo) != 0) { Console.WriteLine($"FBO: [ID {fbo}] Framebuffer object created successfully"); } @@ -247,7 +289,7 @@ public class SkyboxDemo Rlgl.EnableShader(shader.Id); // Define projection matrix and send it to shader - Matrix4x4 matFboProjection = Raymath.MatrixPerspective( + var matFboProjection = Raymath.MatrixPerspective( 90.0f * DEG2RAD, 1.0f, Rlgl.CULL_DISTANCE_NEAR, @@ -256,14 +298,14 @@ public class SkyboxDemo Rlgl.SetUniformMatrix(shader.Locs[(int)ShaderLocationIndex.MatrixProjection], matFboProjection); // Define view matrix for every side of the cubemap - Matrix4x4[] fboViews = new[] + var fboViews = new[] { - Raymath.MatrixLookAt(Vector3.Zero, new Vector3(-1.0f, 0.0f, 0.0f), new Vector3( 0.0f, -1.0f, 0.0f)), Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 1.0f, 0.0f, 0.0f), new Vector3( 0.0f, -1.0f, 0.0f)), + Raymath.MatrixLookAt(Vector3.Zero, new Vector3(-1.0f, 0.0f, 0.0f), new Vector3( 0.0f, -1.0f, 0.0f)), Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 0.0f, 1.0f, 0.0f), new Vector3( 0.0f, 0.0f, 1.0f)), Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 0.0f, -1.0f, 0.0f), new Vector3( 0.0f, 0.0f, -1.0f)), - Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 0.0f, 0.0f, -1.0f), new Vector3( 0.0f, -1.0f, 0.0f)), Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 0.0f, 0.0f, 1.0f), new Vector3( 0.0f, -1.0f, 0.0f)), + Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 0.0f, 0.0f, -1.0f), new Vector3( 0.0f, -1.0f, 0.0f)), }; // Set viewport to current fbo dimensions @@ -273,7 +315,7 @@ public class SkyboxDemo Rlgl.ActiveTextureSlot(0); Rlgl.EnableTexture(panorama.Id); - for (int i = 0; i < 6; i++) + for (var i = 0; i < 6; i++) { // Set the view matrix for the current cube face Rlgl.SetUniformMatrix(shader.Locs[(int)ShaderLocationIndex.MatrixView], fboViews[i]); diff --git a/Examples/Models/SolarSystem.cs b/Examples/Models/SolarSystem.cs index 8b5c294..36c989f 100644 --- a/Examples/Models/SolarSystem.cs +++ b/Examples/Models/SolarSystem.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [models] example - rlgl module usage with push/pop matrix transformations +* raylib [models] example - rlgl solar system * -* This example uses [rlgl] module funtionality (pseudo-OpenGL 1.1 style coding) +* Example complexity rating: [★★★★] 4/4 * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* NOTE: This example uses [rlgl] module functionality (pseudo-OpenGL 1.1 style coding) * -* Copyright (c) 2018 Ramon Santamaria (@raysan5) +* Example originally created with raylib 2.5, last time updated with raylib 4.0 +* +* 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) 2018-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -17,145 +21,137 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class SolarSystem +public partial class SolarSystem : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + private const float sunRadius = 4.0f; + private const float earthRadius = 0.6f; + private const float earthOrbitRadius = 8.0f; + private const float moonRadius = 0.16f; + private const float moonOrbitRadius = 1.5f; + + public string Name => "Models / Solar System"; + + public string Title => "raylib [models] example - rlgl solar system"; + + private Camera3D camera; + + private float rotationSpeed; + + private float earthRotation; + private float earthOrbitRotation; + private float moonRotation; + private float moonOrbitRotation; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - const float sunRadius = 4.0f; - const float earthRadius = 0.6f; - const float earthOrbitRadius = 8.0f; - const float moonRadius = 0.16f; - const float moonOrbitRadius = 1.5f; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - rlgl module usage with push/pop matrix transformations"); - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(16.0f, 16.0f, 16.0f); - camera.Target = new Vector3(0.0f, 0.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; + camera = new(); + camera.Position = new Vector3(16.0f, 16.0f, 16.0f); // Camera position + camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type - // General system rotation speed - float rotationSpeed = 0.2f; - // Rotation of earth around itself (days) in degrees - float earthRotation = 0.0f; - // Rotation of earth around the Sun (years) in degrees - float earthOrbitRotation = 0.0f; - // Rotation of moon around itself - float moonRotation = 0.0f; - // Rotation of moon around earth in degrees - float moonOrbitRotation = 0.0f; + rotationSpeed = 0.2f; // General system rotation speed - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- + earthRotation = 0.0f; // Rotation of earth around itself (days) in degrees + earthOrbitRotation = 0.0f; // Rotation of earth around the Sun (years) in degrees + moonRotation = 0.0f; // Rotation of moon around itself + moonOrbitRotation = 0.0f; // Rotation of moon around earth in degrees + } - // Main game loop - while (!WindowShouldClose()) - { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Free); + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + earthRotation += (5.0f * rotationSpeed); + earthOrbitRotation += (365 / 360.0f * (5.0f * rotationSpeed) * rotationSpeed); + moonRotation += (2.0f * rotationSpeed); + moonOrbitRotation += (8.0f * rotationSpeed); + //---------------------------------------------------------------------------------- - earthRotation += (5.0f * rotationSpeed); - earthOrbitRotation += (365 / 360.0f * (5.0f * rotationSpeed) * rotationSpeed); - moonRotation += (2.0f * rotationSpeed); - moonOrbitRotation += (8.0f * rotationSpeed); - //---------------------------------------------------------------------------------- + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); + BeginMode3D(camera); - BeginMode3D(camera); + Rlgl.PushMatrix(); + // Scale Sun + Rlgl.Scalef(sunRadius, sunRadius, sunRadius); + // Draw the Sun + DrawSphereBasic(Color.Gold); + Rlgl.PopMatrix(); - Rlgl.PushMatrix(); - // Scale Sun - Rlgl.Scalef(sunRadius, sunRadius, sunRadius); - // Draw the Sun - DrawSphereBasic(Color.Gold); - Rlgl.PopMatrix(); + Rlgl.PushMatrix(); + // Rotation for Earth orbit around Sun + Rlgl.Rotatef(earthOrbitRotation, 0.0f, 1.0f, 0.0f); + // Translation for Earth orbit + Rlgl.Translatef(earthOrbitRadius, 0.0f, 0.0f); - Rlgl.PushMatrix(); - // Rotation for Earth orbit around Sun - Rlgl.Rotatef(earthOrbitRotation, 0.0f, 1.0f, 0.0f); - // Translation for Earth orbit - Rlgl.Translatef(earthOrbitRadius, 0.0f, 0.0f); - // Rotation for Earth orbit around Sun inverted - Rlgl.Rotatef(-earthOrbitRotation, 0.0f, 1.0f, 0.0f); + Rlgl.PushMatrix(); + // Rotation for Earth itself + Rlgl.Rotatef(earthRotation, 0.25f, 1.0f, 0.0f); + // Scale Earth + Rlgl.Scalef(earthRadius, earthRadius, earthRadius); - Rlgl.PushMatrix(); - // Rotation for Earth itself - Rlgl.Rotatef(earthRotation, 0.25f, 1.0f, 0.0f); - // Scale Earth - Rlgl.Scalef(earthRadius, earthRadius, earthRadius); + // Draw the Earth + DrawSphereBasic(Color.Blue); + Rlgl.PopMatrix(); - // Draw the Earth - DrawSphereBasic(Color.Blue); - Rlgl.PopMatrix(); + // Rotation for Moon orbit around Earth + Rlgl.Rotatef(moonOrbitRotation, 0.0f, 1.0f, 0.0f); + // Translation for Moon orbit + Rlgl.Translatef(moonOrbitRadius, 0.0f, 0.0f); + // Rotation for Moon itself + Rlgl.Rotatef(moonRotation, 0.0f, 1.0f, 0.0f); + // Scale Moon + Rlgl.Scalef(moonRadius, moonRadius, moonRadius); - // Rotation for Moon orbit around Earth - Rlgl.Rotatef(moonOrbitRotation, 0.0f, 1.0f, 0.0f); - // Translation for Moon orbit - Rlgl.Translatef(moonOrbitRadius, 0.0f, 0.0f); - // Rotation for Moon orbit around Earth inverted - Rlgl.Rotatef(-moonOrbitRotation, 0.0f, 1.0f, 0.0f); - // Rotation for Moon itself - Rlgl.Rotatef(moonRotation, 0.0f, 1.0f, 0.0f); - // Scale Moon - Rlgl.Scalef(moonRadius, moonRadius, moonRadius); + // Draw the Moon + DrawSphereBasic(Color.LightGray); + Rlgl.PopMatrix(); - // Draw the Moon - DrawSphereBasic(Color.LightGray); - Rlgl.PopMatrix(); + // Some reference elements (not affected by previous matrix transformations) + DrawCircle3D( + new Vector3(0.0f, 0.0f, 0.0f), + earthOrbitRadius, + new Vector3(1, 0, 0), + 90.0f, + Fade(Color.Red, 0.5f) + ); + DrawGrid(20, 1.0f); - // Some reference elements (not affected by previous matrix transformations) - DrawCircle3D( - new Vector3(0.0f, 0.0f, 0.0f), - earthOrbitRadius, - new Vector3(1, 0, 0), - 90.0f, - ColorAlpha(Color.Red, 0.5f) - ); - DrawGrid(20, 1.0f); + EndMode3D(); - EndMode3D(); + DrawText("EARTH ORBITING AROUND THE SUN!", 400, 10, 20, Color.Maroon); + DrawFPS(10, 10); - DrawText("EARTH ORBITING AROUND THE SUN!", 400, 10, 20, Color.Maroon); - DrawFPS(10, 10); + EndDrawing(); + //---------------------------------------------------------------------------------- + } - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - CloseWindow(); - //-------------------------------------------------------------------------------------- - - return 0; + public void Unload() + { } // Draw sphere without any matrix transformation // NOTE: Sphere is drawn in world position ( 0, 0, 0 ) with radius 1.0f - static void DrawSphereBasic(Color color) + private static void DrawSphereBasic(Color color) { - int rings = 16; - int slices = 16; + var rings = 16; + var slices = 16; Rlgl.Begin(DrawMode.Triangles); Rlgl.Color4ub(color.R, color.G, color.B, color.A); - for (int i = 0; i < (rings + 2); i++) + for (var i = 0; i < (rings + 2); i++) { - for (int j = 0; j < slices; j++) + for (var j = 0; j < slices; j++) { Rlgl.Vertex3f( MathF.Cos(DEG2RAD * (270 + (180 / (rings + 1)) * i)) * MathF.Sin(DEG2RAD * (j * 360 / slices)), @@ -192,5 +188,32 @@ public class SolarSystem } Rlgl.End(); } -} + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [models] example - rlgl solar system"); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + var game = new SolarSystem(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + game.Update(); + } + + game.Unload(); + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; + } +} diff --git a/Examples/Models/WavingCubes.cs b/Examples/Models/WavingCubes.cs index 27867d5..30a4442 100644 --- a/Examples/Models/WavingCubes.cs +++ b/Examples/Models/WavingCubes.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [models] example - Waving cubes +* raylib [models] example - waving cubes * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 2.5, last time updated with raylib 3.7 * * Example contributed by Codecat (@codecat) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2019 Codecat (@codecat) and 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) 2019-2025 Codecat (@codecat) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -17,101 +21,125 @@ using static Raylib_cs.Raylib; namespace Examples.Models; -public class WavingCubes +public partial class WavingCubes : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + // Specify the amount of blocks in each direction + private const int numBlocks = 15; + + public string Name => "Models / Waving Cubes"; + + public string Title => "raylib [models] example - waving cubes"; + + private Camera3D camera; + + public void Init() + { + // Initialize the camera + camera = new(); + camera.Position = new Vector3(30.0f, 20.0f, 30.0f); // Camera position + camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 70.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + var time = GetTime(); + + // Calculate time scale for cube position and size + var scale = (2.0f + (float)Math.Sin(time)) * 0.7f; + + // Move camera around the scene + var cameraTime = time * 0.3; + camera.Position.X = (float)Math.Cos(cameraTime) * 40.0f; + camera.Position.Z = (float)Math.Sin(cameraTime) * 40.0f; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + DrawGrid(10, 5.0f); + + for (var x = 0; x < numBlocks; x++) + { + for (var y = 0; y < numBlocks; y++) + { + for (var z = 0; z < numBlocks; z++) + { + // Scale of the blocks depends on x/y/z positions + var blockScale = (x + y + z) / 30.0f; + + // Scatter makes the waving effect by adding blockScale over time + var scatter = (float)Math.Sin(blockScale * 20.0f + (float)(time * 4.0f)); + + // Calculate the cube position + Vector3 cubePos = new( + (float)(x - numBlocks / 2) * (scale * 3.0f) + scatter, + (float)(y - numBlocks / 2) * (scale * 2.0f) + scatter, + (float)(z - numBlocks / 2) * (scale * 3.0f) + scatter + ); + + // Pick a color with a hue depending on cube position for the rainbow color effect + // NOTE: This function is quite costly to be done per cube and frame, + // pre-catching the results into a separate array could improve performance + var cubeColor = ColorFromHSV((float)(((x + y + z) * 18) % 360), 0.75f, 0.9f); + + // Calculate cube size + var cubeSize = (2.4f - scale) * blockScale; + + // And finally, draw the cube! + DrawCube(cubePos, cubeSize, cubeSize, cubeSize, cubeColor); + } + } + } + + EndMode3D(); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [models] example - waving cubes"); - // Initialize the camera - Camera3D camera = new(); - camera.Position = new Vector3(30.0f, 20.0f, 30.0f); - camera.Target = new Vector3(0.0f, 0.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 70.0f; - camera.Projection = CameraProjection.Perspective; - - // Specify the amount of blocks in each direction - const int numBlocks = 15; - SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new WavingCubes(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - double time = GetTime(); - - // Calculate time scale for cube position and size - float scale = (2.0f + (float)Math.Sin(time)) * 0.7f; - - // Move camera around the scene - double cameraTime = time * 0.3; - camera.Position.X = (float)Math.Cos(cameraTime) * 40.0f; - camera.Position.Z = (float)Math.Sin(cameraTime) * 40.0f; - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawGrid(10, 5.0f); - - for (int x = 0; x < numBlocks; x++) - { - for (int y = 0; y < numBlocks; y++) - { - for (int z = 0; z < numBlocks; z++) - { - // Scale of the blocks depends on x/y/z positions - float blockScale = (x + y + z) / 30.0f; - - // Scatter makes the waving effect by adding blockScale over time - float scatter = (float)Math.Sin(blockScale * 20.0f + (float)(time * 4.0f)); - - // Calculate the cube position - Vector3 cubePos = new( - (float)(x - numBlocks / 2) * (scale * 3.0f) + scatter, - (float)(y - numBlocks / 2) * (scale * 2.0f) + scatter, - (float)(z - numBlocks / 2) * (scale * 3.0f) + scatter - ); - - // Pick a color with a hue depending on cube position for the rainbow color effect - Color cubeColor = ColorFromHSV((float)(((x + y + z) * 18) % 360), 0.75f, 0.9f); - - // Calculate cube size - float cubeSize = (2.4f - scale) * blockScale; - - // And finally, draw the cube! - DrawCube(cubePos, cubeSize, cubeSize, cubeSize, cubeColor); - } - } - } - - EndMode3D(); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } } - diff --git a/Examples/Models/YawPitchRoll.cs b/Examples/Models/YawPitchRoll.cs index 85e8092..3b26f34 100644 --- a/Examples/Models/YawPitchRoll.cs +++ b/Examples/Models/YawPitchRoll.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [models] example - Plane rotations (yaw, pitch, roll) +* raylib [models] example - yaw pitch roll * -* This example has been created using raylib 1.8 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 1.8, last time updated with raylib 4.0 * * Example contributed by Berni (@Berni8k) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2017-2021 Berni (@Berni8k) and 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) 2017-2025 Berni (@Berni8k) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -17,146 +21,178 @@ using static Raylib_cs.Raymath; namespace Examples.Models; -public class YawPitchRoll +public partial class YawPitchRoll : IExample { - public unsafe static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Models / Yaw Pitch Roll"; + + public string Title => "raylib [models] example - yaw pitch roll"; + + private Camera3D camera; + + private Model model; + private Texture2D texture; + + private float pitch; + private float roll; + private float yaw; + + public unsafe void Init() + { + camera = new(); + camera.Position = new Vector3(0.0f, 50.0f, -120.0f);// Camera position perspective + camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 30.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera type + + model = LoadModel("resources/models/obj/plane.obj"); // Load model + texture = LoadTexture("resources/models/obj/plane_diffuse.png"); // Load model texture + + SetTextureWrap(texture, TextureWrap.Repeat); // Force Repeat to avoid issue on Web version + + model.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Texture = texture; // Set map diffuse texture + + pitch = 0.0f; + roll = 0.0f; + yaw = 0.0f; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + + // Plane pitch (x-axis) controls + if (IsKeyDown(KeyboardKey.Down)) + { + pitch += 0.6f; + } + else if (IsKeyDown(KeyboardKey.Up)) + { + pitch -= 0.6f; + } + else + { + if (pitch > 0.3f) + { + pitch -= 0.3f; + } + else if (pitch < -0.3f) + { + pitch += 0.3f; + } + } + + // Plane yaw (y-axis) controls + if (IsKeyDown(KeyboardKey.S)) + { + yaw += 1.0f; + } + else if (IsKeyDown(KeyboardKey.A)) + { + yaw -= 1.0f; + } + else + { + if (yaw > 0.0f) + { + yaw -= 0.5f; + } + else if (yaw < 0.0f) + { + yaw += 0.5f; + } + } + + // Plane roll (z-axis) controls + if (IsKeyDown(KeyboardKey.Left)) + { + roll += 1.0f; + } + else if (IsKeyDown(KeyboardKey.Right)) + { + roll -= 1.0f; + } + else + { + if (roll > 0.0f) + { + roll -= 0.5f; + } + else if (roll < 0.0f) + { + roll += 0.5f; + } + } + + // Tranformation matrix for rotations + model.Transform = MatrixRotateXYZ(new Vector3(DEG2RAD * pitch, DEG2RAD * yaw, DEG2RAD * roll)); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + // Draw 3D model (recomended to draw 3D always before 2D) + BeginMode3D(camera); + + // Draw 3d model with texture + DrawModel(model, new Vector3(0.0f, -8.0f, 0.0f), 1.0f, Color.White); + DrawGrid(10, 10.0f); + + EndMode3D(); + + // Draw controls info + DrawRectangle(30, 370, 260, 70, Fade(Color.Green, 0.5f)); + DrawRectangleLines(30, 370, 260, 70, Fade(Color.DarkGreen, 0.5f)); + DrawText("Pitch controlled with: KEY_UP / KEY_DOWN", 40, 380, 10, Color.DarkGray); + DrawText("Roll controlled with: KEY_LEFT / KEY_RIGHT", 40, 400, 10, Color.DarkGray); + DrawText("Yaw controlled with: KEY_A / KEY_S", 40, 420, 10, Color.DarkGray); + + DrawText( + "(c) WWI Plane Model created by GiaHanLam", + screenWidth - 240, + screenHeight - 20, + 10, + Color.DarkGray + ); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadModel(model); + } + + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] example - plane rotations (yaw, pitch, roll)"); - - Camera3D camera = new(); - camera.Position = new Vector3(0.0f, 50.0f, -120.0f); - camera.Target = new Vector3(0.0f, 0.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 30.0f; - camera.Projection = CameraProjection.Perspective; - - // Model loading - Model model = LoadModel("resources/models/obj/plane.obj"); - Texture2D texture = LoadTexture("resources/models/obj/plane_diffuse.png"); - model.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Texture = texture; - - float pitch = 0.0f; - float roll = 0.0f; - float yaw = 0.0f; + InitWindow(screenWidth, screenHeight, "raylib [models] example - yaw pitch roll"); SetTargetFPS(60); //-------------------------------------------------------------------------------------- - while (!WindowShouldClose()) + var game = new YawPitchRoll(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - - // Plane roll (x-axis) controls - if (IsKeyDown(KeyboardKey.Down)) - { - pitch += 0.6f; - } - else if (IsKeyDown(KeyboardKey.Up)) - { - pitch -= 0.6f; - } - else - { - if (pitch > 0.3f) - { - pitch -= 0.3f; - } - else if (pitch < -0.3f) - { - pitch += 0.3f; - } - } - - // Plane yaw (y-axis) controls - if (IsKeyDown(KeyboardKey.S)) - { - yaw += 1.0f; - } - else if (IsKeyDown(KeyboardKey.A)) - { - yaw -= 1.0f; - } - else - { - if (yaw > 0.0f) - { - yaw -= 0.5f; - } - else if (yaw < 0.0f) - { - yaw += 0.5f; - } - } - - // Plane pitch (z-axis) controls - if (IsKeyDown(KeyboardKey.Left)) - { - roll += 1.0f; - } - else if (IsKeyDown(KeyboardKey.Right)) - { - roll -= 1.0f; - } - else - { - if (roll > 0.0f) - { - roll -= 0.5f; - } - else if (roll < 0.0f) - { - roll += 0.5f; - } - } - - // Tranformation matrix for rotations - model.Transform = MatrixRotateXYZ(new Vector3(DEG2RAD * pitch, DEG2RAD * yaw, DEG2RAD * roll)); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // Draw 3D model (recomended to draw 3D always before 2D) - BeginMode3D(camera); - - // Draw 3d model with texture - DrawModel(model, new Vector3(0.0f, -8.0f, 0.0f), 1.0f, Color.White); - DrawGrid(10, 10.0f); - - EndMode3D(); - - // Draw controls info - DrawRectangle(30, 370, 260, 70, Fade(Color.Green, 0.5f)); - DrawRectangleLines(30, 370, 260, 70, Fade(Color.DarkGreen, 0.5f)); - DrawText("Pitch controlled with: KEY_UP / KEY_DOWN", 40, 380, 10, Color.DarkGray); - DrawText("Roll controlled with: KEY_LEFT / KEY_RIGHT", 40, 400, 10, Color.DarkGray); - DrawText("Yaw controlled with: KEY_A / KEY_S", 40, 420, 10, Color.DarkGray); - - DrawText( - "(c) WWI Plane Model created by GiaHanLam", - screenWidth - 240, - screenHeight - 20, - 10, - Color.DarkGray - ); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadModel(model); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Program.cs b/Examples/Program.cs index ca374bf..f68b81e 100644 --- a/Examples/Program.cs +++ b/Examples/Program.cs @@ -1,195 +1,81 @@ +#nullable enable using System.Diagnostics; -using Examples.Core; -using Examples.Shapes; -using Examples.Textures; -using Examples.Text; -using Examples.Models; -using Examples.Shaders; -using Examples.Audio; namespace Examples; -public class ExampleInfo +internal static class Program { - public ExampleInfo(string name, Func main) - { - this.Name = name; - this.Main = main; - } + private const int screenWidth = 800; + private const int screenHeight = 450; - public string Name - { - get; set; - } - - public Func Main - { - get; set; - } -} - -public class ExampleList -{ - public static ExampleInfo[] AllExamples = new[] - { - // Core - new ExampleInfo("DeltaTime", DeltaTime.Main), - new ExampleInfo("InputGesturesTestBed", InputGesturesTestBed.Main), - new ExampleInfo("InputVirtualControls", InputVirtualControls.Main), - new ExampleInfo("Camera2dPlatformer", Camera2dPlatformer.Main), - new ExampleInfo("Camera2dDemo", Camera2dDemo.Main), - new ExampleInfo("Camera3dFirstPerson", Camera3dFirstPerson.Main), - new ExampleInfo("Camera3dFree", Camera3dFree.Main), - new ExampleInfo("Camera3dMode", Camera3dMode.Main), - new ExampleInfo("Picking3d", Picking3d.Main), - new ExampleInfo("BasicScreenManager", BasicScreenManager.Main), - new ExampleInfo("BasicWindow", BasicWindow.Main), - new ExampleInfo("CustomLogging", CustomLogging.Main), - new ExampleInfo("DropFiles", DropFiles.Main), - new ExampleInfo("InputGamepad", InputGamepad.Main), - new ExampleInfo("InputGestures", InputGestures.Main), - new ExampleInfo("InputKeys", InputKeys.Main), - new ExampleInfo("InputMouseWheel", InputMouseWheel.Main), - new ExampleInfo("InputMouse", InputMouse.Main), - new ExampleInfo("InputMultitouch", InputMultitouch.Main), - new ExampleInfo("RandomValues", RandomValues.Main), - new ExampleInfo("ScissorTest", ScissorTest.Main), - new ExampleInfo("SmoothPixelPerfect", SmoothPixelPerfect.Main), - new ExampleInfo("SplitScreen", SplitScreen.Main), - new ExampleInfo("StorageValues", StorageValues.Main), - new ExampleInfo("VrSimulator", VrSimulator.Main), - new ExampleInfo("WindowFlags", WindowFlags.Main), - new ExampleInfo("WindowLetterbox", WindowLetterbox.Main), - new ExampleInfo("WorldScreen", WorldScreen.Main), - - // Shapes - new ExampleInfo("BasicShapes", BasicShapes.Main), - new ExampleInfo("BouncingBall", BouncingBall.Main), - new ExampleInfo("CollisionArea", CollisionArea.Main), - new ExampleInfo("ColorsPalette", ColorsPalette.Main), - new ExampleInfo("EasingsBallAnim", EasingsBallAnim.Main), - new ExampleInfo("EasingsBoxAnim", EasingsBoxAnim.Main), - new ExampleInfo("EasingsRectangleArray", EasingsRectangleArray.Main), - new ExampleInfo("FollowingEyes", FollowingEyes.Main), - new ExampleInfo("LinesBezier", LinesBezier.Main), - new ExampleInfo("LogoRaylibAnim", LogoRaylibAnim.Main), - new ExampleInfo("LogoRaylibShape", LogoRaylibShape.Main), - new ExampleInfo("RectangleScaling", RectangleScaling.Main), - - // Textures - new ExampleInfo("BackgroundScrolling", BackgroundScrolling.Main), - new ExampleInfo("BlendModes", BlendModes.Main), - new ExampleInfo("Bunnymark", Bunnymark.Main), - new ExampleInfo("DrawTiled", DrawTiled.Main), - new ExampleInfo("ImageDrawing", ImageDrawing.Main), - new ExampleInfo("ImageGeneration", ImageGeneration.Main), - new ExampleInfo("ImageLoading", ImageLoading.Main), - new ExampleInfo("ImageProcessing", ImageProcessing.Main), - new ExampleInfo("ImageText", ImageText.Main), - new ExampleInfo("LogoRaylibTexture", LogoRaylibTexture.Main), - new ExampleInfo("MousePainting", MousePainting.Main), - new ExampleInfo("NpatchDrawing", NpatchDrawing.Main), - new ExampleInfo("ParticlesBlending", ParticlesBlending.Main), - new ExampleInfo("TexturedCurve", TexturedCurve.Main), - new ExampleInfo("Polygon", Polygon.Main), - new ExampleInfo("RawData", RawData.Main), - new ExampleInfo("SpriteAnim", SpriteAnim.Main), - new ExampleInfo("SpriteButton", SpriteButton.Main), - new ExampleInfo("SpriteExplosion", SpriteExplosion.Main), - new ExampleInfo("SrcRecDstRec", SrcRecDstRec.Main), - new ExampleInfo("ToImage", ToImage.Main), - - // Text - new ExampleInfo("CodepointsLoading", CodepointsLoading.Main), - new ExampleInfo("FontFilters", FontFilters.Main), - new ExampleInfo("FontLoading", FontLoading.Main), - new ExampleInfo("FontSdf", FontSdf.Main), - new ExampleInfo("FontSpritefont", FontSpritefont.Main), - new ExampleInfo("FormatText", FormatText.Main), - new ExampleInfo("InputBox", InputBox.Main), - new ExampleInfo("RaylibFonts", RaylibFonts.Main), - new ExampleInfo("RectangleBounds", RectangleBounds.Main), - new ExampleInfo("WritingAnim", WritingAnim.Main), - - // Models - new ExampleInfo("LoadingIqm", LoadingIqm.Main), - new ExampleInfo("LoadingGltf", LoadingGltf.Main), - new ExampleInfo("BillboardDemo", BillboardDemo.Main), - new ExampleInfo("BoxCollisions", BoxCollisions.Main), - new ExampleInfo("CubicmapDemo", CubicmapDemo.Main), - new ExampleInfo("ModelCubeTexture", ModelCubeTexture.Main), - new ExampleInfo("FirstPersonMaze", FirstPersonMaze.Main), - new ExampleInfo("GeometricShapes", GeometricShapes.Main), - new ExampleInfo("HeightmapDemo", HeightmapDemo.Main), - new ExampleInfo("MeshDemo", MeshDemo.Main), - new ExampleInfo("ModelLoading", ModelLoading.Main), - new ExampleInfo("MeshGeneration", MeshGeneration.Main), - new ExampleInfo("MeshPicking", MeshPicking.Main), - new ExampleInfo("OrthographicProjection", OrthographicProjection.Main), - new ExampleInfo("SolarSystem", SolarSystem.Main), - new ExampleInfo("SkyboxDemo", SkyboxDemo.Main), - new ExampleInfo("WavingCubes", WavingCubes.Main), - new ExampleInfo("YawPitchRoll", YawPitchRoll.Main), - new ExampleInfo("DynamicMesh", DynamicMesh.Main), - - // Shaders - new ExampleInfo("BasicLighting", BasicLighting.Main), - new ExampleInfo("BasicPbr", BasicPbr.Main), - new ExampleInfo("CustomUniform", CustomUniform.Main), - new ExampleInfo("Eratosthenes", Eratosthenes.Main), - new ExampleInfo("Fog", Fog.Main), - new ExampleInfo("HotReloading", HotReloading.Main), - new ExampleInfo("HybridRender", HybridRender.Main), - new ExampleInfo("JuliaSet", JuliaSet.Main), - new ExampleInfo("ModelShader", ModelShader.Main), - new ExampleInfo("MultiSample2d", MultiSample2d.Main), - new ExampleInfo("PaletteSwitch", PaletteSwitch.Main), - new ExampleInfo("PostProcessing", PostProcessing.Main), - new ExampleInfo("Raymarching", Raymarching.Main), - new ExampleInfo("MeshInstancing", MeshInstancing.Main), - new ExampleInfo("ShapesTextures", ShapesTextures.Main), - new ExampleInfo("SimpleMask", SimpleMask.Main), - new ExampleInfo("Spotlight", Spotlight.Main), - new ExampleInfo("TextureDrawing", TextureDrawing.Main), - new ExampleInfo("TextureOutline", TextureOutline.Main), - new ExampleInfo("TextureWaves", TextureWaves.Main), - new ExampleInfo("WriteDepth", WriteDepth.Main), - - // Audio - new ExampleInfo("ModulePlaying", ModulePlaying.Main), - new ExampleInfo("MusicStreamDemo", MusicStreamDemo.Main), - new ExampleInfo("SoundLoading", SoundLoading.Main), - }; - - public static ExampleInfo GetExample(string name) - { - var example = Array.Find(ExampleList.AllExamples, x => - x.Name.Equals(name, StringComparison.OrdinalIgnoreCase) - ); - return example; - } -} - -class Program -{ - static unsafe void Main(string[] args) + private static unsafe void Main(string[] args) { Raylib.SetTraceLogCallback(&Logging.LogConsole); if (args.Length > 0) { - var example = ExampleList.GetExample(args[0]); - example?.Main?.Invoke(); + var example = GetExample(args[0]); + if (example == null) + { + Console.WriteLine($"Unknown example: {args[0]}"); + return; + } + + RunExample(example); return; } - foreach (var example in ExampleList.AllExamples) + foreach (var example in ExampleRegistry.DesktopExamples) { - RunExampleProcess(Environment.ProcessPath, example.Name); + RunExampleProcess(Environment.ProcessPath!, example.GetType().Name); } } - static void RunExampleProcess( + private static IExample? GetExample(string name) + { + return Array.Find(ExampleRegistry.DesktopExamples, x => + x.GetType().Name.Equals(name, StringComparison.OrdinalIgnoreCase) || + x.Name.Equals(name, StringComparison.OrdinalIgnoreCase) + ); + } + + /// + /// Drives an example the same way its standalone Main() does: window setup from the + /// example's properties, then Init/Update/Unload around a blocking frame loop. + /// + private static void RunExample(IExample example) + { + if (example.ConfigFlags != 0) + { + SetConfigFlags(example.ConfigFlags); + } + + InitWindow(screenWidth, screenHeight, example.Title); + + if (example.CursorDisabled) + { + DisableCursor(); + } + else if (example.CursorHidden) + { + HideCursor(); + } + + SetTargetFPS(example.TargetFps); + + example.Init(); + + while (!WindowShouldClose()) + { + example.Update(); + } + + example.Unload(); + + CloseWindow(); + } + + private static void RunExampleProcess( string fileName, string exampleName ) diff --git a/Examples/Shaders/BasicLighting.cs b/Examples/Shaders/BasicLighting.cs index dd98681..13ef4e0 100644 --- a/Examples/Shaders/BasicLighting.cs +++ b/Examples/Shaders/BasicLighting.cs @@ -2,26 +2,21 @@ * * raylib [shaders] example - basic lighting * +* Example complexity rating: [★★★★] 4/4 +* * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * -* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3). +* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3) * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 3.0, last time updated with raylib 4.2 * -* Example contributed by Chris Camacho (@codifies) and reviewed by Ramon Santamaria (@raysan5) +* Example contributed by Chris Camacho (@chriscamacho) and reviewed by Ramon Santamaria (@raysan5) * -* Chris Camacho (@codifies - http://bedroomcoders.co.uk/) notes: +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software * -* This is based on the PBR lighting example, but greatly simplified to aid learning... -* actually there is very little of the PBR example left! -* When I first looked at the bewildering complexity of the PBR example I feared -* I would never understand how I could do simple lighting with raylib however its -* a testement to the authors of raylib (including rlights.h) that the example -* came together fairly quickly. -* -* Copyright (c) 2019 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2025 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -31,52 +26,55 @@ using Examples.Shared; namespace Examples.Shaders; -public class BasicLighting +public class BasicLighting : IExample { - const int GLSL_VERSION = 330; + private const int screenWidth = 800; + private const int screenHeight = 450; - public unsafe static int Main() +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + public string Name => "Shaders / Basic Lighting"; + + public string Title => "raylib [shaders] example - basic lighting"; + + public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint; + + private Camera3D camera; + private Shader shader; + private Light[] lights; + + public unsafe void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - // Enable Multi Sampling Anti Aliasing 4x (if available) - SetConfigFlags(ConfigFlags.Msaa4xHint); - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic lighting"); - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(2.0f, 4.0f, 6.0f); - camera.Target = new Vector3(0.0f, 0.5f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; + camera = new(); + camera.Position = new Vector3(2.0f, 4.0f, 6.0f); // Camera position + camera.Target = new Vector3(0.0f, 0.5f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type - // Load plane model from a generated mesh - Model model = LoadModelFromMesh(GenMeshPlane(10.0f, 10.0f, 3, 3)); - Model cube = LoadModelFromMesh(GenMeshCube(2.0f, 4.0f, 2.0f)); - - Shader shader = LoadShader( - "resources/shaders/glsl330/lighting.vs", - "resources/shaders/glsl330/lighting.fs" + // Load basic lighting shader + shader = LoadShader( + $"resources/shaders/glsl{GlslVersion}/lighting.vs", + $"resources/shaders/glsl{GlslVersion}/lighting.fs" ); - - // Get some required shader loactions + // Get some required shader locations shader.Locs[(int)ShaderLocationIndex.VectorView] = GetShaderLocation(shader, "viewPos"); + // NOTE: "matModel" location name is automatically assigned on shader loading, + // no need to get the location again if using that uniform name + //shader.Locs[(int)ShaderLocationIndex.MatrixModel] = GetShaderLocation(shader, "matModel"); - // ambient light level - int ambientLoc = GetShaderLocation(shader, "ambient"); - float[] ambient = new[] { 0.1f, 0.1f, 0.1f, 1.0f }; + // Ambient light level (some basic lighting) + var ambientLoc = GetShaderLocation(shader, "ambient"); + var ambient = new[] { 0.1f, 0.1f, 0.1f, 1.0f }; Raylib.SetShaderValue(shader, ambientLoc, ambient, ShaderUniformDataType.Vec4); - // Assign out lighting shader to model - model.Materials[0].Shader = shader; - cube.Materials[0].Shader = shader; - - // Using 4 point lights: Color.gold, Color.red, Color.green and Color.blue - Light[] lights = new Light[4]; + // Create lights + lights = new Light[4]; lights[0] = Rlights.CreateLight( 0, LightType.Point, @@ -109,114 +107,116 @@ public class BasicLighting Color.Blue, shader ); + } - SetTargetFPS(60); + public unsafe void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Orbital); + + // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f }) + Raylib.SetShaderValue( + shader, + shader.Locs[(int)ShaderLocationIndex.VectorView], + camera.Position, + ShaderUniformDataType.Vec3 + ); + + // Check key inputs to enable/disable lights + if (IsKeyPressed(KeyboardKey.Y)) + { + lights[0].Enabled = !lights[0].Enabled; + } + if (IsKeyPressed(KeyboardKey.R)) + { + lights[1].Enabled = !lights[1].Enabled; + } + if (IsKeyPressed(KeyboardKey.G)) + { + lights[2].Enabled = !lights[2].Enabled; + } + if (IsKeyPressed(KeyboardKey.B)) + { + lights[3].Enabled = !lights[3].Enabled; + } + + // Update light values (actually, only enable/disable them) + for (var i = 0; i < 4; i++) + { + Rlights.UpdateLightValues(shader, lights[i]); + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + BeginShaderMode(shader); + + DrawPlane(Vector3.Zero, new Vector2(10.0f, 10.0f), Color.White); + DrawCube(Vector3.Zero, 2.0f, 4.0f, 2.0f, Color.White); + + EndShaderMode(); + + // Draw spheres to show where the lights are + for (var i = 0; i < 4; i++) + { + if (lights[i].Enabled) + { + DrawSphereEx(lights[i].Position, 0.2f, 8, 8, lights[i].Color); + } + else + { + DrawSphereWires(lights[i].Position, 0.2f, 8, 8, ColorAlpha(lights[i].Color, 0.3f)); + } + } + + DrawGrid(10, 1.0f); + + EndMode3D(); + + DrawFPS(10, 10); + + DrawText("Use keys [Y][R][G][B] to toggle lights", 10, 40, 20, Color.DarkGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadShader(shader); // Unload shader + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + SetConfigFlags(ConfigFlags.Msaa4xHint); // Enable Multi Sampling Anti Aliasing 4x (if available) + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic lighting"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new BasicLighting(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Orbital); - - if (IsKeyPressed(KeyboardKey.Y)) - { - lights[0].Enabled = !lights[0].Enabled; - } - if (IsKeyPressed(KeyboardKey.R)) - { - lights[1].Enabled = !lights[1].Enabled; - } - if (IsKeyPressed(KeyboardKey.G)) - { - lights[2].Enabled = !lights[2].Enabled; - } - if (IsKeyPressed(KeyboardKey.B)) - { - lights[3].Enabled = !lights[3].Enabled; - } - - // Update light values (actually, only enable/disable them) - Rlights.UpdateLightValues(shader, lights[0]); - Rlights.UpdateLightValues(shader, lights[1]); - Rlights.UpdateLightValues(shader, lights[2]); - Rlights.UpdateLightValues(shader, lights[3]); - - // Update the light shader with the camera view position - Raylib.SetShaderValue( - shader, - shader.Locs[(int)ShaderLocationIndex.VectorView], - camera.Position, - ShaderUniformDataType.Vec3 - ); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawModel(model, Vector3.Zero, 1.0f, Color.White); - DrawModel(cube, Vector3.Zero, 1.0f, Color.White); - - // Draw markers to show where the lights are - if (lights[0].Enabled) - { - DrawSphereEx(lights[0].Position, 0.2f, 8, 8, Color.Yellow); - } - else - { - DrawSphereWires(lights[0].Position, 0.2f, 8, 8, ColorAlpha(Color.Yellow, 0.3f)); - } - - if (lights[1].Enabled) - { - DrawSphereEx(lights[1].Position, 0.2f, 8, 8, Color.Red); - } - else - { - DrawSphereWires(lights[1].Position, 0.2f, 8, 8, ColorAlpha(Color.Red, 0.3f)); - } - - if (lights[2].Enabled) - { - DrawSphereEx(lights[2].Position, 0.2f, 8, 8, Color.Green); - } - else - { - DrawSphereWires(lights[2].Position, 0.2f, 8, 8, ColorAlpha(Color.Green, 0.3f)); - } - - if (lights[3].Enabled) - { - DrawSphereEx(lights[3].Position, 0.2f, 8, 8, Color.Blue); - } - else - { - DrawSphereWires(lights[3].Position, 0.2f, 8, 8, ColorAlpha(Color.Blue, 0.3f)); - } - - DrawGrid(10, 1.0f); - - EndMode3D(); - - DrawFPS(10, 10); - DrawText("Use keys [Y][R][G][B] to toggle lights", 10, 40, 20, Color.DarkGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadModel(model); - UnloadModel(cube); - UnloadShader(shader); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shaders/BasicPbr.cs b/Examples/Shaders/BasicPbr.cs index f551bc3..e29ce26 100644 --- a/Examples/Shaders/BasicPbr.cs +++ b/Examples/Shaders/BasicPbr.cs @@ -1,21 +1,23 @@ /******************************************************************************************* - * - * raylib [shaders] example - Basic PBR - * - * Example originally created with raylib 5.0, last time updated with raylib 5.1-dev - * - * Example contributed by Afan OLOVCIC (@_DevDad) 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-2024 Afan OLOVCIC (@_DevDad) - * - * Model: "Old Rusty Car" (https://skfb.ly/LxRy) by Renafox, - * licensed under Creative Commons Attribution-NonCommercial - * (http://creativecommons.org/licenses/by-nc/4.0/) - * - ********************************************************************************************/ +* +* raylib [shaders] example - basic pbr +* +* Example complexity rating: [★★★★] 4/4 +* +* Example originally created with raylib 5.0, last time updated with raylib 5.5 +* +* Example contributed by Afan OLOVCIC (@_DevDad) 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-2025 Afan OLOVCIC (@_DevDad) +* +* Model: "Old Rusty Car" (https://skfb.ly/LxRy) by Renafox, +* licensed under Creative Commons Attribution-NonCommercial +* (http://creativecommons.org/licenses/by-nc/4.0/) +* +********************************************************************************************/ using System.Numerics; using Examples.Shared; @@ -23,31 +25,50 @@ using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class BasicPbr +public class BasicPbr : IExample { - private const int GLSL_VERSION = 330; +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif - public static unsafe int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shaders / Basic PBR"; + + public string Title => "raylib [shaders] example - basic pbr"; + + public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint; + + private Camera3D camera; + private Shader shader; + private Model car; + private Model floor; + private PbrLight[] lights; + + private int metallicValueLoc; + private int roughnessValueLoc; + private int emissiveIntensityLoc; + private int emissiveColorLoc; + private int textureTilingLoc; + + private Vector2 carTextureTiling; + private Vector2 floorTextureTiling; + + public unsafe void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - // Enable Multi Sampling Anti Aliasing 4x (if available) - SetConfigFlags(ConfigFlags.Msaa4xHint); - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic pbr"); - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(2.0f, 4.0f, 6.0f); + camera = new(); + camera.Position = new Vector3(2.0f, 2.0f, 6.0f); camera.Target = new Vector3(0.0f, 0.5f, 0.0f); camera.Up = new Vector3(0.0f, 1.0f, 0.0f); camera.FovY = 45.0f; camera.Projection = CameraProjection.Perspective; // Load PBR shader and setup all required locations - var shader = LoadShader("resources/shaders/glsl330/pbr.vs", "resources/shaders/glsl330/pbr.fs"); + shader = LoadShader($"resources/shaders/glsl{GlslVersion}/pbr.vs", $"resources/shaders/glsl{GlslVersion}/pbr.fs"); shader.Locs[(int)ShaderLocationIndex.MapAlbedo] = GetShaderLocation(shader, "albedoMap"); // WARNING: Metalness, roughness, and ambient occlusion are all packed into a MRA texture @@ -75,23 +96,25 @@ public class BasicPbr SetShaderValue(shader, GetShaderLocation(shader, "ambient"), &ambientIntensity, ShaderUniformDataType.Float); // Get location for shader parameters that can be modified in real time - var emissiveIntensityLoc = GetShaderLocation(shader, "emissivePower"); - var emissiveColorLoc = GetShaderLocation(shader, "emissiveColor"); - var textureTilingLoc = GetShaderLocation(shader, "tiling"); + metallicValueLoc = GetShaderLocation(shader, "metallicValue"); + roughnessValueLoc = GetShaderLocation(shader, "roughnessValue"); + emissiveIntensityLoc = GetShaderLocation(shader, "emissivePower"); + emissiveColorLoc = GetShaderLocation(shader, "emissiveColor"); + textureTilingLoc = GetShaderLocation(shader, "tiling"); // Load old car model using PBR maps and shader // WARNING: We know this model consists of a single model.meshes[0] and // that model.materials[0] is by default assigned to that mesh // There could be more complex models consisting of multiple meshes and // multiple materials defined for those meshes... but always 1 mesh = 1 material - var car = LoadModel("resources/models/gltf/old_car_new.glb"); + car = LoadModel("resources/models/gltf/old_car_new.glb"); // Assign already setup PBR shader to model.materials[0], used by models.meshes[0] car.Materials[0].Shader = shader; // Setup materials[0].maps default parameters car.Materials[0].Maps[(int)MaterialMapIndex.Albedo].Color = Color.White; - car.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value = 0.0f; + car.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value = 1.0f; car.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value = 0.0f; car.Materials[0].Maps[(int)MaterialMapIndex.Occlusion].Value = 1.0f; car.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color = new Color(255, 162, 0, 255); @@ -104,7 +127,7 @@ public class BasicPbr // Load floor model mesh and assign material parameters // NOTE: A basic plane shape can be generated instead of being loaded from a model file - var floor = LoadModel("resources/models/gltf/plane.glb"); + floor = LoadModel("resources/models/gltf/plane.glb"); //Mesh floorMesh = GenMeshPlane(10, 10, 10, 10); //GenMeshTangents(&floorMesh); // TODO: Review tangents generation //Model floor = LoadModelFromMesh(floorMesh); @@ -113,8 +136,8 @@ public class BasicPbr floor.Materials[0].Shader = shader; floor.Materials[0].Maps[(int)MaterialMapIndex.Albedo].Color = Color.White; - floor.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value = 0.0f; - floor.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value = 0.0f; + floor.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value = 0.8f; + floor.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value = 0.1f; floor.Materials[0].Maps[(int)MaterialMapIndex.Occlusion].Value = 1.0f; floor.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color = Color.Black; @@ -124,11 +147,11 @@ public class BasicPbr // Models texture tiling parameter can be stored in the Material struct if required (CURRENTLY NOT USED) // NOTE: Material.params[4] are available for generic parameters storage (float) - var carTextureTiling = new Vector2(0.5f, 0.5f); - var floorTextureTiling = new Vector2(0.5f, 0.5f); + carTextureTiling = new Vector2(0.5f, 0.5f); + floorTextureTiling = new Vector2(0.5f, 0.5f); // Create some lights - var lights = new PbrLight[4]; + lights = new PbrLight[4]; lights[0] = PbrLights.CreateLight( 0, PbrLightType.Point, @@ -167,104 +190,113 @@ public class BasicPbr SetShaderValue(shader, GetShaderLocation(shader, "useTexNormal"), &usage, ShaderUniformDataType.Int); SetShaderValue(shader, GetShaderLocation(shader, "useTexMRA"), &usage, ShaderUniformDataType.Int); SetShaderValue(shader, GetShaderLocation(shader, "useTexEmissive"), &usage, ShaderUniformDataType.Int); + } - SetTargetFPS(60); // Set our game to run at 60 frames-per-second - //--------------------------------------------------------------------------------------- + public unsafe void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Orbital); - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key + // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f }) + var cameraPos = camera.Position; + SetShaderValue(shader, shader.Locs[(int)ShaderLocationIndex.VectorView], cameraPos, ShaderUniformDataType.Vec3); + + // Check key inputs to enable/disable lights + if (IsKeyPressed(KeyboardKey.One)) { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(&camera, CameraMode.Orbital); - - // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f }) - var cameraPos = camera.Position; - SetShaderValue(shader, shader.Locs[(int)ShaderLocationIndex.VectorView], cameraPos, ShaderUniformDataType.Vec3); - - // Check key inputs to enable/disable lights - if (IsKeyPressed(KeyboardKey.One)) - { - lights[2].Enabled = !lights[2].Enabled; - } - - if (IsKeyPressed(KeyboardKey.Two)) - { - lights[1].Enabled = !lights[1].Enabled; - } - - if (IsKeyPressed(KeyboardKey.Three)) - { - lights[3].Enabled = !lights[3].Enabled; - } - - if (IsKeyPressed(KeyboardKey.Four)) - { - lights[0].Enabled = !lights[0].Enabled; - } - - // Update light values on shader (actually, only enable/disable them) - for (var i = 0; i < 4; i++) - { - UpdateLight(shader, lights[i]); - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(Color.Black); - - BeginMode3D(camera); - - // Set floor model texture tiling and emissive color parameters on shader - SetShaderValue(shader, textureTilingLoc, &floorTextureTiling, ShaderUniformDataType.Vec2); - var floorEmissiveColor = ColorNormalize(floor.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color); - SetShaderValue(shader, emissiveColorLoc, &floorEmissiveColor, ShaderUniformDataType.Vec4); - - DrawModel(floor, Vector3.Zero, 5.0f, Color.White); // Draw floor model - - // Set old car model texture tiling, emissive color and emissive intensity parameters on shader - SetShaderValue(shader, textureTilingLoc, &carTextureTiling, ShaderUniformDataType.Vec2); - var carEmissiveColor = ColorNormalize(car.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color); - SetShaderValue(shader, emissiveColorLoc, &carEmissiveColor, ShaderUniformDataType.Vec4); - var emissiveIntensity = 0.01f; - SetShaderValue(shader, emissiveIntensityLoc, &emissiveIntensity, ShaderUniformDataType.Float); - - DrawModel(car, Vector3.Zero, 0.25f, Color.White); // Draw car model - - // Draw spheres to show the lights positions - for (var i = 0; i < 4; i++) - { - var color = lights[i].Color; - var lightColor = new Color((byte)(color.X * 255), (byte)(color.Y * 255), (byte)(color.Z * 255), - (byte)(color.W * 255)); - - if (lights[i].Enabled) - { - DrawSphereEx(lights[i].Position, 0.2f, 8, 8, lightColor); - } - else - { - DrawSphereWires(lights[i].Position, 0.2f, 8, 8, ColorAlpha(lightColor, 0.3f)); - } - } - - EndMode3D(); - - DrawText("Toggle lights: [1][2][3][4]", 10, 40, 20, Color.LightGray); - - DrawText("(c) Old Rusty Car model by Renafox (https://skfb.ly/LxRy)", screenWidth - 320, screenHeight - 20, 10, Color.LightGray); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + lights[2].Enabled = !lights[2].Enabled; } - // De-Initialization - //-------------------------------------------------------------------------------------- + if (IsKeyPressed(KeyboardKey.Two)) + { + lights[1].Enabled = !lights[1].Enabled; + } + + if (IsKeyPressed(KeyboardKey.Three)) + { + lights[3].Enabled = !lights[3].Enabled; + } + + if (IsKeyPressed(KeyboardKey.Four)) + { + lights[0].Enabled = !lights[0].Enabled; + } + + // Update light values on shader (actually, only enable/disable them) + for (var i = 0; i < 4; i++) + { + UpdateLight(shader, lights[i]); + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(Color.Black); + + BeginMode3D(camera); + + // Set floor model texture tiling and emissive color parameters on shader + SetShaderValue(shader, textureTilingLoc, floorTextureTiling, ShaderUniformDataType.Vec2); + var floorEmissiveColor = ColorNormalize(floor.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color); + SetShaderValue(shader, emissiveColorLoc, &floorEmissiveColor, ShaderUniformDataType.Vec4); + + // Set floor metallic and roughness values + var floorMetallicValue = floor.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value; + SetShaderValue(shader, metallicValueLoc, &floorMetallicValue, ShaderUniformDataType.Float); + var floorRoughnessValue = floor.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value; + SetShaderValue(shader, roughnessValueLoc, &floorRoughnessValue, ShaderUniformDataType.Float); + + DrawModel(floor, Vector3.Zero, 5.0f, Color.White); // Draw floor model + + // Set old car model texture tiling, emissive color and emissive intensity parameters on shader + SetShaderValue(shader, textureTilingLoc, carTextureTiling, ShaderUniformDataType.Vec2); + var carEmissiveColor = ColorNormalize(car.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color); + SetShaderValue(shader, emissiveColorLoc, &carEmissiveColor, ShaderUniformDataType.Vec4); + var emissiveIntensity = 0.01f; + SetShaderValue(shader, emissiveIntensityLoc, &emissiveIntensity, ShaderUniformDataType.Float); + + // Set old car metallic and roughness values + var carMetallicValue = car.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value; + SetShaderValue(shader, metallicValueLoc, &carMetallicValue, ShaderUniformDataType.Float); + var carRoughnessValue = car.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value; + SetShaderValue(shader, roughnessValueLoc, &carRoughnessValue, ShaderUniformDataType.Float); + + DrawModel(car, Vector3.Zero, 0.25f, Color.White); // Draw car model + + // Draw spheres to show the lights positions + for (var i = 0; i < 4; i++) + { + var color = lights[i].Color; + var lightColor = new Color((byte)(color.X * 255), (byte)(color.Y * 255), (byte)(color.Z * 255), + (byte)(color.W * 255)); + + if (lights[i].Enabled) + { + DrawSphereEx(lights[i].Position, 0.2f, 8, 8, lightColor); + } + else + { + DrawSphereWires(lights[i].Position, 0.2f, 8, 8, ColorAlpha(lightColor, 0.3f)); + } + } + + EndMode3D(); + + DrawText("Toggle lights: [1][2][3][4]", 10, 40, 20, Color.LightGray); + + DrawText("(c) Old Rusty Car model by Renafox (https://skfb.ly/LxRy)", screenWidth - 320, screenHeight - 20, 10, Color.LightGray); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public unsafe void Unload() + { // Unbind (disconnect) shader from car.material[0] // to avoid UnloadMaterial() trying to unload it automatically car.Materials[0].Shader = new(); @@ -278,7 +310,32 @@ public class BasicPbr UnloadModel(floor); UnloadShader(shader); + } + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + // Enable Multi Sampling Anti Aliasing 4x (if available) + SetConfigFlags(ConfigFlags.Msaa4xHint); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic pbr"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //--------------------------------------------------------------------------------------- + + var game = new BasicPbr(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + game.Update(); + } + + game.Unload(); + + // De-Initialization + //-------------------------------------------------------------------------------------- CloseWindow(); //-------------------------------------------------------------------------------------- @@ -287,8 +344,8 @@ public class BasicPbr private static void UpdateLight(Shader shader, PbrLight light) { - SetShaderValue(shader, light.EnabledLoc, light.Enabled, ShaderUniformDataType.Int); - SetShaderValue(shader, light.TypeLoc, light.Type, ShaderUniformDataType.Int); + SetShaderValue(shader, light.EnabledLoc, light.Enabled ? 1 : 0, ShaderUniformDataType.Int); + SetShaderValue(shader, light.TypeLoc, (int)light.Type, ShaderUniformDataType.Int); // Send to shader light position values SetShaderValue(shader, light.PositionLoc, light.Position, ShaderUniformDataType.Vec3); diff --git a/Examples/Shaders/CustomUniform.cs b/Examples/Shaders/CustomUniform.cs index 3149df1..44b3159 100644 --- a/Examples/Shaders/CustomUniform.cs +++ b/Examples/Shaders/CustomUniform.cs @@ -1,18 +1,22 @@ /******************************************************************************************* * -* raylib [shaders] example - Apply a postprocessing shader and connect a custom uniform variable +* raylib [shaders] example - custom uniform +* +* Example complexity rating: [★★☆☆] 2/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example * on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders * raylib comes with shaders ready for both versions, check raylib/shaders install folder * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.3, last time updated with raylib 4.0 * -* Copyright (c) 2015 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -21,123 +25,156 @@ using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class CustomUniform +public class CustomUniform : IExample { - public static int Main() +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shaders / Custom Uniform"; + + public string Title => "raylib [shaders] example - custom uniform"; + + public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint; + + private Camera3D camera; + private Model model; + private Texture2D texture; + private Vector3 position; + private Shader shader; + private int swirlCenterLoc; + private float[] swirlCenter; + private RenderTexture2D target; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - // Enable Multi Sampling Anti Aliasing 4x (if available) - SetConfigFlags(ConfigFlags.Msaa4xHint); - - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - custom uniform variable"); - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(8.0f, 8.0f, 8.0f); - camera.Target = new Vector3(0.0f, 1.5f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; + camera = new(); + camera.Position = new Vector3(8.0f, 8.0f, 8.0f); // Camera position + camera.Target = new Vector3(0.0f, 1.5f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type - Model model = LoadModel("resources/models/obj/barracks.obj"); - Texture2D texture = LoadTexture("resources/models/obj/barracks_diffuse.png"); + model = LoadModel("resources/models/obj/barracks.obj"); // Load OBJ model + texture = LoadTexture("resources/models/obj/barracks_diffuse.png"); // Load model texture (diffuse map) // Set model diffuse texture Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture); - Vector3 position = new(0.0f, 0.0f, 0.0f); + position = new(0.0f, 0.0f, 0.0f); // Set model position - // Load postpro shader - Shader shader = LoadShader("resources/shaders/glsl330/base.vs", - "resources/shaders/glsl330/swirl.fs"); + // Load postprocessing shader + // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader + shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/swirl.fs"); // Get variable (uniform) location on the shader to connect with the program // NOTE: If uniform variable could not be found in the shader, function returns -1 - int swirlCenterLoc = GetShaderLocation(shader, "center"); + swirlCenterLoc = GetShaderLocation(shader, "center"); - float[] swirlCenter = new float[2] { (float)screenWidth / 2, (float)screenHeight / 2 }; + swirlCenter = new float[2] { (float)screenWidth / 2, (float)screenHeight / 2 }; // Create a RenderTexture2D to be used for render to texture - RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); + target = LoadRenderTexture(screenWidth, screenHeight); + } - SetTargetFPS(60); + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Orbital); + + var mousePosition = GetMousePosition(); + + swirlCenter[0] = mousePosition.X; + swirlCenter[1] = screenHeight - mousePosition.Y; + + // Send new value to the shader to be used on drawing + Raylib.SetShaderValue(shader, swirlCenterLoc, swirlCenter, ShaderUniformDataType.Vec2); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginTextureMode(target); // Enable drawing to texture + ClearBackground(Color.RayWhite); // Clear texture background + + BeginMode3D(camera); // Begin 3d mode drawing + DrawModel(model, position, 0.5f, Color.White); // Draw 3d model with texture + DrawGrid(10, 1.0f); // Draw a grid + EndMode3D(); // End 3d mode drawing, returns to orthographic 2d mode + + DrawText("TEXT DRAWN IN RENDER TEXTURE", 200, 10, 30, Color.Red); + EndTextureMode(); // End drawing to texture (now we have a texture available for next passes) + + BeginDrawing(); + ClearBackground(Color.RayWhite); // Clear screen background + + // Enable shader using the custom uniform + BeginShaderMode(shader); + + // NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom) + DrawTextureRec( + target.Texture, + new Rectangle(0, 0, target.Texture.Width, -target.Texture.Height), + new Vector2(0, 0), + Color.White + ); + + EndShaderMode(); + + // Draw some 2d text over drawn texture + DrawText( + "(c) Barracks 3D model by Alberto Cano", + screenWidth - 220, + screenHeight - 20, + 10, + Color.Gray + ); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadShader(shader); // Unload shader + UnloadTexture(texture); // Unload texture + UnloadModel(model); // Unload model + UnloadRenderTexture(target); // Unload render texture + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + SetConfigFlags(ConfigFlags.Msaa4xHint); // Enable Multi Sampling Anti Aliasing 4x (if available) + + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - custom uniform"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new CustomUniform(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - Vector2 mousePosition = GetMousePosition(); - - swirlCenter[0] = mousePosition.X; - swirlCenter[1] = screenHeight - mousePosition.Y; - - // Send new value to the shader to be used on drawing - Raylib.SetShaderValue(shader, swirlCenterLoc, swirlCenter, ShaderUniformDataType.Vec2); - - UpdateCamera(ref camera, CameraMode.Orbital); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // Enable drawing to texture - BeginTextureMode(target); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawModel(model, position, 0.5f, Color.White); - DrawGrid(10, 1.0f); - - EndMode3D(); - - DrawText("TEXT DRAWN IN RENDER TEXTURE", 200, 10, 30, Color.Red); - - // End drawing to texture (now we have a texture available for next passes) - EndTextureMode(); - - BeginShaderMode(shader); - - // NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom) - DrawTextureRec( - target.Texture, - new Rectangle(0, 0, target.Texture.Width, -target.Texture.Height), - new Vector2(0, 0), - Color.White - ); - - EndShaderMode(); - - DrawText( - "(c) Barracks 3D model by Alberto Cano", - screenWidth - 220, - screenHeight - 20, - 10, - Color.Gray - ); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadShader(shader); - UnloadTexture(texture); - UnloadModel(model); - UnloadRenderTexture(target); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shaders/Eratosthenes.cs b/Examples/Shaders/Eratosthenes.cs index 73d1cf4..d97ed6d 100644 --- a/Examples/Shaders/Eratosthenes.cs +++ b/Examples/Shaders/Eratosthenes.cs @@ -1,25 +1,29 @@ /******************************************************************************************* * -* raylib [shaders] example - Sieve of Eratosthenes +* raylib [shaders] example - eratosthenes sieve * -* Sieve of Eratosthenes, the earliest known (ancient Greek) prime number sieve. +* Example complexity rating: [★★★☆] 3/4 * -* "Sift the twos and sift the threes, -* The Sieve of Eratosthenes. -* When the multiples sublime, -* the numbers that are left are prime." +* NOTE: Sieve of Eratosthenes, the earliest known (ancient Greek) prime number sieve +* +* "Sift the twos and sift the threes, +* The Sieve of Eratosthenes. +* When the multiples sublime, +* the numbers that are left are prime." * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * -* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3). +* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3) * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 2.5, last time updated with raylib 4.0 * -* Example contributed by ProfJski and reviewed by Ramon Santamaria (@raysan5) +* Example contributed by ProfJski (@ProfJski) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2019 ProfJski and 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) 2019-2025 ProfJski (@ProfJski) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -28,73 +32,97 @@ using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class Eratosthenes +public class Eratosthenes : IExample { - const int GlslVersion = 330; +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shaders / Eratosthenes"; + + public string Title => "raylib [shaders] example - eratosthenes sieve"; + + private RenderTexture2D target; + private Shader shader; + + public void Init() + { + target = LoadRenderTexture(screenWidth, screenHeight); + + // Load Eratosthenes shader + // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader + shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/eratosthenes.fs"); + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + // Nothing to do here, everything is happening in the shader + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginTextureMode(target); // Enable drawing to texture + ClearBackground(Color.Black); // Clear the render texture + + // Draw a rectangle in shader mode to be used as shader canvas + // NOTE: Rectangle uses font white character texture coordinates, + // so shader can not be applied here directly because input vertexTexCoord + // do not represent full screen coordinates (space where want to apply shader) + DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Color.Black); + EndTextureMode(); // End drawing to texture (now we have a blank texture available for the shader) + + BeginDrawing(); + ClearBackground(Color.RayWhite); // Clear screen background + + BeginShaderMode(shader); + // NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom) + DrawTextureRec( + target.Texture, + new Rectangle(0, 0, target.Texture.Width, -target.Texture.Height), + new Vector2(0.0f, 0.0f), + Color.White + ); + EndShaderMode(); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadShader(shader); + UnloadRenderTexture(target); + } public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - Sieve of Eratosthenes"); - - RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); - - // Load Eratosthenes shader - // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader - Shader shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/eratosthenes.fs"); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - eratosthenes sieve"); SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new Eratosthenes(); + game.Init(); + // Main game loop while (!WindowShouldClose()) { - // Update - //---------------------------------------------------------------------------------- - // Nothing to do here, everything is happening in the shader - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // Enable drawing to texture - BeginTextureMode(target); - ClearBackground(Color.Black); - - // Draw a rectangle in shader mode to be used as shader canvas - // NOTE: Rectangle uses font white character texture coordinates, - // so shader can not be applied here directly because input vertexTexCoord - // do not represent full screen coordinates (space where want to apply shader) - DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Color.Black); - - // End drawing to texture (now we have a blank texture available for the shader) - EndTextureMode(); - - BeginShaderMode(shader); - // NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom) - DrawTextureRec( - target.Texture, - new Rectangle(0, 0, target.Texture.Width, -target.Texture.Height), - new Vector2(0.0f, 0.0f), - Color.White - ); - EndShaderMode(); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadShader(shader); - UnloadRenderTexture(target); - CloseWindow(); //-------------------------------------------------------------------------------------- diff --git a/Examples/Shaders/Fog.cs b/Examples/Shaders/Fog.cs index c94ec58..b10f3f5 100644 --- a/Examples/Shaders/Fog.cs +++ b/Examples/Shaders/Fog.cs @@ -1,27 +1,22 @@ /******************************************************************************************* * -* raylib [shaders] example - fog +* raylib [shaders] example - fog rendering +* +* Example complexity rating: [★★★☆] 3/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * -* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3). +* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3) * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 2.5, last time updated with raylib 3.7 * -* Example contributed by Chris Camacho (@codifies) and reviewed by Ramon Santamaria (@raysan5) +* Example contributed by Chris Camacho (@chriscamacho) and reviewed by Ramon Santamaria (@raysan5) * -* Chris Camacho (@codifies - http://bedroomcoders.co.uk/) notes: +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software * -* This is based on the PBR lighting example, but greatly simplified to aid learning... -* actually there is very little of the PBR example left! -* When I first looked at the bewildering complexity of the PBR example I feared -* I would never understand how I could do simple lighting with raylib however its -* a testement to the authors of raylib (including rlights.h) that the example -* came together fairly quickly. -* -* Copyright (c) 2019 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2025 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -32,21 +27,36 @@ using Examples.Shared; namespace Examples.Shaders; -public class Fog +public class Fog : IExample { - public unsafe static int Main() +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shaders / Fog"; + + public string Title => "raylib [shaders] example - fog rendering"; + + public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint; + + private Camera3D camera; + private Model modelA; + private Model modelB; + private Model modelC; + private Texture2D texture; + private Shader shader; + private int fogDensityLoc; + private float fogDensity; + + public unsafe void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - // Enable Multi Sampling Anti Aliasing 4x (if available) - SetConfigFlags(ConfigFlags.Msaa4xHint); - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - fog"); - // Define the camera to look into our 3d world - Camera3D camera = new(); + camera = new(); camera.Position = new Vector3(2.0f, 2.0f, 6.0f); camera.Target = new Vector3(0.0f, 0.5f, 0.0f); camera.Up = new Vector3(0.0f, 1.0f, 0.0f); @@ -54,10 +64,10 @@ public class Fog camera.Projection = CameraProjection.Perspective; // Load models and texture - Model modelA = LoadModelFromMesh(GenMeshTorus(0.4f, 1.0f, 16, 32)); - Model modelB = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f)); - Model modelC = LoadModelFromMesh(GenMeshSphere(0.5f, 32, 32)); - Texture2D texture = LoadTexture("resources/texel_checker.png"); + modelA = LoadModelFromMesh(GenMeshTorus(0.4f, 1.0f, 16, 32)); + modelB = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f)); + modelC = LoadModelFromMesh(GenMeshSphere(0.5f, 32, 32)); + texture = LoadTexture("resources/texel_checker.png"); // Assign texture to default model material Raylib.SetMaterialTexture(ref modelA, 0, MaterialMapIndex.Albedo, ref texture); @@ -65,12 +75,15 @@ public class Fog Raylib.SetMaterialTexture(ref modelC, 0, MaterialMapIndex.Albedo, ref texture); // Load shader and set up some uniforms - Shader shader = LoadShader("resources/shaders/glsl330/lighting.vs", "resources/shaders/glsl330/fog.fs"); + shader = LoadShader( + $"resources/shaders/glsl{GlslVersion}/lighting.vs", + $"resources/shaders/glsl{GlslVersion}/fog.fs" + ); shader.Locs[(int)ShaderLocationIndex.MatrixModel] = GetShaderLocation(shader, "matModel"); shader.Locs[(int)ShaderLocationIndex.VectorView] = GetShaderLocation(shader, "viewPos"); // Ambient light level - int ambientLoc = GetShaderLocation(shader, "ambient"); + var ambientLoc = GetShaderLocation(shader, "ambient"); Raylib.SetShaderValue( shader, ambientLoc, @@ -78,8 +91,12 @@ public class Fog ShaderUniformDataType.Vec4 ); - float fogDensity = 0.15f; - int fogDensityLoc = GetShaderLocation(shader, "fogDensity"); + var fogColor = ColorNormalize(Color.Gray); + var fogColorLoc = GetShaderLocation(shader, "fogColor"); + Raylib.SetShaderValue(shader, fogColorLoc, fogColor, ShaderUniformDataType.Vec4); + + fogDensity = 0.15f; + fogDensityLoc = GetShaderLocation(shader, "fogDensity"); Raylib.SetShaderValue(shader, fogDensityLoc, fogDensity, ShaderUniformDataType.Float); // NOTE: All models share the same shader @@ -89,90 +106,112 @@ public class Fog // Using just 1 point lights Rlights.CreateLight(0, LightType.Point, new Vector3(0, 2, 6), Vector3.Zero, Color.White, shader); + } - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- + public unsafe void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Orbital); - // Main game loop - while (!WindowShouldClose()) + if (IsKeyDown(KeyboardKey.Up)) { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Orbital); - - if (IsKeyDown(KeyboardKey.Up)) + fogDensity += 0.001f; + if (fogDensity > 1.0f) { - fogDensity += 0.001f; - if (fogDensity > 1.0f) - { - fogDensity = 1.0f; - } + fogDensity = 1.0f; } - - if (IsKeyDown(KeyboardKey.Down)) - { - fogDensity -= 0.001f; - if (fogDensity < 0.0f) - { - fogDensity = 0.0f; - } - } - - Raylib.SetShaderValue(shader, fogDensityLoc, fogDensity, ShaderUniformDataType.Float); - - // Rotate the torus - modelA.Transform = MatrixMultiply(modelA.Transform, MatrixRotateX(-0.025f)); - modelA.Transform = MatrixMultiply(modelA.Transform, MatrixRotateZ(0.012f)); - - // Update the light shader with the camera view position - Raylib.SetShaderValue( - shader, - shader.Locs[(int)ShaderLocationIndex.VectorView], - camera.Position, - ShaderUniformDataType.Vec3 - ); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.Gray); - - BeginMode3D(camera); - - // Draw the three models - DrawModel(modelA, Vector3.Zero, 1.0f, Color.White); - DrawModel(modelB, new Vector3(-2.6f, 0, 0), 1.0f, Color.White); - DrawModel(modelC, new Vector3(2.6f, 0, 0), 1.0f, Color.White); - - for (int i = -20; i < 20; i += 2) - { - DrawModel(modelA, new Vector3(i, 0, 2), 1.0f, Color.White); - } - - EndMode3D(); - - DrawText( - $"Use up/down to change fog density [{fogDensity:F2}]", - 10, - 10, - 20, - Color.RayWhite - ); - - EndDrawing(); - //---------------------------------------------------------------------------------- } - // De-Initialization - //-------------------------------------------------------------------------------------- + if (IsKeyDown(KeyboardKey.Down)) + { + fogDensity -= 0.001f; + if (fogDensity < 0.0f) + { + fogDensity = 0.0f; + } + } + + Raylib.SetShaderValue(shader, fogDensityLoc, fogDensity, ShaderUniformDataType.Float); + + // Rotate the torus + modelA.Transform = MatrixMultiply(modelA.Transform, MatrixRotateX(-0.025f)); + modelA.Transform = MatrixMultiply(modelA.Transform, MatrixRotateZ(0.012f)); + + // Update the light shader with the camera view position + Raylib.SetShaderValue( + shader, + shader.Locs[(int)ShaderLocationIndex.VectorView], + camera.Position, + ShaderUniformDataType.Vec3 + ); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.Gray); + + BeginMode3D(camera); + + // Draw the three models + DrawModel(modelA, Vector3.Zero, 1.0f, Color.White); + DrawModel(modelB, new Vector3(-2.6f, 0, 0), 1.0f, Color.White); + DrawModel(modelC, new Vector3(2.6f, 0, 0), 1.0f, Color.White); + + for (var i = -20; i < 20; i += 2) + { + DrawModel(modelA, new Vector3(i, 0, 2), 1.0f, Color.White); + } + + EndMode3D(); + + DrawText( + $"Use KEY_UP/KEY_DOWN to change fog density [{fogDensity:F2}]", + 10, + 10, + 20, + Color.RayWhite + ); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { UnloadModel(modelA); UnloadModel(modelB); UnloadModel(modelC); UnloadTexture(texture); UnloadShader(shader); + } + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + // Enable Multi Sampling Anti Aliasing 4x (if available) + SetConfigFlags(ConfigFlags.Msaa4xHint); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - fog rendering"); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + var game = new Fog(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) + { + game.Update(); + } + + game.Unload(); + + // De-Initialization + //-------------------------------------------------------------------------------------- CloseWindow(); //-------------------------------------------------------------------------------------- diff --git a/Examples/Shaders/HotReloading.cs b/Examples/Shaders/HotReloading.cs index f3d5d29..6390e68 100644 --- a/Examples/Shaders/HotReloading.cs +++ b/Examples/Shaders/HotReloading.cs @@ -1,135 +1,189 @@ /******************************************************************************************* * -* raylib [shaders] example - Hot reloading +* raylib [shaders] example - hot reloading +* +* Example complexity rating: [★★★☆] 3/4 * * NOTE: This example requires raylib OpenGL 3.3 for shaders support and only #version 330 -* is currently supported. OpenGL ES 2.0 platforms are not supported at the moment. +* is currently supported. OpenGL ES 2.0 platforms are not supported at the moment * -* This example has been created using raylib 3.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 3.0, last time updated with raylib 3.5 * -* Copyright (c) 2020 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) 2020-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ +using System; using System.Numerics; using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class HotReloading +public class HotReloading : IExample { +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shaders / Hot Reloading"; + + public string Title => "raylib [shaders] example - hot reloading"; + + private string fragShaderFileName; + private Shader shader; + private int resolutionLoc; + private int mouseLoc; + private int timeLoc; + private float[] resolution; + private float totalTime; +#if !BROWSER + private long fragShaderFileModTime; + private bool shaderAutoReloading; +#endif + + public void Init() + { + fragShaderFileName = $"resources/shaders/glsl{GlslVersion}/reload.fs"; +#if !BROWSER + fragShaderFileModTime = GetFileModTime(fragShaderFileName); +#endif + + // Load raymarching shader + // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader + shader = LoadShader(null, fragShaderFileName); + + // Get shader locations for required uniforms + resolutionLoc = GetShaderLocation(shader, "resolution"); + mouseLoc = GetShaderLocation(shader, "mouse"); + timeLoc = GetShaderLocation(shader, "time"); + + resolution = new[] { (float)screenWidth, (float)screenHeight }; + Raylib.SetShaderValue(shader, resolutionLoc, resolution, ShaderUniformDataType.Vec2); + + totalTime = 0.0f; +#if !BROWSER + shaderAutoReloading = false; +#endif + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + totalTime += GetFrameTime(); + var mouse = GetMousePosition(); + var mousePos = new[] { mouse.X, mouse.Y }; + + // Set shader required uniform values + Raylib.SetShaderValue(shader, timeLoc, totalTime, ShaderUniformDataType.Float); + Raylib.SetShaderValue(shader, mouseLoc, mousePos, ShaderUniformDataType.Vec2); + +#if !BROWSER + // Hot shader reloading + if (shaderAutoReloading || (IsMouseButtonPressed(MouseButton.Left))) + { + var currentFragShaderModTime = GetFileModTime(fragShaderFileName); + + // Check if shader file has been modified + if (currentFragShaderModTime != fragShaderFileModTime) + { + // Try reloading updated shader + var updatedShader = LoadShader(null, fragShaderFileName); + + // It was correctly loaded + if (updatedShader.Id != Rlgl.GetShaderIdDefault()) + { + UnloadShader(shader); + shader = updatedShader; + + // Get shader locations for required uniforms + resolutionLoc = GetShaderLocation(shader, "resolution"); + mouseLoc = GetShaderLocation(shader, "mouse"); + timeLoc = GetShaderLocation(shader, "time"); + + // Reset required uniforms + Raylib.SetShaderValue( + shader, + resolutionLoc, + resolution, + ShaderUniformDataType.Vec2 + ); + } + + fragShaderFileModTime = currentFragShaderModTime; + } + } + + if (IsKeyPressed(KeyboardKey.A)) + { + shaderAutoReloading = !shaderAutoReloading; + } +#endif + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + // We only draw a white full-screen rectangle, frame is generated in shader + BeginShaderMode(shader); + DrawRectangle(0, 0, screenWidth, screenHeight, Color.White); + EndShaderMode(); + +#if BROWSER + DrawText("Shader generates the frame in real time", 10, 10, 10, Color.Black); +#else + var info = $"PRESS [A] to TOGGLE SHADER AUTOLOADING: {(shaderAutoReloading ? "AUTO" : "MANUAL")}"; + DrawText(info, 10, 10, 10, shaderAutoReloading ? Color.Red : Color.Black); + if (!shaderAutoReloading) + { + DrawText("MOUSE CLICK to SHADER RE-LOADING", 10, 30, 10, Color.Black); + } + + var lastModification = DateTimeOffset.FromUnixTimeSeconds(fragShaderFileModTime).LocalDateTime.ToString(); + DrawText($"Shader last modification: {lastModification}", 10, 430, 10, Color.Black); +#endif + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadShader(shader); + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - int screenWidth = 800; - int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - hot reloading"); - string fragShaderFileName = "resources/shaders/glsl330/reload.fs"; - long fragShaderFileModTime = GetFileModTime(fragShaderFileName); - - // Load raymarching shader - // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader - Shader shader = LoadShader(null, fragShaderFileName); - - // Get shader locations for required uniforms - int resolutionLoc = GetShaderLocation(shader, "resolution"); - int mouseLoc = GetShaderLocation(shader, "mouse"); - int timeLoc = GetShaderLocation(shader, "time"); - - float[] resolution = new[] { (float)screenWidth, (float)screenHeight }; - Raylib.SetShaderValue(shader, resolutionLoc, resolution, ShaderUniformDataType.Vec2); - - float totalTime = 0.0f; - bool shaderAutoReloading = false; - SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new HotReloading(); + game.Init(); + // Main game loop while (!WindowShouldClose()) { - // Update - //---------------------------------------------------------------------------------- - totalTime += GetFrameTime(); - Vector2 mouse = GetMousePosition(); - float[] mousePos = new[] { mouse.X, mouse.Y }; - - // Set shader required uniform values - Raylib.SetShaderValue(shader, timeLoc, totalTime, ShaderUniformDataType.Float); - Raylib.SetShaderValue(shader, mouseLoc, mousePos, ShaderUniformDataType.Vec2); - - // Hot shader reloading - if (shaderAutoReloading || (IsMouseButtonPressed(MouseButton.Left))) - { - long currentFragShaderModTime = GetFileModTime(fragShaderFileName); - - // Check if shader file has been modified - if (currentFragShaderModTime != fragShaderFileModTime) - { - // Try reloading updated shader - Shader updatedShader = LoadShader(null, fragShaderFileName); - - // It was correctly loaded - if (updatedShader.Id != 0) //rlGetShaderIdDefault()) - { - UnloadShader(shader); - shader = updatedShader; - - // Get shader locations for required uniforms - resolutionLoc = GetShaderLocation(shader, "resolution"); - mouseLoc = GetShaderLocation(shader, "mouse"); - timeLoc = GetShaderLocation(shader, "time"); - - // Reset required uniforms - Raylib.SetShaderValue( - shader, - resolutionLoc, - resolution, - ShaderUniformDataType.Vec2 - ); - } - - fragShaderFileModTime = currentFragShaderModTime; - } - } - - if (IsKeyPressed(KeyboardKey.A)) - { - shaderAutoReloading = !shaderAutoReloading; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // We only draw a white full-screen rectangle, frame is generated in shader - BeginShaderMode(shader); - DrawRectangle(0, 0, screenWidth, screenHeight, Color.White); - EndShaderMode(); - - string info = $"PRESS [A] to TOGGLE SHADER AUTOLOADING: {(shaderAutoReloading ? "AUTO" : "MANUAL")}"; - DrawText(info, 10, 10, 10, shaderAutoReloading ? Color.Red : Color.Black); - if (!shaderAutoReloading) - { - DrawText("MOUSE CLICK to SHADER RE-LOADING", 10, 30, 10, Color.Black); - } - - // DrawText($"Shader last modification: ", 10, 430, 10, Color.BLACK); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadShader(shader); - CloseWindow(); //-------------------------------------------------------------------------------------- diff --git a/Examples/Shaders/HybridRender.cs b/Examples/Shaders/HybridRender.cs index e610327..4b3ed79 100644 --- a/Examples/Shaders/HybridRender.cs +++ b/Examples/Shaders/HybridRender.cs @@ -1,6 +1,8 @@ /******************************************************************************************* * -* raylib [shaders] example - Hybrid Rendering +* raylib [shaders] example - hybrid rendering +* +* Example complexity rating: [★★★★] 4/4 * * Example originally created with raylib 4.2, last time updated with raylib 4.2 * @@ -9,7 +11,7 @@ * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * -* Copyright (c) 2022-2023 Buğra Alptekin Sarı (@BugraAlptekinSari) +* Copyright (c) 2022-2025 Buğra Alptekin Sarı (@BugraAlptekinSari) * ********************************************************************************************/ @@ -19,43 +21,54 @@ using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class HybridRender +public class HybridRender : IExample { - struct RayLocs + private struct RayLocs { public int CamPos; public int CamDir; public int ScreenCenter; } - const int GLSL_VERSION = 330; +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shaders / Hybrid Render"; + + public string Title => "raylib [shaders] example - hybrid rendering"; + + private Shader shdrRaymarch; + private Shader shdrRaster; + private RayLocs marchLocs; + private RenderTexture2D target; + private Camera3D camera; + private float camDist; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - hybrid render"); - - // This shader calculates pixel depth and color using raymarch - Shader shdrRaymarch = LoadShader(null, $"resources/shaders/glsl{GLSL_VERSION}/hybrid_raymarch.fs"); + // This Shader calculates pixel depth and color using raymarch + shdrRaymarch = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/hybrid_raymarch.fs"); // This Shader is a standard rasterization fragment shader with the addition of depth writing // You are required to write depth for all shaders if one shader does it - Shader shdrRaster = LoadShader(null, $"resources/shaders/glsl{GLSL_VERSION}/hybrid_raster.fs"); + shdrRaster = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/hybrid_raster.fs"); - // Declare struct used to store camera locs - RayLocs marchLocs = new(); + // Declare Struct used to store camera locs + marchLocs = new(); - // Fill the struct with shader locs. + // Fill the struct with shader locs marchLocs.CamPos = GetShaderLocation(shdrRaymarch, "camPos"); marchLocs.CamDir = GetShaderLocation(shdrRaymarch, "camDir"); marchLocs.ScreenCenter = GetShaderLocation(shdrRaymarch, "screenCenter"); - // Transfer screenCenter position to shader. Which is used to calculate ray direction. - Vector2 screenCenter = new(screenWidth / 2, screenHeight / 2); + // Transfer screenCenter position to shader. Which is used to calculate ray direction + Vector2 screenCenter = new(screenWidth / 2.0f, screenHeight / 2.0f); SetShaderValue( shdrRaymarch, marchLocs.ScreenCenter, @@ -63,91 +76,111 @@ public class HybridRender ShaderUniformDataType.Vec2 ); - // Use customized function to create writable depth texture buffer - RenderTexture2D target = LoadRenderTextureDepthTex(screenWidth, screenHeight); + // Use Customized function to create writable depth texture buffer + target = LoadRenderTextureDepthTex(screenWidth, screenHeight); // Define the camera to look into our 3d world - Camera3D camera; - camera.Position = new Vector3(0.5f, 1.0f, 1.5f); - camera.Target = new Vector3(0.0f, 0.5f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; + camera = new(); + camera.Position = new Vector3(0.5f, 1.0f, 1.5f); // Camera position + camera.Target = new Vector3(0.0f, 0.5f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type - // Camera FOV is pre-calculated in the camera Distance. - float camDist = 1.0f / (MathF.Tan(camera.FovY * 0.5f * Raylib.DEG2RAD)); + // Camera FOV is pre-calculated in the camera distance + camDist = 1.0f / (MathF.Tan(camera.FovY * 0.5f * Raylib.DEG2RAD)); + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Orbital); + + // Update Camera Postion in the ray march shader + SetShaderValue( + shdrRaymarch, + marchLocs.CamPos, + camera.Position, + ShaderUniformDataType.Vec3 + ); + + // Update Camera Looking Vector. Vector length determines FOV + var camDir = Vector3.Normalize(camera.Target - camera.Position) * camDist; + SetShaderValue(shdrRaymarch, marchLocs.CamDir, camDir, ShaderUniformDataType.Vec3); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + // Draw into our custom render texture (framebuffer) + BeginTextureMode(target); + ClearBackground(Color.White); + + // Raymarch Scene + // Manually enable Depth Test to handle multiple rendering methods + Rlgl.EnableDepthTest(); + BeginShaderMode(shdrRaymarch); + DrawRectangleRec(new Rectangle(0, 0, screenWidth, screenHeight), Color.White); + EndShaderMode(); + + // Rasterize Scene + BeginMode3D(camera); + BeginShaderMode(shdrRaster); + DrawCubeWiresV(new Vector3(0.0f, 0.5f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Red); + DrawCubeV(new Vector3(0.0f, 0.5f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Purple); + DrawCubeWiresV(new Vector3(0.0f, 0.5f, -1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.DarkGreen); + DrawCubeV(new Vector3(0.0f, 0.5f, -1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Yellow); + DrawGrid(10, 1.0f); + EndShaderMode(); + EndMode3D(); + + EndTextureMode(); + + // Draw custom render texture + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawTextureRec( + target.Texture, + new Rectangle(0, 0, screenWidth, -screenHeight), + Vector2.Zero, + Color.White + ); + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadRenderTextureDepthTex(target); + UnloadShader(shdrRaymarch); + UnloadShader(shdrRaster); + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - hybrid rendering"); SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new HybridRender(); + game.Init(); + // Main game loop while (!WindowShouldClose()) { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Orbital); - - // Update Camera Postion in the ray march shader. - SetShaderValue( - shdrRaymarch, - marchLocs.CamPos, - camera.Position, - ShaderUniformDataType.Vec3 - ); - - // Update Camera Looking Vector. Vector length determines FOV. - Vector3 camDir = Vector3.Normalize(camera.Target - camera.Position) * camDist; - SetShaderValue(shdrRaymarch, marchLocs.CamDir, camDir, ShaderUniformDataType.Vec3); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - // Draw into our custom render texture (framebuffer) - BeginTextureMode(target); - ClearBackground(Color.White); - - // Raymarch Scene - // Manually enable Depth Test to handle multiple rendering methods. - Rlgl.EnableDepthTest(); - BeginShaderMode(shdrRaymarch); - DrawRectangleRec(new Rectangle(0, 0, screenWidth, screenHeight), Color.White); - EndShaderMode(); - - // Rasterize Scene - BeginMode3D(camera); - BeginShaderMode(shdrRaster); - DrawCubeWiresV(new Vector3(0.0f, 0.5f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Red); - DrawCubeV(new Vector3(0.0f, 0.5f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Purple); - DrawCubeWiresV(new Vector3(0.0f, 0.5f, -1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.DarkGreen); - DrawCubeV(new Vector3(0.0f, 0.5f, -1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Yellow); - DrawGrid(10, 1.0f); - EndShaderMode(); - EndMode3D(); - - EndTextureMode(); - - // Draw custom render texture - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawTextureRec( - target.Texture, - new Rectangle(0, 0, screenWidth, -screenHeight), - Vector2.Zero, - Color.White - ); - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadRenderTextureDepthTex(target); - UnloadShader(shdrRaymarch); - UnloadShader(shdrRaster); - CloseWindow(); //-------------------------------------------------------------------------------------- @@ -203,7 +236,7 @@ public class HybridRender ); // Check if fbo is complete with attachments (valid) - if (Rlgl.FramebufferComplete(target.Id)) + if (Rlgl.FramebufferComplete(target.Id) != 0) { TraceLog(TraceLogLevel.Info, $"FBO: [ID {target.Id}] Framebuffer object created successfully"); } diff --git a/Examples/Shaders/JuliaSet.cs b/Examples/Shaders/JuliaSet.cs index 5a1339a..8e146e9 100644 --- a/Examples/Shaders/JuliaSet.cs +++ b/Examples/Shaders/JuliaSet.cs @@ -1,18 +1,22 @@ /******************************************************************************************* * -* raylib [shaders] example - julia sets +* raylib [shaders] example - julia set +* +* Example complexity rating: [★★★☆] 3/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * -* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3). +* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3) * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 2.5, last time updated with raylib 4.0 * -* Example contributed by eggmund (@eggmund) and reviewed by Ramon Santamaria (@raysan5) +* Example contributed by Josh Colclough (@joshcol9232) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2019 eggmund (@eggmund) and 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) 2019-2025 Josh Colclough (@joshcol9232) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -21,12 +25,27 @@ using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class JuliaSet +public class JuliaSet : IExample { - const int GlslVersion = 330; + private const int screenWidth = 800; + private const int screenHeight = 450; + private const float zoomSpeed = 1.01f; + private const float offsetSpeedMul = 2.0f; + + private const float startingZoom = 0.75f; + +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + public string Name => "Shaders / Julia Set"; + + public string Title => "raylib [shaders] example - julia set"; // A few good julia sets - static float[][] PointsOfInterest = new float[][] { + private float[][] PointsOfInterest = new float[][] { new float[] { -0.348827f, 0.607167f }, new float[] { -0.786268f, 0.169728f }, new float[] { -0.8f, 0.156f }, @@ -35,38 +54,38 @@ public class JuliaSet new float[] { -0.70176f, -0.3842f }, }; - public static int Main() + private Shader shader; + private RenderTexture2D target; + private float[] c; + private float[] offset; + private float zoom; + private int cLoc; + private int zoomLoc; + private int offsetLoc; + private int incrementSpeed; + private bool showControls; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - const float zoomSpeed = 1.01f; - const float offsetSpeedMul = 2.0f; - - const float startingZoom = 0.75f; - - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - julia sets"); - // Load julia set shader // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader - Shader shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/julia_set.fs"); + shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/julia_set.fs"); // Create a RenderTexture2D to be used for render to texture - RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); + target = LoadRenderTexture(screenWidth, screenHeight); // c constant to use in z^2 + c - float[] c = { PointsOfInterest[0][0], PointsOfInterest[0][1] }; + c = new float[] { PointsOfInterest[0][0], PointsOfInterest[0][1] }; // Offset and zoom to draw the julia set at. (centered on screen and default size) - float[] offset = { 0, 0 }; - float zoom = startingZoom; + offset = new float[] { 0, 0 }; + zoom = startingZoom; // Get variable (uniform) locations on the shader to connect with the program // NOTE: If uniform variable could not be found in the shader, function returns -1 - int cLoc = GetShaderLocation(shader, "c"); - int zoomLoc = GetShaderLocation(shader, "zoom"); - int offsetLoc = GetShaderLocation(shader, "offset"); + cLoc = GetShaderLocation(shader, "c"); + zoomLoc = GetShaderLocation(shader, "zoom"); + offsetLoc = GetShaderLocation(shader, "offset"); // Upload the shader uniform values! Raylib.SetShaderValue(shader, cLoc, c, ShaderUniformDataType.Vec2); @@ -74,166 +93,189 @@ public class JuliaSet Raylib.SetShaderValue(shader, offsetLoc, offset, ShaderUniformDataType.Vec2); // Multiplier of speed to change c value - int incrementSpeed = 0; + incrementSpeed = 0; // Show controls - bool showControls = true; + showControls = true; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + // Press [1 - 6] to reset c to a point of interest + if (IsKeyPressed(KeyboardKey.One) || + IsKeyPressed(KeyboardKey.Two) || + IsKeyPressed(KeyboardKey.Three) || + IsKeyPressed(KeyboardKey.Four) || + IsKeyPressed(KeyboardKey.Five) || + IsKeyPressed(KeyboardKey.Six)) + { + + if (IsKeyPressed(KeyboardKey.One)) + { + c[0] = PointsOfInterest[0][0]; + c[1] = PointsOfInterest[0][1]; + } + else if (IsKeyPressed(KeyboardKey.Two)) + { + c[0] = PointsOfInterest[1][0]; + c[1] = PointsOfInterest[1][1]; + } + else if (IsKeyPressed(KeyboardKey.Three)) + { + c[0] = PointsOfInterest[2][0]; + c[1] = PointsOfInterest[2][1]; + } + else if (IsKeyPressed(KeyboardKey.Four)) + { + c[0] = PointsOfInterest[3][0]; + c[1] = PointsOfInterest[3][1]; + } + else if (IsKeyPressed(KeyboardKey.Five)) + { + c[0] = PointsOfInterest[4][0]; + c[1] = PointsOfInterest[4][1]; + } + else if (IsKeyPressed(KeyboardKey.Six)) + { + c[0] = PointsOfInterest[5][0]; + c[1] = PointsOfInterest[5][1]; + } + Raylib.SetShaderValue(shader, cLoc, c, ShaderUniformDataType.Vec2); + } + + // If "R" is pressed, reset zoom and offset + if (IsKeyPressed(KeyboardKey.R)) + { + zoom = startingZoom; + offset[0] = 0.0f; + offset[1] = 0.0f; + Raylib.SetShaderValue(shader, zoomLoc, zoom, ShaderUniformDataType.Float); + Raylib.SetShaderValue(shader, offsetLoc, offset, ShaderUniformDataType.Vec2); + } + + // Pause animation (c change) + if (IsKeyPressed(KeyboardKey.Space)) + { + incrementSpeed = 0; + } + + // Toggle whether or not to show controls + if (IsKeyPressed(KeyboardKey.F1)) + { + showControls = !showControls; + } + + if (IsKeyPressed(KeyboardKey.Right)) + { + incrementSpeed++; + } + else if (IsKeyPressed(KeyboardKey.Left)) + { + incrementSpeed--; + } + + // If either left or right button is pressed, zoom in/out + if (IsMouseButtonDown(MouseButton.Left) || IsMouseButtonDown(MouseButton.Right)) + { + if (IsMouseButtonDown(MouseButton.Left)) + { + zoom *= zoomSpeed; + } + + if (IsMouseButtonDown(MouseButton.Right)) + { + zoom *= 1.0f / zoomSpeed; + } + + var mousePos = GetMousePosition(); + var offsetVelocity = Vector2.Zero; + + offsetVelocity.X = (mousePos.X / screenWidth - 0.5f) * offsetSpeedMul / zoom; + offsetVelocity.Y = (mousePos.Y / screenHeight - 0.5f) * offsetSpeedMul / zoom; + + // Apply move velocity to camera + offset[0] += GetFrameTime() * offsetVelocity.X; + offset[1] += GetFrameTime() * offsetVelocity.Y; + + Raylib.SetShaderValue(shader, zoomLoc, zoom, ShaderUniformDataType.Float); + Raylib.SetShaderValue(shader, offsetLoc, offset, ShaderUniformDataType.Vec2); + } + + // Increment c value with time + var dc = GetFrameTime() * incrementSpeed * 0.0005f; + c[0] += dc; + c[1] += dc; + + Raylib.SetShaderValue(shader, cLoc, c, ShaderUniformDataType.Vec2); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + // Using a render texture to draw Julia set + BeginTextureMode(target); + ClearBackground(Color.Black); + + // Draw a rectangle in shader mode to be used as shader canvas + // NOTE: Rectangle uses font white character texture coordinates, + // so shader can not be applied here directly because input vertexTexCoord + // do not represent full screen coordinates (space where want to apply shader) + DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Color.Black); + EndTextureMode(); + + BeginDrawing(); + ClearBackground(Color.Black); + + // Draw the saved texture and rendered julia set with shader + // NOTE: We do not invert texture on Y, already considered inside shader + BeginShaderMode(shader); + // WARNING: If FLAG_WINDOW_HIGHDPI is enabled, HighDPI monitor scaling should be considered + // when rendering the RenderTexture2D to fit in the HighDPI scaled Window + DrawTextureEx(target.Texture, new Vector2(0.0f, 0.0f), 0.0f, 1.0f, Color.White); + EndShaderMode(); + + if (showControls) + { + DrawText("Press Mouse buttons right/left to zoom in/out and move", 10, 15, 10, Color.RayWhite); + DrawText("Press KEY_F1 to toggle these controls", 10, 30, 10, Color.RayWhite); + DrawText("Press KEYS [1 - 6] to change point of interest", 10, 45, 10, Color.RayWhite); + DrawText("Press KEY_LEFT | KEY_RIGHT to change speed", 10, 60, 10, Color.RayWhite); + DrawText("Press KEY_SPACE to stop movement animation", 10, 75, 10, Color.RayWhite); + DrawText("Press KEY_R to recenter the camera", 10, 90, 10, Color.RayWhite); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadShader(shader); + UnloadRenderTexture(target); + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - julia set"); SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new JuliaSet(); + game.Init(); + // Main game loop while (!WindowShouldClose()) { - // Update - //---------------------------------------------------------------------------------- - // Press [1 - 6] to reset c to a point of interest - if (IsKeyPressed(KeyboardKey.One) || - IsKeyPressed(KeyboardKey.Two) || - IsKeyPressed(KeyboardKey.Three) || - IsKeyPressed(KeyboardKey.Four) || - IsKeyPressed(KeyboardKey.Five) || - IsKeyPressed(KeyboardKey.Six)) - { - - if (IsKeyPressed(KeyboardKey.One)) - { - c[0] = PointsOfInterest[0][0]; - c[1] = PointsOfInterest[0][1]; - } - else if (IsKeyPressed(KeyboardKey.Two)) - { - c[0] = PointsOfInterest[1][0]; - c[1] = PointsOfInterest[1][1]; - } - else if (IsKeyPressed(KeyboardKey.Three)) - { - c[0] = PointsOfInterest[2][0]; - c[1] = PointsOfInterest[2][1]; - } - else if (IsKeyPressed(KeyboardKey.Four)) - { - c[0] = PointsOfInterest[3][0]; - c[1] = PointsOfInterest[3][1]; - } - else if (IsKeyPressed(KeyboardKey.Five)) - { - c[0] = PointsOfInterest[4][0]; - c[1] = PointsOfInterest[4][1]; - } - else if (IsKeyPressed(KeyboardKey.Six)) - { - c[0] = PointsOfInterest[5][0]; - c[1] = PointsOfInterest[5][1]; - } - Raylib.SetShaderValue(shader, cLoc, c, ShaderUniformDataType.Vec2); - } - - if (IsKeyPressed(KeyboardKey.R)) - { - zoom = startingZoom; - offset[0] = 1f; - offset[1] = 1f; - Raylib.SetShaderValue(shader, zoomLoc, zoom, ShaderUniformDataType.Float); - Raylib.SetShaderValue(shader, offsetLoc, offset, ShaderUniformDataType.Vec2); - } - - // Pause animation (c change) - if (IsKeyPressed(KeyboardKey.Space)) - { - incrementSpeed = 0; - } - - // Toggle whether or not to show controls - if (IsKeyPressed(KeyboardKey.F1)) - { - showControls = !showControls; - } - - if (IsKeyPressed(KeyboardKey.Right)) - { - incrementSpeed++; - } - else if (IsKeyPressed(KeyboardKey.Left)) - { - incrementSpeed--; - } - - // If either left or right button is pressed, zoom in/out - if (IsMouseButtonDown(MouseButton.Left) || IsMouseButtonDown(MouseButton.Right)) - { - if (IsMouseButtonDown(MouseButton.Left)) - { - zoom *= zoomSpeed; - } - - if (IsMouseButtonDown(MouseButton.Right)) - { - zoom *= 1.0f / zoomSpeed; - } - - Vector2 mousePos = GetMousePosition(); - Vector2 offsetVelocity = Vector2.Zero; - - offsetVelocity.X = (mousePos.X / screenWidth - 0.5f) * offsetSpeedMul / zoom; - offsetVelocity.Y = (mousePos.Y / screenHeight - 0.5f) * offsetSpeedMul / zoom; - - // Apply move velocity to camera - offset[0] += GetFrameTime() * offsetVelocity.X; - offset[1] += GetFrameTime() * offsetVelocity.Y; - - Raylib.SetShaderValue(shader, zoomLoc, zoom, ShaderUniformDataType.Float); - Raylib.SetShaderValue(shader, offsetLoc, offset, ShaderUniformDataType.Vec2); - } - - // Increment c value with time - float amount = GetFrameTime() * incrementSpeed * 0.0005f; - c[0] += amount; - c[1] += amount; - - Raylib.SetShaderValue(shader, cLoc, c, ShaderUniformDataType.Vec2); - - // Using a render texture to draw Julia set - // Enable drawing to texture - BeginTextureMode(target); - ClearBackground(Color.Black); - - // Draw a rectangle in shader mode to be used as shader canvas - // NOTE: Rectangle uses font Color.white character texture coordinates, - // so shader can not be applied here directly because input vertexTexCoord - // do not represent full screen coordinates (space where want to apply shader) - DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Color.Black); - EndTextureMode(); - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.Black); - - // Draw the saved texture and rendered julia set with shader - // NOTE: We do not invert texture on Y, already considered inside shader - BeginShaderMode(shader); - DrawTexture(target.Texture, 0, 0, Color.White); - EndShaderMode(); - - if (showControls) - { - DrawText("Press Mouse buttons right/left to zoom in/out and move", 10, 15, 10, Color.RayWhite); - DrawText("Press KEY_F1 to toggle these controls", 10, 30, 10, Color.RayWhite); - DrawText("Press KEYS [1 - 6] to change point of interest", 10, 45, 10, Color.RayWhite); - DrawText("Press KEY_LEFT | KEY_RIGHT to change speed", 10, 60, 10, Color.RayWhite); - DrawText("Press KEY_SPACE to pause movement animation", 10, 75, 10, Color.RayWhite); - DrawText("Press KEY_R to recenter the camera", 10, 90, 10, Color.RayWhite); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadShader(shader); - UnloadRenderTexture(target); - CloseWindow(); //-------------------------------------------------------------------------------------- diff --git a/Examples/Shaders/MeshInstancing.cs b/Examples/Shaders/MeshInstancing.cs index 20d3870..a639530 100644 --- a/Examples/Shaders/MeshInstancing.cs +++ b/Examples/Shaders/MeshInstancing.cs @@ -1,112 +1,97 @@ /******************************************************************************************* * -* raylib [shaders] example - rlgl module usage for instanced meshes +* raylib [shaders] example - mesh instancing * -* This example uses [rlgl] module funtionality (pseudo-OpenGL 1.1 style coding) +* Example complexity rating: [★★★★] 4/4 * -* This example has been created using raylib 3.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 3.7, last time updated with raylib 4.2 * -* Example contributed by @seanpringle and reviewed by Ramon Santamaria (@raysan5) +* Example contributed by seanpringle (@seanpringle) and reviewed by Max (@moliad) and Ramon Santamaria (@raysan5) * -* Copyright (c) 2020 @seanpringle +* 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) 2020-2025 seanpringle (@seanpringle), Max (@moliad) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ +using System; using System.Numerics; using static Raylib_cs.Raylib; using Examples.Shared; namespace Examples.Shaders; -public class MeshInstancing +public class MeshInstancing : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + private const int MaxInstances = 10000; + + public string Name => "Shaders / Mesh Instancing"; + + public string Title => "raylib [shaders] example - mesh instancing"; + + private Camera3D camera; + private Mesh cube; + private Matrix4x4[] transforms; + private Shader shader; + private Material matInstances; + private Material matDefault; + + public unsafe void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - const int fps = 60; - - // Enable Multi Sampling Anti Aliasing 4x (if available) - SetConfigFlags(ConfigFlags.Msaa4xHint); - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - rlgl mesh instanced"); - - // Speed of jump animation - int speed = 30; - // Count of separate groups jumping around - int groups = 2; - // Maximum amplitude of jump - float amp = 10; - // Global variance in jump height - float variance = 0.8f; - // Individual cube's computed loop timer - float loop = 0.0f; - - // Used for various 3D coordinate & vector ops - float x = 0.0f; - float y = 0.0f; - float z = 0.0f; - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(-125.0f, 125.0f, -125.0f); - camera.Target = new Vector3(0.0f, 0.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; + camera = new(); + camera.Position = new Vector3(-125.0f, 125.0f, -125.0f); // Camera position + camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type - // Number of instances to display - const int instances = 10000; - Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f); + // Define mesh to be instanced + cube = GenMeshCube(1.0f, 1.0f, 1.0f); - // Rotation state of instances - Matrix4x4[] rotations = new Matrix4x4[instances]; - // Per-frame rotation animation of instances - Matrix4x4[] rotationsInc = new Matrix4x4[instances]; - // Locations of instances - Matrix4x4[] translations = new Matrix4x4[instances]; + // Define transforms to be uploaded to GPU for instances + transforms = new Matrix4x4[MaxInstances]; // Pre-multiplied transformations passed to rlgl - // Scatter random cubes around - for (int i = 0; i < instances; i++) + // Translate and rotate cubes randomly + for (var i = 0; i < MaxInstances; i++) { - x = GetRandomValue(-50, 50); - y = GetRandomValue(-50, 50); - z = GetRandomValue(-50, 50); - translations[i] = Matrix4x4.CreateTranslation(x, y, z); - - x = GetRandomValue(0, 360); - y = GetRandomValue(0, 360); - z = GetRandomValue(0, 360); - Vector3 axis = Vector3.Normalize(new Vector3(x, y, z)); - float angle = (float)GetRandomValue(0, 10) * DEG2RAD; - - rotationsInc[i] = Matrix4x4.CreateFromAxisAngle(axis, angle); - rotations[i] = Matrix4x4.Identity; - } - - // Pre-multiplied transformations passed to rlgl - Matrix4x4[] transforms = new Matrix4x4[instances]; - Shader shader = LoadShader( - "resources/shaders/glsl330/lighting_instancing.vs", - "resources/shaders/glsl330/lighting.fs" - ); - - // Get some shader loactions - unsafe - { - int* locs = (int*)shader.Locs; - locs[(int)ShaderLocationIndex.MatrixMvp] = GetShaderLocation(shader, "mvp"); - locs[(int)ShaderLocationIndex.VectorView] = GetShaderLocation(shader, "viewPos"); - locs[(int)ShaderLocationIndex.MatrixModel] = GetShaderLocationAttrib( - shader, - "instanceTransform" + var translation = Matrix4x4.CreateTranslation( + GetRandomValue(-50, 50), + GetRandomValue(-50, 50), + GetRandomValue(-50, 50) ); + var axis = Vector3.Normalize(new Vector3( + GetRandomValue(0, 360), + GetRandomValue(0, 360), + GetRandomValue(0, 360) + )); + var angle = GetRandomValue(0, 180) * DEG2RAD; + var rotation = Matrix4x4.CreateFromAxisAngle(axis, angle); + + transforms[i] = Matrix4x4.Transpose(Matrix4x4.Multiply(rotation, translation)); } - // Ambient light level - int ambientLoc = GetShaderLocation(shader, "ambient"); + // Load lighting shader + shader = LoadShader( + $"resources/shaders/glsl{GlslVersion}/lighting_instancing.vs", + $"resources/shaders/glsl{GlslVersion}/lighting.fs" + ); + // Get shader locations + shader.Locs[(int)ShaderLocationIndex.MatrixMvp] = GetShaderLocation(shader, "mvp"); + shader.Locs[(int)ShaderLocationIndex.VectorView] = GetShaderLocation(shader, "viewPos"); + + // Set shader value: ambient light level + var ambientLoc = GetShaderLocation(shader, "ambient"); Raylib.SetShaderValue( shader, ambientLoc, @@ -114,211 +99,105 @@ public class MeshInstancing ShaderUniformDataType.Vec4 ); + // Create one light Rlights.CreateLight( 0, LightType.Directorional, - new Vector3(50, 50, 0), + new Vector3(50.0f, 50.0f, 0.0f), Vector3.Zero, Color.White, shader ); - Material material = LoadMaterialDefault(); - material.Shader = shader; - unsafe - { - material.Maps[(int)MaterialMapIndex.Diffuse].Color = Color.Red; - } + // NOTE: We are assigning the intancing shader to material.shader + // to be used on mesh drawing with DrawMeshInstanced() + matInstances = LoadMaterialDefault(); + matInstances.Shader = shader; + matInstances.Maps[(int)MaterialMapIndex.Diffuse].Color = Color.Red; - int textPositionY = 300; + // Load default material (using raylib intenral default shader) for non-instanced mesh drawing + // WARNING: Default shader enables vertex color attribute BUT GenMeshCube() does not generate vertex colors, so, + // when drawing the color attribute is disabled and a default color value is provided as input for thevertex attribute + matDefault = LoadMaterialDefault(); + matDefault.Maps[(int)MaterialMapIndex.Diffuse].Color = Color.Blue; + } - // Simple frames counter to manage animation - int framesCounter = 0; + public unsafe void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Orbital); - SetTargetFPS(fps); + // Update the light shader with the camera view position + float[] cameraPos = { camera.Position.X, camera.Position.Y, camera.Position.Z }; + Raylib.SetShaderValue( + shader, + shader.Locs[(int)ShaderLocationIndex.VectorView], + cameraPos, + ShaderUniformDataType.Vec3 + ); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + // Draw cube mesh with default material (BLUE) + DrawMesh(cube, matDefault, Matrix4x4.Transpose(Matrix4x4.CreateTranslation(-10.0f, 0.0f, 0.0f))); + + // Draw meshes instanced using material containing instancing shader (RED + lighting), + // transforms[] for the instances should be provided, they are dynamically + // updated in GPU every frame, so we can animate the different mesh instances + DrawMeshInstanced(cube, matInstances, transforms, MaxInstances); + + // Draw cube mesh with default material (BLUE) + DrawMesh(cube, matDefault, Matrix4x4.Transpose(Matrix4x4.CreateTranslation(10.0f, 0.0f, 0.0f))); + + EndMode3D(); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + // Detach shader so UnloadMaterial does not also unload it, then free everything. + matInstances.Shader = new(); + UnloadMaterial(matInstances); + UnloadMaterial(matDefault); + UnloadMesh(cube); + UnloadShader(shader); + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - mesh instancing"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new MeshInstancing(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Free); - - textPositionY = 300; - framesCounter += 1; - - if (IsKeyDown(KeyboardKey.Up)) - { - amp += 0.5f; - } - - if (IsKeyDown(KeyboardKey.Down)) - { - amp = (amp <= 1) ? 1.0f : (amp - 1.0f); - } - - if (IsKeyDown(KeyboardKey.Left)) - { - variance = (variance <= 0.0f) ? 0.0f : (variance - 0.01f); - } - - if (IsKeyDown(KeyboardKey.Right)) - { - variance = (variance >= 1.0f) ? 1.0f : (variance + 0.01f); - } - - if (IsKeyDown(KeyboardKey.One)) - { - groups = 1; - } - - if (IsKeyDown(KeyboardKey.Two)) - { - groups = 2; - } - - if (IsKeyDown(KeyboardKey.Three)) - { - groups = 3; - } - - if (IsKeyDown(KeyboardKey.Four)) - { - groups = 4; - } - - if (IsKeyDown(KeyboardKey.Five)) - { - groups = 5; - } - - if (IsKeyDown(KeyboardKey.Six)) - { - groups = 6; - } - - if (IsKeyDown(KeyboardKey.Seven)) - { - groups = 7; - } - - if (IsKeyDown(KeyboardKey.Eight)) - { - groups = 8; - } - - if (IsKeyDown(KeyboardKey.Nine)) - { - groups = 9; - } - - if (IsKeyDown(KeyboardKey.W)) - { - groups = 7; - amp = 25; - speed = 18; - variance = 0.70f; - } - - if (IsKeyDown(KeyboardKey.Equal)) - { - speed = (speed <= (int)(fps * 0.25f)) ? (int)(fps * 0.25f) : (int)(speed * 0.95f); - } - - if (IsKeyDown(KeyboardKey.KpAdd)) - { - speed = (speed <= (int)(fps * 0.25f)) ? (int)(fps * 0.25f) : (int)(speed * 0.95f); - } - - if (IsKeyDown(KeyboardKey.Minus)) - { - speed = (int)MathF.Max(speed * 1.02f, speed + 1); - } - - if (IsKeyDown(KeyboardKey.KpSubtract)) - { - speed = (int)MathF.Max(speed * 1.02f, speed + 1); - } - - // Update the light shader with the camera view position - float[] cameraPos = { camera.Position.X, camera.Position.Y, camera.Position.Z }; - Raylib.SetShaderValue( - shader, - (int)ShaderLocationIndex.VectorView, - cameraPos, - ShaderUniformDataType.Vec3 - ); - - // Apply per-instance transformations - for (int i = 0; i < instances; i++) - { - rotations[i] = Matrix4x4.Multiply(rotations[i], rotationsInc[i]); - transforms[i] = Matrix4x4.Multiply(rotations[i], translations[i]); - - // Get the animation cycle's framesCounter for this instance - loop = (float)((framesCounter + (int)(((float)(i % groups) / groups) * speed)) % speed) / speed; - - // Calculate the y according to loop cycle - y = (MathF.Sin(loop * MathF.PI * 2)) * amp * ((1 - variance) + (variance * (float)(i % (groups * 10)) / (groups * 10))); - - // Clamp to floor - y = (y < 0) ? 0.0f : y; - - transforms[i] = Matrix4x4.Multiply(transforms[i], Matrix4x4.CreateTranslation(0.0f, y, 0.0f)); - transforms[i] = Matrix4x4.Transpose(transforms[i]); - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - DrawMeshInstanced(cube, material, transforms, instances); - EndMode3D(); - - DrawText("A CUBE OF DANCING CUBES!", 490, 10, 20, Color.Maroon); - DrawText("PRESS KEYS:", 10, textPositionY, 20, Color.Black); - - DrawText("1 - 9", 10, textPositionY += 25, 10, Color.Black); - DrawText(": Number of groups", 50, textPositionY, 10, Color.Black); - DrawText($": {groups}", 160, textPositionY, 10, Color.Black); - - DrawText("UP", 10, textPositionY += 15, 10, Color.Black); - DrawText(": increase amplitude", 50, textPositionY, 10, Color.Black); - DrawText($": {amp}%.2f", 160, textPositionY, 10, Color.Black); - - DrawText("DOWN", 10, textPositionY += 15, 10, Color.Black); - DrawText(": decrease amplitude", 50, textPositionY, 10, Color.Black); - - DrawText("LEFT", 10, textPositionY += 15, 10, Color.Black); - DrawText(": decrease variance", 50, textPositionY, 10, Color.Black); - DrawText($": {variance}.2f", 160, textPositionY, 10, Color.Black); - - DrawText("RIGHT", 10, textPositionY += 15, 10, Color.Black); - DrawText(": increase variance", 50, textPositionY, 10, Color.Black); - - DrawText("+/=", 10, textPositionY += 15, 10, Color.Black); - DrawText(": increase speed", 50, textPositionY, 10, Color.Black); - DrawText($": {speed} = {((float)fps / speed)} loops/sec", 160, textPositionY, 10, Color.Black); - - DrawText("-", 10, textPositionY += 15, 10, Color.Black); - DrawText(": decrease speed", 50, textPositionY, 10, Color.Black); - - DrawText("W", 10, textPositionY += 15, 10, Color.Black); - DrawText(": Wild setup!", 50, textPositionY, 10, Color.Black); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shaders/ModelShader.cs b/Examples/Shaders/ModelShader.cs index 1d78b96..033b530 100644 --- a/Examples/Shaders/ModelShader.cs +++ b/Examples/Shaders/ModelShader.cs @@ -1,18 +1,22 @@ /******************************************************************************************* * -* raylib [shaders] example - Apply a shader to a 3d model +* raylib [shaders] example - model shader +* +* Example complexity rating: [★★☆☆] 2/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example * on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders * raylib comes with shaders ready for both versions, check raylib/shaders install folder * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.3, last time updated with raylib 3.7 * -* Copyright (c) 2014 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -21,86 +25,122 @@ using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class ModelShader +public class ModelShader : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + public string Name => "Shaders / Model Shader"; + + public string Title => "raylib [shaders] example - model shader"; + + public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint; + + public bool CursorDisabled => true; + + private Camera3D camera; + private Model model; + private Texture2D texture; + private Shader shader; + private Vector3 position; + + public void Init() + { + // Define the camera to look into our 3d world + camera = new(); + camera.Position = new Vector3(4.0f, 4.0f, 4.0f); // Camera position + camera.Target = new Vector3(0.0f, 1.0f, -1.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type + + model = LoadModel("resources/models/obj/watermill.obj"); // Load OBJ model + texture = LoadTexture("resources/models/obj/watermill_diffuse.png"); // Load model texture + + // Load shader for model + // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader + shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/grayscale.fs"); + + Raylib.SetMaterialShader(ref model, 0, ref shader); // Set shader effect to 3d model + Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture); // Bind texture to model + + position = new(0.0f, 0.0f, 0.0f); // Set model position + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Free); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + DrawModel(model, position, 0.2f, Color.White); // Draw 3d model with texture + + DrawGrid(10, 1.0f); // Draw a grid + + EndMode3D(); + + DrawText( + "(c) Watermill 3D model by Alberto Cano", + screenWidth - 210, + screenHeight - 20, + 10, + Color.Gray + ); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadShader(shader); // Unload shader + UnloadTexture(texture); // Unload texture + UnloadModel(model); // Unload model + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - // Enable Multi Sampling Anti Aliasing 4x (if available) - SetConfigFlags(ConfigFlags.Msaa4xHint); + SetConfigFlags(ConfigFlags.Msaa4xHint); // Enable Multi Sampling Anti Aliasing 4x (if available) InitWindow(screenWidth, screenHeight, "raylib [shaders] example - model shader"); - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(4.0f, 4.0f, 4.0f); - camera.Target = new Vector3(0.0f, 1.0f, -1.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; - - Model model = LoadModel("resources/models/obj/watermill.obj"); - Texture2D texture = LoadTexture("resources/models/obj/watermill_diffuse.png"); - Shader shader = LoadShader("resources/shaders/glsl330/base.vs", - "resources/shaders/glsl330/grayscale.fs"); - - Raylib.SetMaterialShader(ref model, 0, ref shader); - Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture); - - Vector3 position = new(0.0f, 0.0f, 0.0f); - - SetTargetFPS(60); + DisableCursor(); // Limit cursor to relative movement inside the window + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new ModelShader(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Free); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawModel(model, position, 0.2f, Color.White); - - DrawGrid(10, 1.0f); - - EndMode3D(); - - DrawText( - "(c) Watermill 3D model by Alberto Cano", - screenWidth - 210, - screenHeight - 20, - 10, - Color.Gray - ); - - DrawText($"Camera3D position: ({camera.Position})", 600, 20, 10, Color.Black); - DrawText($"Camera3D target: ({camera.Position})", 600, 40, 10, Color.Gray); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadShader(shader); - UnloadTexture(texture); - UnloadModel(model); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shaders/MultiSample2d.cs b/Examples/Shaders/MultiSample2d.cs index ba318fc..5375cf2 100644 --- a/Examples/Shaders/MultiSample2d.cs +++ b/Examples/Shaders/MultiSample2d.cs @@ -1,18 +1,22 @@ /******************************************************************************************* * -* raylib [shaders] example - Multiple sample2D with default batch system +* raylib [shaders] example - multi sample2d +* +* Example complexity rating: [★★☆☆] 2/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example * on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders * raylib comes with shaders ready for both versions, check raylib/shaders install folder * -* This example has been created using raylib 3.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 3.5, last time updated with raylib 3.5 * -* Copyright (c) 2020 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) 2020-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -20,94 +24,129 @@ using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class MultiSample2d +public class MultiSample2d : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + public string Name => "Shaders / Multi Sample 2D"; + + public string Title => "raylib [shaders] example - multi sample2d"; + + private Texture2D texRed; + private Texture2D texBlue; + private Shader shader; + private int texBlueLoc; + private int dividerLoc; + private float dividerValue; + + public void Init() + { + var imRed = GenImageColor(800, 450, new Color(255, 0, 0, 255)); + texRed = LoadTextureFromImage(imRed); + UnloadImage(imRed); + + var imBlue = GenImageColor(800, 450, new Color(0, 0, 255, 255)); + texBlue = LoadTextureFromImage(imBlue); + UnloadImage(imBlue); + + shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/color_mix.fs"); + + // Get an additional sampler2D location to be enabled on drawing + texBlueLoc = GetShaderLocation(shader, "texture1"); + + // Get shader uniform for divider + dividerLoc = GetShaderLocation(shader, "divider"); + dividerValue = 0.5f; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyDown(KeyboardKey.Right)) + { + dividerValue += 0.01f; + } + else if (IsKeyDown(KeyboardKey.Left)) + { + dividerValue -= 0.01f; + } + + if (dividerValue < 0.0f) + { + dividerValue = 0.0f; + } + else if (dividerValue > 1.0f) + { + dividerValue = 1.0f; + } + + Raylib.SetShaderValue(shader, dividerLoc, dividerValue, ShaderUniformDataType.Float); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginShaderMode(shader); + + // WARNING: Additional textures (sampler2D) are enabled for ALL draw calls in the batch, + // but EndShaderMode() forces batch drawing and resets active textures, this way + // other textures (sampler2D) can be activated on consequent drawings (if required) + // The downside of this approach is that SetShaderValue() must be called inside the loop, + // to be set again after every EndShaderMode() reset + SetShaderValueTexture(shader, texBlueLoc, texBlue); + + // We are drawing texRed using default [sampler2D texture0] but + // an additional texture units is enabled for texBlue [sampler2D texture1] + DrawTexture(texRed, 0, 0, Color.White); + + EndShaderMode(); // Texture sampler2D is reseted, needs to be set again for next frame + + var y = GetScreenHeight() - 40; + DrawText("Use KEY_LEFT/KEY_RIGHT to move texture mixing in shader!", 80, y, 20, Color.RayWhite); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadShader(shader); + UnloadTexture(texRed); + UnloadTexture(texBlue); + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib - multiple sample2D"); - - Image imRed = GenImageColor(800, 450, new Color(255, 0, 0, 255)); - Texture2D texRed = LoadTextureFromImage(imRed); - UnloadImage(imRed); - - Image imBlue = GenImageColor(800, 450, new Color(0, 0, 255, 255)); - Texture2D texBlue = LoadTextureFromImage(imBlue); - UnloadImage(imBlue); - - Shader shader = LoadShader(null, "resources/shaders/glsl330/color_mix.fs"); - - // Get an additional sampler2D location to be enabled on drawing - int texBlueLoc = GetShaderLocation(shader, "texture1"); - - // Get shader uniform for divider - int dividerLoc = GetShaderLocation(shader, "divider"); - float dividerValue = 0.5f; + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - multi sample2d"); SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new MultiSample2d(); + game.Init(); + // Main game loop while (!WindowShouldClose()) { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyDown(KeyboardKey.Right)) - { - dividerValue += 0.01f; - } - else if (IsKeyDown(KeyboardKey.Left)) - { - dividerValue -= 0.01f; - } - - if (dividerValue < 0.0f) - { - dividerValue = 0.0f; - } - else if (dividerValue > 1.0f) - { - dividerValue = 1.0f; - } - - Raylib.SetShaderValue(shader, dividerLoc, dividerValue, ShaderUniformDataType.Float); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginShaderMode(shader); - - // WARNING: Additional samplers are enabled for all draw calls in the batch, - // EndShaderMode() forces batch drawing and consequently resets active textures - // to let other sampler2D to be activated on consequent drawings (if required) - SetShaderValueTexture(shader, texBlueLoc, texBlue); - - // We are drawing texRed using default sampler2D texture0 but - // an additional texture units is enabled for texBlue (sampler2D texture1) - DrawTexture(texRed, 0, 0, Color.White); - - EndShaderMode(); - - int y = GetScreenHeight() - 40; - DrawText("Use KEY_LEFT/KEY_RIGHT to move texture mixing in shader!", 80, y, 20, Color.RayWhite); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadShader(shader); - UnloadTexture(texRed); - UnloadTexture(texBlue); - CloseWindow(); //-------------------------------------------------------------------------------------- diff --git a/Examples/Shaders/PaletteSwitch.cs b/Examples/Shaders/PaletteSwitch.cs index b14b189..bed75e4 100644 --- a/Examples/Shaders/PaletteSwitch.cs +++ b/Examples/Shaders/PaletteSwitch.cs @@ -1,20 +1,24 @@ /******************************************************************************************* * -* raylib [shaders] example - Color palette switch +* raylib [shaders] example - palette switch +* +* Example complexity rating: [★★★☆] 3/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example * on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders * raylib comes with shaders ready for both versions, check raylib/shaders install folder * -* This example has been created using raylib 2.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 2.5, last time updated with raylib 3.7 * * Example contributed by Marco Lizza (@MarcoLizza) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2019 Marco Lizza (@MarcoLizza) and 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) 2019-2025 Marco Lizza (@MarcoLizza) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -22,13 +26,24 @@ using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class PaletteSwitch +public class PaletteSwitch : IExample { - const int GlslVersion = 330; - const int ColorsPerPalette = 8; - const int VALUES_PER_COLOR = 3; + private const int screenWidth = 800; + private const int screenHeight = 450; - static int[][] Palettes = new int[][] { +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + private const int ColorsPerPalette = 8; + private const int VALUES_PER_COLOR = 3; + + public string Name => "Shaders / Palette Switch"; + + public string Title => "raylib [shaders] example - palette switch"; + + private int[][] Palettes = new int[][] { // 3-BIT RGB new int[] { 0, 0, 0, @@ -64,101 +79,119 @@ public class PaletteSwitch } }; - static string[] PaletteText = new string[] { + private string[] PaletteText = new string[] { "3-BIT RGB", "AMMO-8 (GameBoy-like)", "RKBV (2-strip film)" }; + private Shader shader; + private int paletteLoc; + private int currentPalette; + private int lineHeight; + + public void Init() + { + // Load shader to be used on some parts drawing + // NOTE 1: Using GLSL 330 shader version, on OpenGL ES 2.0 use GLSL 100 shader version + // NOTE 2: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader + shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/palette_switch.fs"); + + // Get variable (uniform) location on the shader to connect with the program + // NOTE: If uniform variable could not be found in the shader, function returns -1 + paletteLoc = GetShaderLocation(shader, "palette"); + + currentPalette = 0; + lineHeight = screenHeight / ColorsPerPalette; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KeyboardKey.Right)) + { + currentPalette++; + } + else if (IsKeyPressed(KeyboardKey.Left)) + { + currentPalette--; + } + + if (currentPalette >= Palettes.Length) + { + currentPalette = 0; + } + else if (currentPalette < 0) + { + currentPalette = Palettes.Length - 1; + } + + // Send palette data to the shader to be used on drawing + // NOTE: We are sending RGB triplets w/o the alpha channel + Raylib.SetShaderValueV( + shader, + paletteLoc, + Palettes[currentPalette], + ShaderUniformDataType.IVec3, + ColorsPerPalette + ); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginShaderMode(shader); + + for (var i = 0; i < ColorsPerPalette; i++) + { + // Draw horizontal screen-wide rectangles with increasing "palette index" + // The used palette index is encoded in the RGB components of the pixel + DrawRectangle(0, lineHeight * i, GetScreenWidth(), lineHeight, new Color(i, i, i, 255)); + } + + EndShaderMode(); + + DrawText("< >", 10, 10, 30, Color.DarkBlue); + DrawText("CURRENT PALETTE:", 60, 15, 20, Color.RayWhite); + DrawText(PaletteText[currentPalette], 300, 15, 20, Color.Red); + + DrawFPS(700, 15); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadShader(shader); // Unload shader + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - palette switch"); - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - color palette switch"); - - // Load shader to be used on some parts drawing - // NOTE 1: Using GLSL 330 shader version, on OpenGL ES 2.0 use GLSL 100 shader version - // NOTE 2: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader - Shader shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/palette_switch.fs"); - - // Get variable (uniform) location on the shader to connect with the program - // NOTE: If uniform variable could not be found in the shader, function returns -1 - int paletteLoc = GetShaderLocation(shader, "palette"); - - int currentPalette = 0; - int lineHeight = screenHeight / ColorsPerPalette; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new PaletteSwitch(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyPressed(KeyboardKey.Right)) - { - currentPalette++; - } - else if (IsKeyPressed(KeyboardKey.Left)) - { - currentPalette--; - } - - if (currentPalette >= Palettes.Length) - { - currentPalette = 0; - } - else if (currentPalette < 0) - { - currentPalette = Palettes.Length - 1; - } - - // Send new value to the shader to be used on drawing. - // NOTE: We are sending RGB triplets w/o the alpha channel - Raylib.SetShaderValueV( - shader, - paletteLoc, - Palettes[currentPalette], - ShaderUniformDataType.IVec3, - ColorsPerPalette - ); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginShaderMode(shader); - - for (int i = 0; i < ColorsPerPalette; i++) - { - // Draw horizontal screen-wide rectangles with increasing "palette index" - // The used palette index is encoded in the RGB components of the pixel - DrawRectangle(0, lineHeight * i, GetScreenWidth(), lineHeight, new Color(i, i, i, 255)); - } - - EndShaderMode(); - - DrawText("< >", 10, 10, 30, Color.DarkBlue); - DrawText("CURRENT PALETTE:", 60, 15, 20, Color.RayWhite); - DrawText(PaletteText[currentPalette], 300, 15, 20, Color.Red); - - DrawFPS(700, 15); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadShader(shader); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shaders/PostProcessing.cs b/Examples/Shaders/PostProcessing.cs index b7dd2a5..9df3515 100644 --- a/Examples/Shaders/PostProcessing.cs +++ b/Examples/Shaders/PostProcessing.cs @@ -1,18 +1,22 @@ /******************************************************************************************* * -* raylib [shaders] example - Apply a postprocessing shader to a scene +* raylib [shaders] example - postprocessing +* +* Example complexity rating: [★★★☆] 3/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example * on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders * raylib comes with shaders ready for both versions, check raylib/shaders install folder * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.3, last time updated with raylib 4.0 * -* Copyright (c) 2015 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -21,11 +25,24 @@ using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class PostProcessing +public class PostProcessing : IExample { - public const int GLSL_VERSION = 330; + private const int screenWidth = 800; + private const int screenHeight = 450; - enum PostproShader +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + public string Name => "Shaders / Post Processing"; + + public string Title => "raylib [shaders] example - postprocessing"; + + public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint; + + private enum PostproShader { FxGrayScale = 0, FxPosterization, @@ -43,7 +60,7 @@ public class PostProcessing Max } - static string[] postproShaderText = new string[] { + private string[] postproShaderText = new string[] { "GRAYSCALE", "POSTERIZATION", "DREAM_VISION", @@ -59,40 +76,39 @@ public class PostProcessing //"FXAA" }; - public static int Main() + private Camera3D camera; + private Model model; + private Texture2D texture; + private Vector3 position; + private Shader[] shaders; + private int currentShader; + private RenderTexture2D target; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - // Enable Multi Sampling Anti Aliasing 4x (if available) - SetConfigFlags(ConfigFlags.Msaa4xHint); - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - postprocessing shader"); - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(2.0f, 3.0f, 2.0f); - camera.Target = new Vector3(0.0f, 1.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; + camera = new(); + camera.Position = new Vector3(2.0f, 3.0f, 2.0f); // Camera position + camera.Target = new Vector3(0.0f, 1.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type - Model model = LoadModel("resources/models/obj/church.obj"); - Texture2D texture = LoadTexture("resources/models/obj/church_diffuse.png"); + model = LoadModel("resources/models/church.obj"); // Load OBJ model + texture = LoadTexture("resources/models/church_diffuse.png"); // Load model texture (diffuse map) // Set model diffuse texture Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture); - Vector3 position = new(0.0f, 0.0f, 0.0f); + position = new(0.0f, 0.0f, 0.0f); // Set model position // Load all postpro shaders // NOTE 1: All postpro shader use the base vertex shader (DEFAULT_VERTEX_SHADER) // NOTE 2: We load the correct shader depending on GLSL version - Shader[] shaders = new Shader[(int)PostproShader.Max]; + shaders = new Shader[(int)PostproShader.Max]; // NOTE: Defining null (NULL) for vertex shader forces usage of internal default vertex shader - string shaderPath = "resources/shaders/glsl330"; + var shaderPath = $"resources/shaders/glsl{GlslVersion}"; shaders[(int)PostproShader.FxGrayScale] = LoadShader(null, $"{shaderPath}/grayscale.fs"); shaders[(int)PostproShader.FxPosterization] = LoadShader(null, $"{shaderPath}/posterization.fs"); shaders[(int)PostproShader.FxDreamVision] = LoadShader(null, $"{shaderPath}/dream_vision.fs"); @@ -106,99 +122,122 @@ public class PostProcessing shaders[(int)PostproShader.FxBloom] = LoadShader(null, $"{shaderPath}/bloom.fs"); shaders[(int)PostproShader.FxBlur] = LoadShader(null, $"{shaderPath}/blur.fs"); - int currentShader = (int)PostproShader.FxGrayScale; + currentShader = (int)PostproShader.FxGrayScale; // Create a RenderTexture2D to be used for render to texture - RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); + target = LoadRenderTexture(screenWidth, screenHeight); + } - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Orbital); - // Main game loop - while (!WindowShouldClose()) + if (IsKeyPressed(KeyboardKey.Right)) { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Orbital); - - if (IsKeyPressed(KeyboardKey.Right)) - { - currentShader++; - } - else if (IsKeyPressed(KeyboardKey.Left)) - { - currentShader--; - } - - if (currentShader >= (int)PostproShader.Max) - { - currentShader = 0; - } - else if (currentShader < 0) - { - currentShader = (int)PostproShader.Max - 1; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // Enable drawing to texture - BeginTextureMode(target); - ClearBackground(Color.RayWhite); - - BeginMode3D(camera); - - DrawModel(model, position, 0.1f, Color.White); - - DrawGrid(10, 1.0f); - - EndMode3D(); - - // End drawing to texture (now we have a texture available for next passes) - EndTextureMode(); - - // Render previously generated texture using selected postpro shader - BeginShaderMode(shaders[currentShader]); - - // NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom) - DrawTextureRec( - target.Texture, - new Rectangle(0, 0, target.Texture.Width, -target.Texture.Height), - new Vector2(0, 0), - Color.White - ); - - EndShaderMode(); - - DrawRectangle(0, 9, 580, 30, ColorAlpha(Color.LightGray, 0.7f)); - - DrawText("(c) Church 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, Color.Gray); - - DrawText("CURRENT POSTPRO SHADER:", 10, 15, 20, Color.Black); - DrawText(postproShaderText[currentShader], 330, 15, 20, Color.Red); - DrawText("< >", 540, 10, 30, Color.DarkBlue); - - DrawFPS(700, 15); - - EndDrawing(); - //---------------------------------------------------------------------------------- + currentShader++; + } + else if (IsKeyPressed(KeyboardKey.Left)) + { + currentShader--; } - // De-Initialization - //-------------------------------------------------------------------------------------- - for (int i = 0; i < (int)PostproShader.Max; i++) + if (currentShader >= (int)PostproShader.Max) + { + currentShader = 0; + } + else if (currentShader < 0) + { + currentShader = (int)PostproShader.Max - 1; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + // Enable drawing to texture + BeginTextureMode(target); + ClearBackground(Color.RayWhite); + + BeginMode3D(camera); + + DrawModel(model, position, 0.1f, Color.White); + + DrawGrid(10, 1.0f); + + EndMode3D(); + + // End drawing to texture (now we have a texture available for next passes) + EndTextureMode(); + + // Render generated texture using selected postprocessing shader + BeginShaderMode(shaders[currentShader]); + + // NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom) + DrawTextureRec( + target.Texture, + new Rectangle(0, 0, target.Texture.Width, -target.Texture.Height), + new Vector2(0, 0), + Color.White + ); + + EndShaderMode(); + + DrawRectangle(0, 9, 580, 30, Fade(Color.LightGray, 0.7f)); + + DrawText("(c) Church 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, Color.Gray); + + DrawText("CURRENT POSTPRO SHADER:", 10, 15, 20, Color.Black); + DrawText(postproShaderText[currentShader], 330, 15, 20, Color.Red); + DrawText("< >", 540, 10, 30, Color.DarkBlue); + + DrawFPS(700, 15); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + // Unload all postpro shaders + for (var i = 0; i < (int)PostproShader.Max; i++) { UnloadShader(shaders[i]); } - UnloadTexture(texture); - UnloadModel(model); - UnloadRenderTexture(target); + UnloadTexture(texture); // Unload texture + UnloadModel(model); // Unload model + UnloadRenderTexture(target); // Unload render texture + } - CloseWindow(); + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + SetConfigFlags(ConfigFlags.Msaa4xHint); // Enable Multi Sampling Anti Aliasing 4x (if available) + + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - postprocessing"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + var game = new PostProcessing(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + game.Update(); + } + + game.Unload(); + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shaders/Raymarching.cs b/Examples/Shaders/Raymarching.cs index b40c032..e521b6d 100644 --- a/Examples/Shaders/Raymarching.cs +++ b/Examples/Shaders/Raymarching.cs @@ -1,18 +1,18 @@ /******************************************************************************************* * -* raylib [shaders] example - Raymarching shapes generation +* raylib [shaders] example - raymarching rendering * -* NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* Example complexity rating: [★★★★] 4/4 * -* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example -* on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders -* raylib comes with shaders ready for both versions, check raylib/shaders install folder +* NOTE: This example requires raylib OpenGL 3.3 for shaders support and only #version 330 +* is currently supported. OpenGL ES 2.0 platforms are not supported at the moment * -* This example has been created using raylib 2.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 2.0, last time updated with raylib 4.2 * -* Copyright (c) 2018 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) 2018-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -22,98 +22,138 @@ using static Raylib_cs.ConfigFlags; namespace Examples.Shaders; -public class Raymarching +public class Raymarching : IExample { +#if BROWSER + public const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else public const int GlslVersion = 330; +#endif + + public string Name => "Shaders / Raymarching"; + + public string Title => "raylib [shaders] example - raymarching rendering"; + + public ConfigFlags ConfigFlags => ConfigFlags.ResizableWindow; + + public bool CursorDisabled => true; + + private int screenWidth; + private int screenHeight; + + private Camera3D camera; + private Shader shader; + private int viewEyeLoc; + private int viewCenterLoc; + private int runTimeLoc; + private int resolutionLoc; + private float runTime; + + public void Init() + { + screenWidth = GetScreenWidth(); + screenHeight = GetScreenHeight(); + + camera = new(); + camera.Position = new Vector3(2.5f, 2.5f, 3.0f); // Camera position + camera.Target = new Vector3(0.0f, 0.0f, 0.7f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 65.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type + + // Load raymarching shader + // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader + shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/raymarching.fs"); + + // Get shader locations for required uniforms + viewEyeLoc = GetShaderLocation(shader, "viewEye"); + viewCenterLoc = GetShaderLocation(shader, "viewCenter"); + runTimeLoc = GetShaderLocation(shader, "runTime"); + resolutionLoc = GetShaderLocation(shader, "resolution"); + + float[] resolution = { (float)screenWidth, (float)screenHeight }; + Raylib.SetShaderValue(shader, resolutionLoc, resolution, ShaderUniformDataType.Vec2); + + runTime = 0.0f; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.FirstPerson); + + var deltaTime = GetFrameTime(); + runTime += deltaTime; + + // Set shader required uniform values + Raylib.SetShaderValue(shader, viewEyeLoc, camera.Position, ShaderUniformDataType.Vec3); + Raylib.SetShaderValue(shader, viewCenterLoc, camera.Target, ShaderUniformDataType.Vec3); + Raylib.SetShaderValue(shader, runTimeLoc, runTime, ShaderUniformDataType.Float); + + // Check if screen is resized + if (IsWindowResized()) + { + screenWidth = GetScreenWidth(); + screenHeight = GetScreenHeight(); + var resolution = new float[] { (float)screenWidth, (float)screenHeight }; + Raylib.SetShaderValue(shader, resolutionLoc, resolution, ShaderUniformDataType.Vec2); + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + // We only draw a white full-screen rectangle, + // frame is generated in shader using raymarching + BeginShaderMode(shader); + DrawRectangle(0, 0, screenWidth, screenHeight, Color.White); + EndShaderMode(); + + DrawText( + "(c) Raymarching shader by Iñigo Quilez. MIT License.", + screenWidth - 280, + screenHeight - 20, + 10, + Color.Black + ); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadShader(shader); // Unload shader + } public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - int screenWidth = 800; - int screenHeight = 450; - SetConfigFlags(ResizableWindow); - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - raymarching shapes"); + InitWindow(800, 450, "raylib [shaders] example - raymarching rendering"); - Camera3D camera = new(); - camera.Position = new Vector3(2.5f, 2.5f, 3.0f); - camera.Target = new Vector3(0.0f, 0.0f, 0.7f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 65.0f; - - // Load raymarching shader - // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader - Shader shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/raymarching.fs"); - - // Get shader locations for required uniforms - int viewEyeLoc = GetShaderLocation(shader, "viewEye"); - int viewCenterLoc = GetShaderLocation(shader, "viewCenter"); - int runTimeLoc = GetShaderLocation(shader, "runTime"); - int resolutionLoc = GetShaderLocation(shader, "resolution"); - - float[] resolution = { (float)screenWidth, (float)screenHeight }; - Raylib.SetShaderValue(shader, resolutionLoc, resolution, ShaderUniformDataType.Vec2); - - float runTime = 0.0f; - - SetTargetFPS(60); + DisableCursor(); // Limit cursor to relative movement inside the window + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new Raymarching(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Check if screen is resized - //---------------------------------------------------------------------------------- - if (IsWindowResized()) - { - screenWidth = GetScreenWidth(); - screenHeight = GetScreenHeight(); - resolution = new float[] { (float)screenWidth, (float)screenHeight }; - Raylib.SetShaderValue(shader, resolutionLoc, resolution, ShaderUniformDataType.Vec2); - } - - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Free); - - float deltaTime = GetFrameTime(); - runTime += deltaTime; - - // Set shader required uniform values - Raylib.SetShaderValue(shader, viewEyeLoc, camera.Position, ShaderUniformDataType.Vec3); - Raylib.SetShaderValue(shader, viewCenterLoc, camera.Target, ShaderUniformDataType.Vec3); - Raylib.SetShaderValue(shader, runTimeLoc, runTime, ShaderUniformDataType.Float); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // We only draw a white full-screen rectangle, - // frame is generated in shader using raymarching - BeginShaderMode(shader); - DrawRectangle(0, 0, screenWidth, screenHeight, Color.White); - EndShaderMode(); - - DrawText( - "(c) Raymarching shader by Iñigo Quilez. MIT License.", - screenWidth - 280, - screenHeight - 20, - 10, - Color.Black - ); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadShader(shader); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shaders/ShapesTextures.cs b/Examples/Shaders/ShapesTextures.cs index 2def789..c0a5eef 100644 --- a/Examples/Shaders/ShapesTextures.cs +++ b/Examples/Shaders/ShapesTextures.cs @@ -1,18 +1,22 @@ /******************************************************************************************* * -* raylib [shaders] example - Apply a shader to some shape or texture +* raylib [shaders] example - shapes textures +* +* Example complexity rating: [★★☆☆] 2/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example * on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders * raylib comes with shaders ready for both versions, check raylib/shaders install folder * -* This example has been created using raylib 1.7 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.7, last time updated with raylib 3.7 * -* Copyright (c) 2015 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -21,101 +25,123 @@ using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class ShapesTextures +public class ShapesTextures : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + public string Name => "Shaders / Shapes Textures"; + + public string Title => "raylib [shaders] example - shapes textures"; + + private Texture2D fudesumi; + private Shader shader; + + public void Init() + { + fudesumi = LoadTexture("resources/fudesumi.png"); + + // Load shader to be used on some parts drawing + // NOTE 1: Using GLSL 330 shader version, on OpenGL ES 2.0 use GLSL 100 shader version + // NOTE 2: Defining null (NULL) for vertex shader forces usage of internal default vertex shader + shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/grayscale.fs"); + } + + public void Update() + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + // Start drawing with default shader + DrawText("USING DEFAULT SHADER", 20, 40, 10, Color.Red); + + DrawCircle(80, 120, 35, Color.DarkBlue); + DrawCircleGradient(new Vector2(80, 220), 60, Color.Green, Color.SkyBlue); + DrawCircleLines(80, 340, 80, Color.DarkBlue); + + + // Activate our custom shader to be applied on next shapes/textures drawings + BeginShaderMode(shader); + + DrawText("USING CUSTOM SHADER", 190, 40, 10, Color.Red); + + DrawRectangle(250 - 60, 90, 120, 60, Color.Red); + DrawRectangleGradientH(250 - 90, 170, 180, 130, Color.Maroon, Color.Gold); + DrawRectangleLines(250 - 40, 320, 80, 60, Color.Orange); + + // Activate our default shader for next drawings + EndShaderMode(); + + DrawText("USING DEFAULT SHADER", 370, 40, 10, Color.Red); + + DrawTriangle( + new Vector2(430, 80), + new Vector2(430 - 60, 150), + new Vector2(430 + 60, 150), Color.Violet + ); + + DrawTriangleLines( + new Vector2(430, 160), + new Vector2(430 - 20, 230), + new Vector2(430 + 20, 230), Color.DarkBlue + ); + + DrawPoly(new Vector2(430, 320), 6, 80, 0, Color.Brown); + + // Activate our custom shader to be applied on next shapes/textures drawings + BeginShaderMode(shader); + + // Using custom shader + DrawTexture(fudesumi, 500, -30, Color.White); + + // Activate our default shader for next drawings + EndShaderMode(); + + DrawText("(c) Fudesumi sprite by Eiden Marsal", 380, screenHeight - 20, 10, Color.Gray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadShader(shader); // Unload shader + UnloadTexture(fudesumi); // Unload texture + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shapes textures"); - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shapes and texture shaders"); - - Texture2D fudesumi = LoadTexture("resources/fudesumi.png"); - - // NOTE: Using GLSL 330 shader version, on OpenGL ES 2.0 use GLSL 100 shader version - Shader shader = LoadShader( - "resources/shaders/glsl330/base.vs", - "resources/shaders/glsl330/grayscale.fs" - ); - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new ShapesTextures(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update your variables here - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // Start drawing with default shader - DrawText("USING DEFAULT SHADER", 20, 40, 10, Color.Red); - - DrawCircle(80, 120, 35, Color.DarkBlue); - DrawCircleGradient(new Vector2(80, 220), 60, Color.Green, Color.SkyBlue); - DrawCircleLines(80, 340, 80, Color.DarkBlue); - - - // Activate our custom shader to be applied on next shapes/textures drawings - BeginShaderMode(shader); - - DrawText("USING CUSTOM SHADER", 190, 40, 10, Color.Red); - - DrawRectangle(250 - 60, 90, 120, 60, Color.Red); - DrawRectangleGradientH(250 - 90, 170, 180, 130, Color.Maroon, Color.Gold); - DrawRectangleLines(250 - 40, 320, 80, 60, Color.Orange); - - // Activate our default shader for next drawings - EndShaderMode(); - - DrawText("USING DEFAULT SHADER", 370, 40, 10, Color.Red); - - DrawTriangle( - new Vector2(430, 80), - new Vector2(430 - 60, 150), - new Vector2(430 + 60, 150), Color.Violet - ); - - DrawTriangleLines( - new Vector2(430, 160), - new Vector2(430 - 20, 230), - new Vector2(430 + 20, 230), Color.DarkBlue - ); - - DrawPoly(new Vector2(430, 320), 6, 80, 0, Color.Brown); - - // Activate our custom shader to be applied on next shapes/textures drawings - BeginShaderMode(shader); - - // Using custom shader - DrawTexture(fudesumi, 500, -30, Color.White); - - // Activate our default shader for next drawings - EndShaderMode(); - - DrawText("(c) Fudesumi sprite by Eiden Marsal", 380, screenHeight - 20, 10, Color.Gray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadShader(shader); - UnloadTexture(fudesumi); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } - } diff --git a/Examples/Shaders/SimpleMask.cs b/Examples/Shaders/SimpleMask.cs index ce43c2f..032aa90 100644 --- a/Examples/Shaders/SimpleMask.cs +++ b/Examples/Shaders/SimpleMask.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [shaders] example - Simple shader mask +* raylib [shaders] example - simple mask * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Example contributed by Chris Camacho (@codifies) and reviewed by Ramon Santamaria (@raysan5) +* Example originally created with raylib 2.5, last time updated with raylib 3.7 * -* Copyright (c) 2019 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5) +* Example contributed by Chris Camacho (@chriscamacho) 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) 2019-2025 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5) * ******************************************************************************************** * @@ -24,54 +28,72 @@ using static Raylib_cs.Raymath; namespace Examples.Shaders; -public class SimpleMask +public class SimpleMask : IExample { - public unsafe static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + public string Name => "Shaders / Simple Mask"; + + public string Title => "raylib [shaders] example - simple mask"; + + public bool CursorDisabled => true; + + private Camera3D camera; + private Model model1; + private Model model2; + private Model model3; + private Shader shader; + private Texture2D texDiffuse; + private Texture2D texMask; + private int shaderFrame; + private int framesCounter; + private Vector3 rotation; + + public unsafe void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib - simple shader mask"); - // Define the camera to look into our 3d world - Camera3D camera = new(); - camera.Position = new Vector3(0.0f, 1.0f, 2.0f); - camera.Target = new Vector3(0.0f, 0.0f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; + camera = new(); + camera.Position = new Vector3(0.0f, 1.0f, 2.0f); // Camera position + camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type // Define our three models to show the shader on - Mesh torus = GenMeshTorus(.3f, 1, 16, 32); - Model model1 = LoadModelFromMesh(torus); + var torus = GenMeshTorus(.3f, 1, 16, 32); + model1 = LoadModelFromMesh(torus); - Mesh cube = GenMeshCube(.8f, .8f, .8f); - Model model2 = LoadModelFromMesh(cube); + var cube = GenMeshCube(.8f, .8f, .8f); + model2 = LoadModelFromMesh(cube); // Generate model to be shaded just to see the gaps in the other two - Mesh sphere = GenMeshSphere(1, 16, 16); - Model model3 = LoadModelFromMesh(sphere); + var sphere = GenMeshSphere(1, 16, 16); + model3 = LoadModelFromMesh(sphere); // Load the shader - Shader shader = LoadShader("resources/shaders/glsl330/mask.vs", "resources/shaders/glsl330/mask.fs"); + shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/mask.fs"); // Load and apply the diffuse texture (colour map) - Texture2D texDiffuse = LoadTexture("resources/plasma.png"); + texDiffuse = LoadTexture("resources/plasma.png"); - Material* materials = model1.Materials; - MaterialMap* maps = materials[0].Maps; + var materials = model1.Materials; + var maps = materials[0].Maps; model1.Materials[0].Maps[(int)MaterialMapIndex.Albedo].Texture = texDiffuse; materials = model2.Materials; maps = materials[0].Maps; maps[(int)MaterialMapIndex.Albedo].Texture = texDiffuse; - // Using MAP_EMISSION as a spare slot to use for 2nd texture - // NOTE: Don't use MAP_IRRADIANCE, MAP_PREFILTER or MAP_CUBEMAP - // as they are bound as cube maps - Texture2D texMask = LoadTexture("resources/mask.png"); + // Using MATERIAL_MAP_EMISSION as a spare slot to use for 2nd texture + // NOTE: Don't use MATERIAL_MAP_IRRADIANCE, MATERIAL_MAP_PREFILTER or MATERIAL_MAP_CUBEMAP as they are bound as cube maps + texMask = LoadTexture("resources/mask.png"); materials = model1.Materials; maps = (MaterialMap*)materials[0].Maps; @@ -81,11 +103,11 @@ public class SimpleMask maps = (MaterialMap*)materials[0].Maps; maps[(int)MaterialMapIndex.Emission].Texture = texMask; - int* locs = shader.Locs; + var locs = shader.Locs; locs[(int)ShaderLocationIndex.MapEmission] = GetShaderLocation(shader, "mask"); // Frame is incremented each frame to animate the shader - int shaderFrame = GetShaderLocation(shader, "framesCounter"); + shaderFrame = GetShaderLocation(shader, "frame"); // Apply the shader to the two models materials = model1.Materials; @@ -94,69 +116,88 @@ public class SimpleMask materials = (Material*)model2.Materials; materials[0].Shader = shader; - int framesCounter = 0; + framesCounter = 0; + rotation = new(0, 0, 0); // Model rotation angles + } - // Model rotation angles - Vector3 rotation = new(0, 0, 0); + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.FirstPerson); - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- + framesCounter++; + rotation.X += 0.01f; + rotation.Y += 0.005f; + rotation.Z -= 0.0025f; - // Main game loop - while (!WindowShouldClose()) - { - // Update - //---------------------------------------------------------------------------------- - framesCounter++; - rotation.X += 0.01f; - rotation.Y += 0.005f; - rotation.Z -= 0.0025f; + // Send frames counter to shader for animation + Raylib.SetShaderValue(shader, shaderFrame, framesCounter, ShaderUniformDataType.Int); - // Send frames counter to shader for animation - Raylib.SetShaderValue(shader, shaderFrame, framesCounter, ShaderUniformDataType.Int); + // Rotate one of the models + model1.Transform = MatrixRotateXYZ(rotation); + //---------------------------------------------------------------------------------- - // Rotate one of the models - model1.Transform = MatrixRotateXYZ(rotation); + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.DarkBlue); - UpdateCamera(ref camera, CameraMode.Custom); - //---------------------------------------------------------------------------------- + BeginMode3D(camera); - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.DarkBlue); + DrawModel(model1, new Vector3(0.5f, 0, 0), 1, Color.White); + DrawModelEx(model2, new Vector3(-.5f, 0, 0), new Vector3(1, 1, 0), 50, new Vector3(1, 1, 1), Color.White); + DrawModel(model3, new Vector3(0, 0, -1.5f), 1, Color.White); + DrawGrid(10, 1.0f); // Draw a grid - BeginMode3D(camera); + EndMode3D(); - DrawModel(model1, new Vector3(0.5f, 0, 0), 1, Color.White); - DrawModelEx(model2, new Vector3(-.5f, 0, 0), new Vector3(1, 1, 0), 50, new Vector3(1, 1, 1), Color.White); - DrawModel(model3, new Vector3(0, 0, -1.5f), 1, Color.White); - DrawGrid(10, 1.0f); + var frameText = $"Frame: {framesCounter}"; + DrawRectangle(16, 698, MeasureText(frameText, 20) + 8, 42, Color.Blue); + DrawText(frameText, 20, 700, 20, Color.White); - EndMode3D(); + DrawFPS(10, 10); - string frameText = $"Frame: {framesCounter}"; - DrawRectangle(16, 698, MeasureText(frameText, 20) + 8, 42, Color.Blue); - DrawText(frameText, 20, 700, 20, Color.White); + EndDrawing(); + //---------------------------------------------------------------------------------- + } - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- + public void Unload() + { UnloadModel(model1); UnloadModel(model2); UnloadModel(model3); - UnloadTexture(texDiffuse); - UnloadTexture(texMask); + UnloadTexture(texDiffuse); // Unload default diffuse texture + UnloadTexture(texMask); // Unload texture mask - UnloadShader(shader); + UnloadShader(shader); // Unload shader + } - CloseWindow(); + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - simple mask"); + + DisableCursor(); // Limit cursor to relative movement inside the window + SetTargetFPS(60); // Set to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + var game = new SimpleMask(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + game.Update(); + } + + game.Unload(); + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shaders/Spotlight.cs b/Examples/Shaders/Spotlight.cs index cf86e3a..b8e37c4 100644 --- a/Examples/Shaders/Spotlight.cs +++ b/Examples/Shaders/Spotlight.cs @@ -1,29 +1,32 @@ /******************************************************************************************* * -* raylib [shaders] example - Simple shader mask +* raylib [shaders] example - spotlight rendering * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Example contributed by Chris Camacho (@chriscamacho - http://bedroomcoders.co.uk/) -* and reviewed by Ramon Santamaria (@raysan5) +* Example originally created with raylib 2.5, last time updated with raylib 3.7 * -* Copyright (c) 2019 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5) +* Example contributed by Chris Camacho (@chriscamacho) 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) 2019-2025 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5) * ******************************************************************************************** * -* The shader makes alpha holes in the forground to give the apearance of a top +* The shader makes alpha holes in the forground to give the appearance of a top * down look at a spotlight casting a pool of light... * * The right hand side of the screen there is just enough light to see whats * going on without the spot light, great for a stealth type game where you -* have to avoid the spotlights. +* have to avoid the spotlights * -* The left hand side of the screen is in pitch dark except for where the spotlights are. +* The left hand side of the screen is in pitch dark except for where the spotlights are * * Although this example doesn't scale like the letterbox example, you could integrate * the two techniques, but by scaling the actual colour of the render texture rather -* than using alpha as a mask. +* than using alpha as a mask * ********************************************************************************************/ @@ -33,14 +36,29 @@ using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class Spotlight +public class Spotlight : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + // NOTE: It must be the same as define in shader - const int MaxSpots = 3; - const int MaxStars = 400; + private const int MaxSpots = 3; + private const int MaxStars = 400; + + public string Name => "Shaders / Spotlight"; + + public string Title => "raylib [shaders] example - spotlight rendering"; + + public bool CursorHidden => true; // Spot data - struct Spot + private struct Spot { public Vector2 pos; public Vector2 vel; @@ -54,53 +72,51 @@ public class Spotlight } // Stars in the star field have a position and velocity - struct Star + private struct Star { public Vector2 pos; public Vector2 vel; } - public static int Main() + private Texture2D texRay; + private Star[] stars; + private int frameCounter; + private Shader shdrSpot; + private Spot[] spots; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + texRay = LoadTexture("resources/raysan.png"); - InitWindow(screenWidth, screenHeight, "raylib - shader spotlight"); - HideCursor(); + stars = new Star[MaxStars]; - Texture2D texRay = LoadTexture("resources/raysan.png"); - - Star[] stars = new Star[MaxStars]; - - for (int n = 0; n < MaxStars; n++) + for (var n = 0; n < MaxStars; n++) { ResetStar(ref stars[n]); } // Progress all the stars on, so they don't all start in the centre - for (int m = 0; m < screenWidth / 2.0; m++) + for (var m = 0; m < screenWidth / 2.0; m++) { - for (int n = 0; n < MaxStars; n++) + for (var n = 0; n < MaxStars; n++) { UpdateStar(ref stars[n]); } } - int frameCounter = 0; + frameCounter = 0; // Use default vert shader - Shader shdrSpot = LoadShader(null, "resources/shaders/glsl330/spotlight.fs"); + shdrSpot = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/spotlight.fs"); // Get the locations of spots in the shader - Spot[] spots = new Spot[MaxSpots]; + spots = new Spot[MaxSpots]; - for (int i = 0; i < MaxSpots; i++) + for (var i = 0; i < MaxSpots; i++) { - string posName = $"spots[{i}].pos"; - string innerName = $"spots[{i}].inner"; - string radiusName = $"spots[{i}].radius"; + var posName = $"spots[{i}].pos"; + var innerName = $"spots[{i}].inner"; + var radiusName = $"spots[{i}].radius"; spots[i].posLoc = GetShaderLocation(shdrSpot, posName); spots[i].innerLoc = GetShaderLocation(shdrSpot, innerName); @@ -108,14 +124,14 @@ public class Spotlight } // Tell the shader how wide the screen is so we can have - // a pitch Color.black half and a dimly lit half. - int wLoc = GetShaderLocation(shdrSpot, "screenWidth"); - float sw = (float)GetScreenWidth(); + // a pitch black half and a dimly lit half + var wLoc = GetShaderLocation(shdrSpot, "screenWidth"); + var sw = (float)GetScreenWidth(); Raylib.SetShaderValue(shdrSpot, wLoc, sw, ShaderUniformDataType.Float); - // Randomise the locations and velocities of the spotlights - // and initialise the shader locations - for (int i = 0; i < MaxSpots; i++) + // Randomize the locations and velocities of the spotlights + // and initialize the shader locations + for (var i = 0; i < MaxSpots; i++) { spots[i].pos.X = GetRandomValue(64, screenWidth - 64); spots[i].pos.Y = GetRandomValue(64, screenHeight - 64); @@ -123,8 +139,8 @@ public class Spotlight while ((MathF.Abs(spots[i].vel.X) + MathF.Abs(spots[i].vel.Y)) < 2) { - spots[i].vel.X = GetRandomValue(-40, 40) / 10.0f; - spots[i].vel.Y = GetRandomValue(-40, 40) / 10.0f; + spots[i].vel.X = GetRandomValue(-400, 40) / 25.0f; + spots[i].vel.Y = GetRandomValue(-400, 40) / 25.0f; } spots[i].inner = 28.0f * (i + 1); @@ -149,118 +165,110 @@ public class Spotlight ShaderUniformDataType.Float ); } - - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) - { - // Update - //---------------------------------------------------------------------------------- - frameCounter++; - - // Move the stars, resetting them if the go offscreen - for (int n = 0; n < MaxStars; n++) - { - UpdateStar(ref stars[n]); - } - - // Update the spots, send them to the shader - for (int i = 0; i < MaxSpots; i++) - { - if (i == 0) - { - Vector2 mp = GetMousePosition(); - spots[i].pos.X = mp.X; - spots[i].pos.Y = screenHeight - mp.Y; - } - else - { - spots[i].pos.X += spots[i].vel.X; - spots[i].pos.Y += spots[i].vel.Y; - - if (spots[i].pos.X < 64) - { - spots[i].vel.X = -spots[i].vel.X; - } - - if (spots[i].pos.X > (screenWidth - 64)) - { - spots[i].vel.X = -spots[i].vel.X; - } - - if (spots[i].pos.Y < 64) - { - spots[i].vel.Y = -spots[i].vel.Y; - } - - if (spots[i].pos.Y > (screenHeight - 64)) - { - spots[i].vel.Y = -spots[i].vel.Y; - } - } - - Raylib.SetShaderValue( - shdrSpot, - spots[i].posLoc, - spots[i].pos, - ShaderUniformDataType.Vec2 - ); - } - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.DarkBlue); - - // Draw stars and bobs - for (int n = 0; n < MaxStars; n++) - { - // MathF.Single pixel is just too small these days! - DrawRectangle((int)stars[n].pos.X, (int)stars[n].pos.Y, 2, 2, Color.White); - } - - for (int i = 0; i < 16; i++) - { - DrawTexture( - texRay, - (int)((screenWidth / 2.0) + MathF.Cos((frameCounter + i * 8) / 51.45f) * (screenWidth / 2.2) - 32), - (int)((screenHeight / 2.0) + MathF.Sin((frameCounter + i * 8) / 17.87f) * (screenHeight / 4.2)), - Color.White - ); - } - - // Draw spot lights - BeginShaderMode(shdrSpot); - - // Instead of a blank rectangle you could render a render texture of the full screen used to do screen - // scaling (slight adjustment to shader would be required to actually pay attention to the colour!) - DrawRectangle(0, 0, screenWidth, screenHeight, Color.White); - EndShaderMode(); - - DrawFPS(10, 10); - - DrawText("Move the mouse!", 10, 30, 20, Color.Green); - DrawText("Pitch Color.Black", (int)(screenWidth * 0.2f), screenHeight / 2, 20, Color.Green); - DrawText("Dark", (int)(screenWidth * 0.66f), screenHeight / 2, 20, Color.Green); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - UnloadTexture(texRay); - UnloadShader(shdrSpot); - - CloseWindow(); - //-------------------------------------------------------------------------------------- - - return 0; } - static void ResetStar(ref Star s) + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + frameCounter++; + + // Move the stars, resetting them if the go offscreen + for (var n = 0; n < MaxStars; n++) + { + UpdateStar(ref stars[n]); + } + + // Update the spots, send them to the shader + for (var i = 0; i < MaxSpots; i++) + { + if (i == 0) + { + var mp = GetMousePosition(); + spots[i].pos.X = mp.X; + spots[i].pos.Y = screenHeight - mp.Y; + } + else + { + spots[i].pos.X += spots[i].vel.X; + spots[i].pos.Y += spots[i].vel.Y; + + if (spots[i].pos.X < 64) + { + spots[i].vel.X = -spots[i].vel.X; + } + + if (spots[i].pos.X > (screenWidth - 64)) + { + spots[i].vel.X = -spots[i].vel.X; + } + + if (spots[i].pos.Y < 64) + { + spots[i].vel.Y = -spots[i].vel.Y; + } + + if (spots[i].pos.Y > (screenHeight - 64)) + { + spots[i].vel.Y = -spots[i].vel.Y; + } + } + + Raylib.SetShaderValue( + shdrSpot, + spots[i].posLoc, + spots[i].pos, + ShaderUniformDataType.Vec2 + ); + } + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.DarkBlue); + + // Draw stars and bobs + for (var n = 0; n < MaxStars; n++) + { + // Single pixel is just too small these days! + DrawRectangle((int)stars[n].pos.X, (int)stars[n].pos.Y, 2, 2, Color.White); + } + + for (var i = 0; i < 16; i++) + { + DrawTexture( + texRay, + (int)((screenWidth / 2.0) + MathF.Cos((frameCounter + i * 8) / 51.45f) * (screenWidth / 2.2) - 32), + (int)((screenHeight / 2.0) + MathF.Sin((frameCounter + i * 8) / 17.87f) * (screenHeight / 4.2)), + Color.White + ); + } + + // Draw spot lights + BeginShaderMode(shdrSpot); + + // Instead of a blank rectangle you could render a render texture of the full screen used to do screen + // scaling (slight adjustment to shader would be required to actually pay attention to the colour!) + DrawRectangle(0, 0, screenWidth, screenHeight, Color.White); + EndShaderMode(); + + DrawFPS(10, 10); + + DrawText("Move the mouse!", 10, 30, 20, Color.Green); + DrawText("Pitch Black", (int)(screenWidth * 0.2f), screenHeight / 2, 20, Color.Green); + DrawText("Dark", (int)(screenWidth * 0.66f), screenHeight / 2, 20, Color.Green); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texRay); + UnloadShader(shdrSpot); + } + + private static void ResetStar(ref Star s) { s.pos = new Vector2(GetScreenWidth() / 2.0f, GetScreenHeight() / 2.0f); @@ -270,10 +278,10 @@ public class Spotlight s.vel.Y = (float)GetRandomValue(-1000, 1000) / 100.0f; } while (!((MathF.Abs(s.vel.X) + (MathF.Abs(s.vel.Y)) > 1))); - s.pos += s.pos + (s.vel * new Vector2(8.0f, 8.0f)); + s.pos += s.vel * new Vector2(8.0f, 8.0f); } - static void UpdateStar(ref Star s) + private static void UpdateStar(ref Star s) { s.pos += s.vel; @@ -283,4 +291,33 @@ public class Spotlight ResetStar(ref s); } } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - spotlight rendering"); + HideCursor(); + + SetTargetFPS(60); // Set to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + var game = new Spotlight(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + game.Update(); + } + + game.Unload(); + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); + //-------------------------------------------------------------------------------------- + + return 0; + } } diff --git a/Examples/Shaders/TextureDrawing.cs b/Examples/Shaders/TextureDrawing.cs index 001e900..1d70b32 100644 --- a/Examples/Shaders/TextureDrawing.cs +++ b/Examples/Shaders/TextureDrawing.cs @@ -1,15 +1,17 @@ /******************************************************************************************* * -* raylib [textures] example - Texture drawing +* raylib [shaders] example - texture rendering * -* This example illustrates how to draw on a blank texture using a shader +* Example complexity rating: [★★☆☆] 2/4 * -* This example has been created using raylib 2.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 2.0, last time updated with raylib 3.7 * -* Example contributed by Michał Ciesielski and reviewed by Ramon Santamaria (@raysan5) +* Example contributed by Michał Ciesielski (@ciessielski) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2019 Michał Ciesielski and 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) 2019-2025 Michał Ciesielski (@ciessielski) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -17,68 +19,92 @@ using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class TextureDrawing +public class TextureDrawing : IExample { - const int GlslVersion = 330; + private const int screenWidth = 800; + private const int screenHeight = 450; + +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + public string Name => "Shaders / Texture Drawing"; + + public string Title => "raylib [shaders] example - texture rendering"; + + private Texture2D texture; + private Shader shader; + private float time; + private int timeLoc; + + public void Init() + { + var imBlank = GenImageColor(1024, 1024, Color.Blank); + texture = LoadTextureFromImage(imBlank); // Load blank texture to fill on shader + UnloadImage(imBlank); + + // NOTE: Using GLSL 330 shader version, on OpenGL ES 2.0 use GLSL 100 shader version + shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/cubes_panning.fs"); + + time = 0.0f; + timeLoc = GetShaderLocation(shader, "uTime"); + Raylib.SetShaderValue(shader, timeLoc, time, ShaderUniformDataType.Float); + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + time = (float)GetTime(); + Raylib.SetShaderValue(shader, timeLoc, time, ShaderUniformDataType.Float); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginShaderMode(shader); // Enable our custom shader for next shapes/textures drawings + DrawTexture(texture, 0, 0, Color.White); // Drawing BLANK texture, all rendering magic happens on shader + EndShaderMode(); // Disable our custom shader, return to default shader + + DrawText("BACKGROUND is PAINTED and ANIMATED on SHADER!", 10, 10, 20, Color.Maroon); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadShader(shader); + UnloadTexture(texture); + } public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture rendering"); - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture drawing"); - - // Load blank texture to fill on shader - Image imBlank = GenImageColor(1024, 1024, Color.Blank); - Texture2D texture = LoadTextureFromImage(imBlank); - UnloadImage(imBlank); - - // NOTE: Using GLSL 330 shader version, on OpenGL ES 2.0 use GLSL 100 shader version - Shader shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/cubes_panning.fs"); - - float time = 0.0f; - int timeLoc = GetShaderLocation(shader, "uTime"); - Raylib.SetShaderValue(shader, timeLoc, time, ShaderUniformDataType.Float); - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new TextureDrawing(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - time = (float)GetTime(); - Raylib.SetShaderValue(shader, timeLoc, time, ShaderUniformDataType.Float); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // Enable our custom shader for next shapes/textures drawings - BeginShaderMode(shader); - - // Drawing blank texture, all magic happens on shader - DrawTexture(texture, 0, 0, Color.White); - - // Disable our custom shader, return to default shader - EndShaderMode(); - - DrawText("BACKGROUND is PAINTED and ANIMATED on SHADER!", 10, 10, 20, Color.Maroon); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadShader(shader); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shaders/TextureOutline.cs b/Examples/Shaders/TextureOutline.cs index 4023d5c..63cf304 100644 --- a/Examples/Shaders/TextureOutline.cs +++ b/Examples/Shaders/TextureOutline.cs @@ -1,15 +1,20 @@ /******************************************************************************************* * -* raylib [textures] example - Texture drawing +* raylib [shaders] example - texture outline * -* This example illustrates how to draw on a blank texture using a shader +* Example complexity rating: [★★★☆] 3/4 * -* This example has been created using raylib 2.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * -* Example contributed by Michał Ciesielski and reviewed by Ramon Santamaria (@raysan5) +* Example originally created with raylib 4.0, last time updated with raylib 4.0 * -* Copyright (c) 2019 Michał Ciesielski and Ramon Santamaria (@raysan5) +* Example contributed by Serenity Skiff (@GoldenThumbs) 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) 2021-2025 Serenity Skiff (@GoldenThumbs) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -17,32 +22,41 @@ using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class TextureOutline +public class TextureOutline : IExample { - const int GLSL_VERSION = 330; + private const int screenWidth = 800; + private const int screenHeight = 450; - public static int Main() +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + public string Name => "Shaders / Texture Outline"; + + public string Title => "raylib [shaders] example - texture outline"; + + private Texture2D texture; + private Shader shdrOutline; + private float outlineSize; + private int outlineSizeLoc; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + texture = LoadTexture("resources/fudesumi.png"); + shdrOutline = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/outline.fs"); - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - Apply an outline to a texture"); + outlineSize = 2.0f; - Texture2D texture = LoadTexture("resources/fudesumi.png"); - Shader shdrOutline = LoadShader(null, $"resources/shaders/glsl{GLSL_VERSION}/outline.fs"); - - float outlineSize = 2.0f; - - // Normalized red color - float[] outlineColor = new[] { 1.0f, 0.0f, 0.0f, 1.0f }; + // Normalized RED color + var outlineColor = new[] { 1.0f, 0.0f, 0.0f, 1.0f }; float[] textureSize = { (float)texture.Width, (float)texture.Height }; // Get shader locations - int outlineSizeLoc = GetShaderLocation(shdrOutline, "outlineSize"); - int outlineColorLoc = GetShaderLocation(shdrOutline, "outlineColor"); - int textureSizeLoc = GetShaderLocation(shdrOutline, "textureSize"); + outlineSizeLoc = GetShaderLocation(shdrOutline, "outlineSize"); + var outlineColorLoc = GetShaderLocation(shdrOutline, "outlineColor"); + var textureSizeLoc = GetShaderLocation(shdrOutline, "textureSize"); // Set shader values (they can be changed later) Raylib.SetShaderValue( @@ -63,55 +77,75 @@ public class TextureOutline textureSize, ShaderUniformDataType.Vec2 ); + } - SetTargetFPS(60); + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + outlineSize += GetMouseWheelMove(); + if (outlineSize < 1.0f) + { + outlineSize = 1.0f; + } + + Raylib.SetShaderValue( + shdrOutline, + outlineSizeLoc, + outlineSize, + ShaderUniformDataType.Float + ); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(Color.RayWhite); + + BeginShaderMode(shdrOutline); + DrawTexture(texture, GetScreenWidth() / 2 - texture.Width / 2, -30, Color.White); + EndShaderMode(); + + DrawText("Shader-based\ntexture\noutline", 10, 10, 20, Color.Gray); + DrawText("Scroll mouse wheel to\nchange outline size", 10, 72, 20, Color.Gray); + DrawText($"Outline size: {(int)outlineSize} px", 10, 120, 20, Color.Maroon); + + DrawFPS(710, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texture); + UnloadShader(shdrOutline); + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture outline"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new TextureOutline(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - outlineSize += GetMouseWheelMove(); - if (outlineSize < 1.0f) - { - outlineSize = 1.0f; - } - - Raylib.SetShaderValue( - shdrOutline, - outlineSizeLoc, - outlineSize, - ShaderUniformDataType.Float - ); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(Color.RayWhite); - - BeginShaderMode(shdrOutline); - DrawTexture(texture, GetScreenWidth() / 2 - texture.Width / 2, -30, Color.White); - EndShaderMode(); - - DrawText("Shader-based\ntexture\noutline", 10, 10, 20, Color.Gray); - - DrawText($"Outline size: {outlineSize} px", 10, 120, 20, Color.Maroon); - - DrawFPS(710, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texture); - UnloadShader(shdrOutline); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shaders/TextureWaves.cs b/Examples/Shaders/TextureWaves.cs index 8446a92..bb49258 100644 --- a/Examples/Shaders/TextureWaves.cs +++ b/Examples/Shaders/TextureWaves.cs @@ -1,20 +1,24 @@ /******************************************************************************************* * -* raylib [shaders] example - Texture Waves +* raylib [shaders] example - texture waves +* +* Example complexity rating: [★★☆☆] 2/4 * * NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support, -* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version. +* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version * * NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example * on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders * raylib comes with shaders ready for both versions, check raylib/shaders install folder * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 2.5, last time updated with raylib 3.7 * * Example contributed by Anata (@anatagawa) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2019 Anata (@anatagawa) and 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) 2019-2025 Anata (@anatagawa) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -22,40 +26,49 @@ using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class TextureWaves +public class TextureWaves : IExample { - const int GlslVersion = 330; + private const int screenWidth = 800; + private const int screenHeight = 450; - public static int Main() +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + public string Name => "Shaders / Texture Waves"; + + public string Title => "raylib [shaders] example - texture waves"; + + private Texture2D texture; + private Shader shader; + private int secondsLoc; + private float seconds; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture waves"); - // Load texture texture to apply shaders - Texture2D texture = LoadTexture("resources/space.png"); + texture = LoadTexture("resources/space.png"); // Load shader and setup location points and values - Shader shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/wave.fs"); + shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/wave.fs"); - int secondsLoc = GetShaderLocation(shader, "secondes"); - int freqXLoc = GetShaderLocation(shader, "freqX"); - int freqYLoc = GetShaderLocation(shader, "freqY"); - int ampXLoc = GetShaderLocation(shader, "ampX"); - int ampYLoc = GetShaderLocation(shader, "ampY"); - int speedXLoc = GetShaderLocation(shader, "speedX"); - int speedYLoc = GetShaderLocation(shader, "speedY"); + secondsLoc = GetShaderLocation(shader, "seconds"); + var freqXLoc = GetShaderLocation(shader, "freqX"); + var freqYLoc = GetShaderLocation(shader, "freqY"); + var ampXLoc = GetShaderLocation(shader, "ampX"); + var ampYLoc = GetShaderLocation(shader, "ampY"); + var speedXLoc = GetShaderLocation(shader, "speedX"); + var speedYLoc = GetShaderLocation(shader, "speedY"); // Shader uniform values that can be updated at any time - float freqX = 25.0f; - float freqY = 25.0f; - float ampX = 5.0f; - float ampY = 5.0f; - float speedX = 8.0f; - float speedY = 8.0f; + var freqX = 25.0f; + var freqY = 25.0f; + var ampX = 5.0f; + var ampY = 5.0f; + var speedX = 8.0f; + var speedY = 8.0f; float[] screenSize = { (float)GetScreenWidth(), (float)GetScreenHeight() }; Raylib.SetShaderValue( @@ -71,43 +84,63 @@ public class TextureWaves Raylib.SetShaderValue(shader, speedXLoc, speedX, ShaderUniformDataType.Float); Raylib.SetShaderValue(shader, speedYLoc, speedY, ShaderUniformDataType.Float); - float seconds = 0.0f; + seconds = 0.0f; + } - SetTargetFPS(60); + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + seconds += GetFrameTime(); + + Raylib.SetShaderValue(shader, secondsLoc, seconds, ShaderUniformDataType.Float); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + BeginShaderMode(shader); + + DrawTexture(texture, 0, 0, Color.White); + DrawTexture(texture, texture.Width, 0, Color.White); + + EndShaderMode(); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadShader(shader); // Unload shader + UnloadTexture(texture); // Unload texture + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture waves"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new TextureWaves(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - seconds += GetFrameTime(); - - Raylib.SetShaderValue(shader, secondsLoc, seconds, ShaderUniformDataType.Float); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - BeginShaderMode(shader); - - DrawTexture(texture, 0, 0, Color.White); - DrawTexture(texture, texture.Width, 0, Color.White); - - EndShaderMode(); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadShader(shader); - UnloadTexture(texture); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shaders/WriteDepth.cs b/Examples/Shaders/WriteDepth.cs index 7b6a6ac..e2c917d 100644 --- a/Examples/Shaders/WriteDepth.cs +++ b/Examples/Shaders/WriteDepth.cs @@ -1,6 +1,8 @@ /******************************************************************************************* * -* raylib [shaders] example - Depth buffer writing +* raylib [shaders] example - depth writing +* +* Example complexity rating: [★★☆☆] 2/4 * * Example originally created with raylib 4.2, last time updated with raylib 4.2 * @@ -9,7 +11,7 @@ * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * -* Copyright (c) 2022-2023 Buğra Alptekin Sarı (@BugraAlptekinSari) +* Copyright (c) 2022-2025 Buğra Alptekin Sarı (@BugraAlptekinSari) * ********************************************************************************************/ @@ -18,86 +20,115 @@ using static Raylib_cs.Raylib; namespace Examples.Shaders; -public class WriteDepth +public class WriteDepth : IExample { - const int GLSL_VERSION = 330; + private const int screenWidth = 800; + private const int screenHeight = 450; + +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + public string Name => "Shaders / Write Depth"; + + public string Title => "raylib [shaders] example - depth writing"; + + private Camera3D camera; + private RenderTexture2D target; + private Shader shader; + + public void Init() + { + // Define the camera to look into our 3d world + camera = new(); + camera.Position = new Vector3(2.0f, 2.0f, 3.0f); // Camera position + camera.Target = new Vector3(0.0f, 0.5f, 0.0f); // Camera looking at point + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target) + camera.FovY = 45.0f; // Camera field-of-view Y + camera.Projection = CameraProjection.Perspective; // Camera projection type + + // Load custom render texture with writable depth texture buffer + target = LoadRenderTextureDepthTex(screenWidth, screenHeight); + + // Load depth writing shader + // NOTE: The shader inverts the depth buffer by writing into it by `gl_FragDepth = 1 - gl_FragCoord.z;` + shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/depth_write.fs"); + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(ref camera, CameraMode.Orbital); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + + // Draw into our custom render texture + BeginTextureMode(target); + ClearBackground(Color.White); + + BeginMode3D(camera); + BeginShaderMode(shader); + + DrawCubeWiresV(new Vector3(0.0f, 0.5f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Red); + DrawCubeV(new Vector3(0.0f, 0.5f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Purple); + DrawCubeWiresV(new Vector3(0.0f, 0.5f, -1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.DarkGreen); + DrawCubeV(new Vector3(0.0f, 0.5f, -1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Yellow); + DrawGrid(10, 1.0f); + + EndShaderMode(); + EndMode3D(); + EndTextureMode(); + + // Draw into screen our custom render texture + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawTextureRec( + target.Texture, + new Rectangle(0, 0, screenWidth, -screenHeight), + Vector2.Zero, + Color.White + ); + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadRenderTextureDepthTex(target); + UnloadShader(shader); + } public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - depth writing"); - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - write depth buffer"); - - // The shader inverts the depth buffer by writing into it by `gl_FragDepth = 1 - gl_FragCoord.z;` - Shader shader = LoadShader(null, $"resources/shaders/glsl{GLSL_VERSION}/write_depth.fs"); - - // Use customized function to create writable depth texture buffer - RenderTexture2D target = LoadRenderTextureDepthTex(screenWidth, screenHeight); - - // Define the camera to look into our 3d world - Camera3D camera; - camera.Position = new Vector3(2.0f, 2.0f, 3.0f); - camera.Target = new Vector3(0.0f, 0.5f, 0.0f); - camera.Up = new Vector3(0.0f, 1.0f, 0.0f); - camera.FovY = 45.0f; - camera.Projection = CameraProjection.Perspective; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new WriteDepth(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - UpdateCamera(ref camera, CameraMode.Orbital); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - - // Draw into our custom render texture (framebuffer) - BeginTextureMode(target); - ClearBackground(Color.White); - - BeginMode3D(camera); - BeginShaderMode(shader); - - DrawCubeWiresV(new Vector3(0.0f, 0.5f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Red); - DrawCubeV(new Vector3(0.0f, 0.5f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Purple); - DrawCubeWiresV(new Vector3(0.0f, 0.5f, -1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.DarkGreen); - DrawCubeV(new Vector3(0.0f, 0.5f, -1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Yellow); - DrawGrid(10, 1.0f); - - EndShaderMode(); - EndMode3D(); - EndTextureMode(); - - // Draw custom render texture - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawTextureRec( - target.Texture, - new Rectangle(0, 0, screenWidth, -screenHeight), - Vector2.Zero, - Color.White - ); - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadRenderTextureDepthTex(target); - UnloadShader(shader); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; @@ -152,7 +183,7 @@ public class WriteDepth ); // Check if fbo is complete with attachments (valid) - if (Rlgl.FramebufferComplete(target.Id)) + if (Rlgl.FramebufferComplete(target.Id) != 0) { TraceLog(TraceLogLevel.Info, $"FBO: [ID {target.Id}] Framebuffer object created successfully"); } diff --git a/Examples/Shapes/BasicShapes.cs b/Examples/Shapes/BasicShapes.cs index 6846847..57f425c 100644 --- a/Examples/Shapes/BasicShapes.cs +++ b/Examples/Shapes/BasicShapes.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [shapes] example - Draw basic shapes 2d (rectangle, circle, line...) +* raylib [shapes] example - basic shapes * -* This example has been created using raylib 1.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 * -* Copyright (c) 2014 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.0, last time updated with raylib 4.2 +* +* 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,66 +18,101 @@ using static Raylib_cs.Raylib; namespace Examples.Shapes; -public class BasicShapes +public partial class BasicShapes : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shapes / Basic Shapes"; + + public string Title => "raylib [shapes] example - basic shapes"; + + private float rotation; + + public void Init() + { + rotation = 0.0f; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + rotation += 0.2f; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("some basic shapes available on raylib", 20, 20, 20, Color.DarkGray); + + // Circle shapes and lines + DrawCircle(screenWidth / 5, 120, 35, Color.DarkBlue); + DrawCircleGradient(new Vector2(screenWidth / 5.0f, 220.0f), 60, Color.Green, Color.SkyBlue); + DrawCircleLines(screenWidth / 5, 340, 80, Color.DarkBlue); + DrawEllipse(screenWidth / 5, 120, 25, 20, Color.Yellow); + DrawEllipseLines(screenWidth / 5, 120, 30, 25, Color.Yellow); + + // Rectangle shapes and lines + DrawRectangle(screenWidth / 4 * 2 - 60, 100, 120, 60, Color.Red); + DrawRectangleGradientH(screenWidth / 4 * 2 - 90, 170, 180, 130, Color.Maroon, Color.Gold); + DrawRectangleLines(screenWidth / 4 * 2 - 40, 320, 80, 60, Color.Orange); // NOTE: Uses QUADS internally, not lines + + // Triangle shapes and lines + DrawTriangle( + new Vector2(screenWidth / 4.0f * 3.0f, 80.0f), + new Vector2(screenWidth / 4.0f * 3.0f - 60.0f, 150.0f), + new Vector2(screenWidth / 4.0f * 3.0f + 60.0f, 150.0f), Color.Violet + ); + + DrawTriangleLines( + new Vector2(screenWidth / 4.0f * 3.0f, 160.0f), + new Vector2(screenWidth / 4.0f * 3.0f - 20.0f, 230.0f), + new Vector2(screenWidth / 4.0f * 3.0f + 20.0f, 230.0f), Color.DarkBlue + ); + + // Polygon shapes and lines + DrawPoly(new Vector2(screenWidth / 4.0f * 3, 330), 6, 80, rotation, Color.Brown); + DrawPolyLines(new Vector2(screenWidth / 4.0f * 3, 330), 6, 90, rotation, Color.Brown); + DrawPolyLinesEx(new Vector2(screenWidth / 4.0f * 3, 330), 6, 85, rotation, 6, Color.Beige); + + // NOTE: We draw all LINES based shapes together to optimize internal drawing, + // this way, all LINES are rendered in a single draw pass + DrawLine(18, 42, screenWidth - 18, 42, Color.Black); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - basic shapes"); - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - basic shapes drawing"); - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new BasicShapes(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update your variables here - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("some basic shapes available on raylib", 20, 20, 20, Color.DarkGray); - - DrawLine(18, 42, screenWidth - 18, 42, Color.Black); - - DrawCircle(screenWidth / 4, 120, 35, Color.DarkBlue); - DrawCircleGradient(new Vector2(screenWidth / 4, 220), 60, Color.Green, Color.SkyBlue); - DrawCircleLines(screenWidth / 4, 340, 80, Color.DarkBlue); - - DrawRectangle(screenWidth / 4 * 2 - 60, 100, 120, 60, Color.Red); - DrawRectangleGradientH(screenWidth / 4 * 2 - 90, 170, 180, 130, Color.Maroon, Color.Gold); - DrawRectangleLines(screenWidth / 4 * 2 - 40, 320, 80, 60, Color.Orange); - - DrawTriangle( - new Vector2(screenWidth / 4 * 3, 80), - new Vector2(screenWidth / 4 * 3 - 60, 150), - new Vector2(screenWidth / 4 * 3 + 60, 150), Color.Violet - ); - - DrawTriangleLines( - new Vector2(screenWidth / 4 * 3, 160), - new Vector2(screenWidth / 4 * 3 - 20, 230), - new Vector2(screenWidth / 4 * 3 + 20, 230), Color.DarkBlue - ); - - DrawPoly(new Vector2(screenWidth / 4 * 3, 320), 6, 80, 0, Color.Brown); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shapes/BouncingBall.cs b/Examples/Shapes/BouncingBall.cs index 0771ef1..eca2249 100644 --- a/Examples/Shapes/BouncingBall.cs +++ b/Examples/Shapes/BouncingBall.cs @@ -2,10 +2,16 @@ * * raylib [shapes] example - bouncing ball * -* This example has been created using raylib 1.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 * -* Copyright (c) 2013 Ramon Santamaria (@raysan5) +* Example originally created with raylib 2.5, last time updated with raylib 2.5 +* +* Example contributed by Ramon Santamaria (@raysan5), reviewed by Jopestpe (@jopestpe) +* +* 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) 2013-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,83 +20,136 @@ using static Raylib_cs.Raylib; namespace Examples.Shapes; -public class BouncingBall +public partial class BouncingBall : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shapes / Bouncing Ball"; + + public string Title => "raylib [shapes] example - bouncing ball"; + + public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint; + + private Vector2 ballPosition; + private Vector2 ballSpeed; + private int ballRadius; + private float gravity; + + private bool useGravity; + private bool pause; + private int framesCounter; + + public void Init() + { + ballPosition = new(GetScreenWidth() / 2.0f, GetScreenHeight() / 2.0f); + ballSpeed = new(5.0f, 4.0f); + ballRadius = 20; + gravity = 0.2f; + + useGravity = true; + pause = false; + framesCounter = 0; + } + + public void Update() + { + // Update + //----------------------------------------------------- + if (IsKeyPressed(KeyboardKey.G)) + { + useGravity = !useGravity; + } + if (IsKeyPressed(KeyboardKey.Space)) + { + pause = !pause; + } + + if (!pause) + { + ballPosition.X += ballSpeed.X; + ballPosition.Y += ballSpeed.Y; + + if (useGravity) + { + ballSpeed.Y += gravity; + } + + // Check walls collision for bouncing + if ((ballPosition.X >= (GetScreenWidth() - ballRadius)) || (ballPosition.X <= ballRadius)) + { + ballSpeed.X *= -1.0f; + } + if ((ballPosition.Y >= (GetScreenHeight() - ballRadius)) || (ballPosition.Y <= ballRadius)) + { + ballSpeed.Y *= -0.95f; + } + } + else + { + framesCounter++; + } + //----------------------------------------------------- + + // Draw + //----------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawCircleV(ballPosition, (float)ballRadius, Color.Maroon); + DrawText("PRESS SPACE to PAUSE BALL MOVEMENT", 10, GetScreenHeight() - 25, 20, Color.LightGray); + + if (useGravity) + { + DrawText("GRAVITY: ON (Press G to disable)", 10, GetScreenHeight() - 50, 20, Color.DarkGreen); + } + else + { + DrawText("GRAVITY: OFF (Press G to enable)", 10, GetScreenHeight() - 50, 20, Color.Red); + } + + // On pause, we draw a blinking message + if (pause && ((framesCounter / 30) % 2) != 0) + { + DrawText("PAUSED", 350, 200, 30, Color.Gray); + } + + DrawFPS(10, 10); + + EndDrawing(); + //----------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //--------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - + SetConfigFlags(ConfigFlags.Msaa4xHint); InitWindow(screenWidth, screenHeight, "raylib [shapes] example - bouncing ball"); - Vector2 ballPosition = new(GetScreenWidth() / 2, GetScreenHeight() / 2); - Vector2 ballSpeed = new(5.0f, 4.0f); - int ballRadius = 20; - - bool pause = false; - int framesCounter = 0; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //---------------------------------------------------------- + var game = new BouncingBall(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //----------------------------------------------------- - if (IsKeyPressed(KeyboardKey.Space)) - { - pause = !pause; - } - - if (!pause) - { - ballPosition.X += ballSpeed.X; - ballPosition.Y += ballSpeed.Y; - - // Check walls collision for bouncing - if ((ballPosition.X >= (GetScreenWidth() - ballRadius)) || (ballPosition.X <= ballRadius)) - { - ballSpeed.X *= -1.0f; - } - if ((ballPosition.Y >= (GetScreenHeight() - ballRadius)) || (ballPosition.Y <= ballRadius)) - { - ballSpeed.Y *= -1.0f; - } - } - else - { - framesCounter += 1; - } - //----------------------------------------------------- - - // Draw - //----------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawCircleV(ballPosition, ballRadius, Color.Maroon); - DrawText("PRESS SPACE to PAUSE BALL MOVEMENT", 10, GetScreenHeight() - 25, 20, Color.LightGray); - - // On pause, we draw a blinking message - if (pause && ((framesCounter / 30) % 2) == 0) - { - DrawText("PAUSED", 350, 200, 30, Color.Gray); - } - DrawFPS(10, 10); - - EndDrawing(); - //----------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //--------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //---------------------------------------------------------- return 0; } } - diff --git a/Examples/Shapes/CollisionArea.cs b/Examples/Shapes/CollisionArea.cs index 05ba2f0..6b01cd5 100644 --- a/Examples/Shapes/CollisionArea.cs +++ b/Examples/Shapes/CollisionArea.cs @@ -2,10 +2,14 @@ * * raylib [shapes] example - collision area * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) +* Example originally created with raylib 2.5, last time updated with raylib 2.5 +* +* 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) 2013-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -13,124 +17,156 @@ using static Raylib_cs.Raylib; namespace Examples.Shapes; -public class CollisionArea +public partial class CollisionArea : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shapes / Collision Area"; + + public string Title => "raylib [shapes] example - collision area"; + + private Rectangle boxA; + private int boxASpeedX; + private Rectangle boxB; + private Rectangle boxCollision; + private int screenUpperLimit; + private bool pause; + private bool collision; + + public void Init() + { + // Box A: Moving box + boxA = new(10, GetScreenHeight() / 2.0f - 50, 200, 100); + boxASpeedX = 4; + + // Box B: Mouse moved box + boxB = new(GetScreenWidth() / 2.0f - 30, GetScreenHeight() / 2.0f - 30, 60, 60); + + boxCollision = new(); // Collision rectangle + + screenUpperLimit = 40; // Top menu limits + + pause = false; // Movement pause + collision = false; // Collision detection + } + + public void Update() + { + // Update + //----------------------------------------------------- + // Move box if not paused + if (!pause) + { + boxA.X += boxASpeedX; + } + + // Bounce box on x screen limits + if (((boxA.X + boxA.Width) >= GetScreenWidth()) || (boxA.X <= 0)) + { + boxASpeedX *= -1; + } + + // Update player-controlled-box (box02) + boxB.X = GetMouseX() - boxB.Width / 2; + boxB.Y = GetMouseY() - boxB.Height / 2; + + // Make sure Box B does not go out of move area limits + if ((boxB.X + boxB.Width) >= GetScreenWidth()) + { + boxB.X = GetScreenWidth() - boxB.Width; + } + else if (boxB.X <= 0) + { + boxB.X = 0; + } + + if ((boxB.Y + boxB.Height) >= GetScreenHeight()) + { + boxB.Y = GetScreenHeight() - boxB.Height; + } + else if (boxB.Y <= screenUpperLimit) + { + boxB.Y = screenUpperLimit; + } + + // Check boxes collision + collision = CheckCollisionRecs(boxA, boxB); + + // Get collision rectangle (only on collision) + if (collision) + { + boxCollision = GetCollisionRec(boxA, boxB); + } + + // Pause Box A movement + if (IsKeyPressed(KeyboardKey.Space)) + { + pause = !pause; + } + //----------------------------------------------------- + + // Draw + //----------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawRectangle(0, 0, screenWidth, screenUpperLimit, collision ? Color.Red : Color.Black); + + DrawRectangleRec(boxA, Color.Gold); + DrawRectangleRec(boxB, Color.Blue); + + if (collision) + { + // Draw collision area + DrawRectangleRec(boxCollision, Color.Lime); + + // Draw collision message + var cx = GetScreenWidth() / 2 - MeasureText("COLLISION!", 20) / 2; + var cy = screenUpperLimit / 2 - 10; + DrawText("COLLISION!", cx, cy, 20, Color.Black); + + // Draw collision area + var text = $"Collision Area: {(int)boxCollision.Width * (int)boxCollision.Height}"; + DrawText(text, GetScreenWidth() / 2 - 100, screenUpperLimit + 10, 20, Color.Black); + } + + // Draw help instructions + DrawText("Press SPACE to PAUSE/RESUME", 20, screenHeight - 35, 20, Color.LightGray); + + DrawFPS(10, 10); + + EndDrawing(); + //----------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //--------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - collision area"); - // Box A: Moving box - Rectangle boxA = new(10, GetScreenHeight() / 2 - 50, 200, 100); - int boxASpeedX = 4; - - // Box B: Mouse moved box - Rectangle boxB = new(GetScreenWidth() / 2 - 30, GetScreenHeight() / 2 - 30, 60, 60); - Rectangle boxCollision = new(); - - int screenUpperLimit = 40; - - // Movement pause - bool pause = false; - bool collision = false; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //---------------------------------------------------------- + var game = new CollisionArea(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //----------------------------------------------------- - // Move box if not paused - if (!pause) - { - boxA.X += boxASpeedX; - } - - // Bounce box on x screen limits - if (((boxA.X + boxA.Width) >= GetScreenWidth()) || (boxA.X <= 0)) - { - boxASpeedX *= -1; - } - - // Update player-controlled-box (box02) - boxB.X = GetMouseX() - boxB.Width / 2; - boxB.Y = GetMouseY() - boxB.Height / 2; - - // Make sure Box B does not go out of move area limits - if ((boxB.X + boxB.Width) >= GetScreenWidth()) - { - boxB.X = GetScreenWidth() - boxB.Width; - } - else if (boxB.X <= 0) - { - boxB.X = 0; - } - - if ((boxB.Y + boxB.Height) >= GetScreenHeight()) - { - boxB.Y = GetScreenHeight() - boxB.Height; - } - else if (boxB.Y <= screenUpperLimit) - { - boxB.Y = screenUpperLimit; - } - - // Check boxes collision - collision = CheckCollisionRecs(boxA, boxB); - - // Get collision rectangle (only on collision) - if (collision) - { - boxCollision = GetCollisionRec(boxA, boxB); - } - - // Pause Box A movement - if (IsKeyPressed(KeyboardKey.Space)) - { - pause = !pause; - } - //----------------------------------------------------- - - // Draw - //----------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawRectangle(0, 0, screenWidth, screenUpperLimit, collision ? Color.Red : Color.Black); - - DrawRectangleRec(boxA, Color.Gold); - DrawRectangleRec(boxB, Color.Blue); - - if (collision) - { - // Draw collision area - DrawRectangleRec(boxCollision, Color.Lime); - - // Draw collision message - int cx = GetScreenWidth() / 2 - MeasureText("COLLISION!", 20) / 2; - int cy = screenUpperLimit / 2 - 10; - DrawText("COLLISION!", cx, cy, 20, Color.Black); - - // Draw collision area - string text = $"Collision Area: {(int)boxCollision.Width * (int)boxCollision.Height}"; - DrawText(text, GetScreenWidth() / 2 - 100, screenUpperLimit + 10, 20, Color.Black); - } - - DrawFPS(10, 10); - - EndDrawing(); - //----------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //--------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //---------------------------------------------------------- return 0; diff --git a/Examples/Shapes/ColorsPalette.cs b/Examples/Shapes/ColorsPalette.cs index bb3a9c2..9ada68a 100644 --- a/Examples/Shapes/ColorsPalette.cs +++ b/Examples/Shapes/ColorsPalette.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [shapes] example - Colors palette +* raylib [shapes] example - colors palette * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.0, last time updated with raylib 2.5 +* +* 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,72 +18,80 @@ using static Raylib_cs.Raylib; namespace Examples.Shapes; -public class ColorsPalette +public partial class ColorsPalette : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public const int MaxColorsCount = 21; // Number of colors available + + public string Name => "Shapes / Colors Palette"; + + public string Title => "raylib [shapes] example - colors palette"; + + private Color[] colors; + private string[] colorNames; + private Rectangle[] colorsRecs; + private int[] colorState; + private Vector2 mousePoint; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - colors palette"); - - Color[] colors = new[] + colors = new[] { - Color.DarkGray, - Color.Maroon, - Color.Orange, - Color.DarkGreen, - Color.DarkBlue, - Color.DarkPurple, - Color.DarkBrown, - Color.Gray, - Color.Red, - Color.Gold, - Color.Lime, - Color.Blue, - Color.Violet, - Color.Brown, - Color.LightGray, - Color.Pink, - Color.Yellow, - Color.Green, - Color.SkyBlue, - Color.Purple, - Color.Beige - }; + Color.DarkGray, + Color.Maroon, + Color.Orange, + Color.DarkGreen, + Color.DarkBlue, + Color.DarkPurple, + Color.DarkBrown, + Color.Gray, + Color.Red, + Color.Gold, + Color.Lime, + Color.Blue, + Color.Violet, + Color.Brown, + Color.LightGray, + Color.Pink, + Color.Yellow, + Color.Green, + Color.SkyBlue, + Color.Purple, + Color.Beige + }; - string[] colorNames = new[] + colorNames = new[] { - "DARKGRAY", - "MAROON", - "ORANGE", - "DARKGREEN", - "DARKBLUE", - "DARKPURPLE", - "DARKBROWN", - "GRAY", - "RED", - "GOLD", - "LIME", - "BLUE", - "VIOLET", - "BROWN", - "LIGHTGRAY", - "PINK", - "YELLOW", - "GREEN", - "SKYBLUE", - "PURPLE", - "BEIGE" - }; + "DARKGRAY", + "MAROON", + "ORANGE", + "DARKGREEN", + "DARKBLUE", + "DARKPURPLE", + "DARKBROWN", + "GRAY", + "RED", + "GOLD", + "LIME", + "BLUE", + "VIOLET", + "BROWN", + "LIGHTGRAY", + "PINK", + "YELLOW", + "GREEN", + "SKYBLUE", + "PURPLE", + "BEIGE" + }; // Rectangles array - Rectangle[] colorsRecs = new Rectangle[colors.Length]; + colorsRecs = new Rectangle[colors.Length]; // Fills colorsRecs data (for every rectangle) - for (int i = 0; i < colorsRecs.Length; i++) + for (var i = 0; i < colorsRecs.Length; i++) { colorsRecs[i].X = 20 + 100 * (i % 7) + 10 * (i % 7); colorsRecs[i].Y = 80 + 100 * (i / 7) + 10 * (i / 7); @@ -88,82 +100,101 @@ public class ColorsPalette } // Color state: 0-DEFAULT, 1-MOUSE_HOVER - int[] colorState = new int[colors.Length]; + colorState = new int[colors.Length]; - Vector2 mousePoint = new(0.0f, 0.0f); + mousePoint = new(0.0f, 0.0f); + } - SetTargetFPS(60); + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + mousePoint = GetMousePosition(); + + for (var i = 0; i < colors.Length; i++) + { + if (CheckCollisionPointRec(mousePoint, colorsRecs[i])) + { + colorState[i] = 1; + } + else + { + colorState[i] = 0; + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("raylib colors palette", 28, 42, 20, Color.Black); + DrawText( + "press SPACE to see all colors", + GetScreenWidth() - 180, + GetScreenHeight() - 40, + 10, + Color.Gray + ); + + for (var i = 0; i < colorsRecs.Length; i++) // Draw all rectangles + { + DrawRectangleRec(colorsRecs[i], Fade(colors[i], colorState[i] != 0 ? 0.6f : 1.0f)); + + if (IsKeyDown(KeyboardKey.Space) || colorState[i] != 0) + { + DrawRectangle( + (int)colorsRecs[i].X, + (int)(colorsRecs[i].Y + colorsRecs[i].Height - 26), + (int)colorsRecs[i].Width, + 20, + Color.Black + ); + DrawRectangleLinesEx(colorsRecs[i], 6, Fade(Color.Black, 0.3f)); + DrawText( + colorNames[i], + (int)(colorsRecs[i].X + colorsRecs[i].Width - MeasureText(colorNames[i], 10) - 12), + (int)(colorsRecs[i].Y + colorsRecs[i].Height - 20), + 10, + colors[i] + ); + } + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - colors palette"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new ColorsPalette(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - mousePoint = GetMousePosition(); - - for (int i = 0; i < colors.Length; i++) - { - if (CheckCollisionPointRec(mousePoint, colorsRecs[i])) - { - colorState[i] = 1; - } - else - { - colorState[i] = 0; - } - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("raylib colors palette", 28, 42, 20, Color.Black); - DrawText( - "press SPACE to see all colors", - GetScreenWidth() - 180, - GetScreenHeight() - 40, - 10, - Color.Gray - ); - - // Draw all rectangles - for (int i = 0; i < colorsRecs.Length; i++) - { - DrawRectangleRec(colorsRecs[i], ColorAlpha(colors[i], colorState[i] != 0 ? 0.6f : 1.0f)); - - if (IsKeyDown(KeyboardKey.Space) || colorState[i] != 0) - { - DrawRectangle( - (int)colorsRecs[i].X, - (int)(colorsRecs[i].Y + colorsRecs[i].Height - 26), - (int)colorsRecs[i].Width, - 20, - Color.Black - ); - DrawRectangleLinesEx(colorsRecs[i], 6, ColorAlpha(Color.Black, 0.3f)); - DrawText( - colorNames[i], - (int)(colorsRecs[i].X + colorsRecs[i].Width - MeasureText(colorNames[i], 10) - 12), - (int)(colorsRecs[i].Y + colorsRecs[i].Height - 20), - 10, - colors[i] - ); - } - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } } - diff --git a/Examples/Shapes/DrawCircleSector.cs b/Examples/Shapes/DrawCircleSector.cs index 7178fc3..88044be 100644 --- a/Examples/Shapes/DrawCircleSector.cs +++ b/Examples/Shapes/DrawCircleSector.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [shapes] example - draw circle sector (with gui options) +* raylib [shapes] example - circle sector drawing * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 2.5, last time updated with raylib 2.5 * * Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2018 Vlad Adrian (@demizdor) and 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) 2018-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -17,79 +21,106 @@ using static Raylib_cs.Raylib; namespace Examples.Shapes; -public class DrawCircleSector +public partial class DrawCircleSector : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shapes / Draw Circle Sector"; + + public string Title => "raylib [shapes] example - circle sector drawing"; + + private Vector2 center; + private float outerRadius; + private float startAngle; + private float endAngle; + private float segments; + private float minSegments; + + public void Init() + { + center = new((GetScreenWidth() - 300) / 2.0f, GetScreenHeight() / 2.0f); + + outerRadius = 180.0f; + startAngle = 0.0f; + endAngle = 180.0f; + segments = 10.0f; + minSegments = 4; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + // NOTE: All variables update happens inside GUI control functions + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawLine(500, 0, 500, GetScreenHeight(), Fade(Color.LightGray, 0.6f)); + DrawRectangle(500, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(Color.LightGray, 0.3f)); + + DrawCircleSector(center, outerRadius, startAngle, endAngle, (int)segments, Fade(Color.Maroon, 0.3f)); + DrawCircleSectorLines( + center, + outerRadius, + startAngle, + endAngle, + (int)segments, + Fade(Color.Maroon, 0.6f) + ); + + // Draw GUI controls + //------------------------------------------------------------------------------ + /*GuiSliderBar(new Rectangle( 600, 40, 120, 20), "StartAngle", TextFormat("%.2f", startAngle), ref startAngle, 0, 720); + GuiSliderBar(new Rectangle( 600, 70, 120, 20), "EndAngle", TextFormat("%.2f", endAngle), ref endAngle, 0, 720); + + GuiSliderBar(new Rectangle( 600, 140, 120, 20), "Radius", TextFormat("%.2f", outerRadius), ref outerRadius, 0, 200); + GuiSliderBar(new Rectangle( 600, 170, 120, 20), "Segments", TextFormat("%.2f", segments), ref segments, 0, 100);*/ + //------------------------------------------------------------------------------ + + minSegments = MathF.Truncate(MathF.Ceiling((endAngle - startAngle) / 90)); + var color = (segments >= minSegments) ? Color.Maroon : Color.DarkGray; + DrawText($"MODE: {((segments >= minSegments) ? "MANUAL" : "AUTO")}", 600, 200, 10, color); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - circle sector drawing"); - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - draw circle sector"); - - Vector2 center = new((GetScreenWidth() - 300) / 2, GetScreenHeight() / 2); - - float outerRadius = 180.0f; - int startAngle = 0; - int endAngle = 180; - int segments = 0; - int minSegments = 4; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new DrawCircleSector(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // NOTE: All variables update happens inside GUI control functions - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawLine(500, 0, 500, GetScreenHeight(), ColorAlpha(Color.LightGray, 0.6f)); - DrawRectangle(500, 0, GetScreenWidth() - 500, GetScreenHeight(), ColorAlpha(Color.LightGray, 0.3f)); - - DrawCircleSector(center, outerRadius, startAngle, endAngle, segments, ColorAlpha(Color.Maroon, 0.3f)); - DrawCircleSectorLines( - center, - outerRadius, - startAngle, - endAngle, - segments, - ColorAlpha(Color.Maroon, 0.6f) - ); - - // Draw GUI controls - //------------------------------------------------------------------------------ - /*startAngle = GuiSliderBar(new Rectangle( 600, 40, 120, 20), "StartAngle", startAngle, 0, 720, true ); - endAngle = GuiSliderBar(new Rectangle( 600, 70, 120, 20), "EndAngle", endAngle, 0, 720, true); - - outerRadius = GuiSliderBar(new Rectangle( 600, 140, 120, 20), "Radius", outerRadius, 0, 200, true); - segments = GuiSliderBar(new Rectangle( 600, 170, 120, 20), "Segments", segments, 0, 100, true);*/ - //------------------------------------------------------------------------------ - - minSegments = (int)MathF.Ceiling((endAngle - startAngle) / 90); - Color color = (segments >= minSegments) ? Color.Maroon : Color.DarkGray; - DrawText($"MODE: {((segments >= minSegments) ? "MANUAL" : "AUTO")}", 600, 270, 10, color); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } } - diff --git a/Examples/Shapes/DrawRectangleRounded.cs b/Examples/Shapes/DrawRectangleRounded.cs index 5190358..5700cf3 100644 --- a/Examples/Shapes/DrawRectangleRounded.cs +++ b/Examples/Shapes/DrawRectangleRounded.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [shapes] example - draw rectangle rounded (with gui options) +* raylib [shapes] example - rounded rectangle drawing * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 2.5, last time updated with raylib 2.5 * * Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2018 Vlad Adrian (@demizdor) and 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) 2018-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -15,91 +19,121 @@ using static Raylib_cs.Raylib; namespace Examples.Shapes; -public class DrawRectangleRounded +public partial class DrawRectangleRounded : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shapes / Draw Rectangle Rounded"; + + public string Title => "raylib [shapes] example - rounded rectangle drawing"; + + private float roundness; + private float width; + private float height; + private float segments; + private float lineThick; + + private bool drawRect; + private bool drawRoundedRect; + private bool drawRoundedLines; + + public void Init() + { + roundness = 0.2f; + width = 200.0f; + height = 100.0f; + segments = 0.0f; + lineThick = 1.0f; + + drawRect = false; + drawRoundedRect = true; + drawRoundedLines = false; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + Rectangle rec = new( + ((float)GetScreenWidth() - width - 250) / 2, + (GetScreenHeight() - height) / 2.0f, + (float)width, + (float)height + ); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawLine(560, 0, 560, GetScreenHeight(), Fade(Color.LightGray, 0.6f)); + DrawRectangle(560, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(Color.LightGray, 0.3f)); + + if (drawRect) + { + DrawRectangleRec(rec, Fade(Color.Gold, 0.6f)); + } + if (drawRoundedRect) + { + DrawRectangleRounded(rec, roundness, (int)segments, Fade(Color.Maroon, 0.2f)); + } + if (drawRoundedLines) + { + DrawRectangleRoundedLinesEx(rec, roundness, (int)segments, lineThick, Fade(Color.Maroon, 0.4f)); + } + + // Draw GUI controls + //------------------------------------------------------------------------------ + /*GuiSliderBar(new Rectangle( 640, 40, 105, 20 ), "Width", TextFormat("%.2f", width), ref width, 0, (float)GetScreenWidth() - 300); + GuiSliderBar(new Rectangle( 640, 70, 105, 20 ), "Height", TextFormat("%.2f", height), ref height, 0, (float)GetScreenHeight() - 50); + GuiSliderBar(new Rectangle( 640, 140, 105, 20 ), "Roundness", TextFormat("%.2f", roundness), ref roundness, 0.0f, 1.0f); + GuiSliderBar(new Rectangle( 640, 170, 105, 20 ), "Thickness", TextFormat("%.2f", lineThick), ref lineThick, 0, 20); + GuiSliderBar(new Rectangle( 640, 240, 105, 20), "Segments", TextFormat("%.2f", segments), ref segments, 0, 60); + + GuiCheckBox(new Rectangle( 640, 320, 20, 20 ), "DrawRoundedRect", ref drawRoundedRect); + GuiCheckBox(new Rectangle( 640, 350, 20, 20 ), "DrawRoundedLines", ref drawRoundedLines); + GuiCheckBox(new Rectangle( 640, 380, 20, 20), "DrawRect", ref drawRect);*/ + //------------------------------------------------------------------------------ + + var text = $"MODE: {((segments >= 4) ? "MANUAL" : "AUTO")}"; + DrawText(text, 640, 280, 10, (segments >= 4) ? Color.Maroon : Color.DarkGray); + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rounded rectangle drawing"); - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - draw rectangle rounded"); - - float roundness = 0.2f; - int width = 400; - int height = 200; - int segments = 0; - int lineThick = 10; - - bool drawRect = false; - bool drawRoundedRect = false; - bool drawRoundedLines = true; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new DrawRectangleRounded(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - Rectangle rec = new( - (GetScreenWidth() - width - 250) / 2.0f, - (GetScreenHeight() - height) / 2.0f, - (float)width, - (float)height - ); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawLine(560, 0, 560, GetScreenHeight(), ColorAlpha(Color.LightGray, 0.6f)); - DrawRectangle(560, 0, GetScreenWidth() - 500, GetScreenHeight(), ColorAlpha(Color.LightGray, 0.3f)); - - if (drawRect) - { - DrawRectangleRec(rec, ColorAlpha(Color.Gold, 0.6f)); - } - if (drawRoundedRect) - { - DrawRectangleRounded(rec, roundness, segments, ColorAlpha(Color.Maroon, 0.2f)); - } - if (drawRoundedLines) - { - DrawRectangleRoundedLinesEx(rec, roundness, segments, (float)lineThick, ColorAlpha(Color.Maroon, 0.4f)); - } - - // Draw GUI controls - //------------------------------------------------------------------------------ - /*width = GuiSliderBar(new Rectangle( 640, 40, 105, 20 ), "Width", width, 0, GetScreenWidth() - 300, true ); - height = GuiSliderBar(new Rectangle( 640, 70, 105, 20 ), "Height", height, 0, GetScreenHeight() - 50, true); - roundness = GuiSliderBar(new Rectangle( 640, 140, 105, 20 ), "Roundness", roundness, 0.0f, 1.0f, true); - lineThick = GuiSliderBar(new Rectangle( 640, 170, 105, 20 ), "Thickness", lineThick, 0, 20, true); - segments = GuiSliderBar(new Rectangle( 640, 240, 105, 20), "Segments", segments, 0, 60, true); - - drawRoundedRect = GuiCheckBox(new Rectangle( 640, 320, 20, 20 ), "DrawRoundedRect", drawRoundedRect); - drawRoundedLines = GuiCheckBox(new Rectangle( 640, 350, 20, 20 ), "DrawRoundedLines", drawRoundedLines); - drawRect = GuiCheckBox(new Rectangle( 640, 380, 20, 20), "DrawRect", drawRect);*/ - //------------------------------------------------------------------------------ - - string text = $"MODE: {((segments >= 4) ? "MANUAL" : "AUTO")}"; - DrawText(text, 640, 280, 10, (segments >= 4) ? Color.Maroon : Color.DarkGray); - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } } - diff --git a/Examples/Shapes/DrawRing.cs b/Examples/Shapes/DrawRing.cs index 2a53ce7..8a42c03 100644 --- a/Examples/Shapes/DrawRing.cs +++ b/Examples/Shapes/DrawRing.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [shapes] example - draw ring (with gui options) +* raylib [shapes] example - ring drawing * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 2.5, last time updated with raylib 2.5 * * Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2018 Vlad Adrian (@demizdor) and 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) 2018-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -17,114 +21,147 @@ using static Raylib_cs.Raylib; namespace Examples.Shapes; -public class DrawRing +public partial class DrawRing : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shapes / Draw Ring"; + + public string Title => "raylib [shapes] example - ring drawing"; + + private Vector2 center; + + private float innerRadius; + private float outerRadius; + + private float startAngle; + private float endAngle; + private float segments; + + private bool drawRing; + private bool drawRingLines; + private bool drawCircleLines; + + public void Init() + { + center = new((GetScreenWidth() - 300) / 2.0f, GetScreenHeight() / 2.0f); + + innerRadius = 80.0f; + outerRadius = 190.0f; + + startAngle = 0.0f; + endAngle = 360.0f; + segments = 0.0f; + + drawRing = true; + drawRingLines = false; + drawCircleLines = false; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + // NOTE: All variables update happens inside GUI control functions + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawLine(500, 0, 500, GetScreenHeight(), Fade(Color.LightGray, 0.6f)); + DrawRectangle(500, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(Color.LightGray, 0.3f)); + + if (drawRing) + { + DrawRing( + center, + innerRadius, + outerRadius, + startAngle, + endAngle, + (int)segments, + Fade(Color.Maroon, 0.3f) + ); + } + if (drawRingLines) + { + DrawRingLines( + center, + innerRadius, + outerRadius, + startAngle, + endAngle, + (int)segments, + Fade(Color.Black, 0.4f) + ); + } + if (drawCircleLines) + { + DrawCircleSectorLines( + center, + outerRadius, + startAngle, + endAngle, + (int)segments, + Fade(Color.Black, 0.4f) + ); + } + + // Draw GUI controls + //------------------------------------------------------------------------------ + /*GuiSliderBar(new Rectangle( 600, 40, 120, 20 ), "StartAngle", TextFormat("%.2f", startAngle), ref startAngle, -450, 450); + GuiSliderBar(new Rectangle( 600, 70, 120, 20 ), "EndAngle", TextFormat("%.2f", endAngle), ref endAngle, -450, 450); + + GuiSliderBar(new Rectangle( 600, 140, 120, 20 ), "InnerRadius", TextFormat("%.2f", innerRadius), ref innerRadius, 0, 100); + GuiSliderBar(new Rectangle( 600, 170, 120, 20 ), "OuterRadius", TextFormat("%.2f", outerRadius), ref outerRadius, 0, 200); + + GuiSliderBar(new Rectangle( 600, 240, 120, 20 ), "Segments", TextFormat("%.2f", segments), ref segments, 0, 100); + + GuiCheckBox(new Rectangle( 600, 320, 20, 20 ), "Draw Ring", ref drawRing); + GuiCheckBox(new Rectangle( 600, 350, 20, 20 ), "Draw RingLines", ref drawRingLines); + GuiCheckBox(new Rectangle( 600, 380, 20, 20 ), "Draw CircleLines", ref drawCircleLines);*/ + //------------------------------------------------------------------------------ + + var minSegments = (int)MathF.Ceiling((endAngle - startAngle) / 90); + var color = (segments >= minSegments) ? Color.Maroon : Color.DarkGray; + DrawText($"MODE: {((segments >= minSegments) ? "MANUAL" : "AUTO")}", 600, 270, 10, color); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - ring drawing"); - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - draw ring"); - - Vector2 center = new((GetScreenWidth() - 300) / 2, GetScreenHeight() / 2); - - float innerRadius = 80.0f; - float outerRadius = 190.0f; - - int startAngle = 0; - int endAngle = 360; - int segments = 0; - int minSegments = 4; - - bool drawRing = true; - bool drawRingLines = false; - bool drawCircleLines = false; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new DrawRing(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // NOTE: All variables update happens inside GUI control functions - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawLine(500, 0, 500, GetScreenHeight(), ColorAlpha(Color.LightGray, 0.6f)); - DrawRectangle(500, 0, GetScreenWidth() - 500, GetScreenHeight(), ColorAlpha(Color.LightGray, 0.3f)); - - if (drawRing) - { - DrawRing( - center, - innerRadius, - outerRadius, - startAngle, - endAngle, - segments, - ColorAlpha(Color.Maroon, 0.3f) - ); - } - if (drawRingLines) - { - DrawRingLines( - center, - innerRadius, - outerRadius, - startAngle, - endAngle, - segments, - ColorAlpha(Color.Black, 0.4f) - ); - } - if (drawCircleLines) - { - DrawCircleSectorLines( - center, - outerRadius, - startAngle, - endAngle, - segments, - ColorAlpha(Color.Black, 0.4f) - ); - } - - // Draw GUI controls - //------------------------------------------------------------------------------ - /*startAngle = GuiSliderBar(new Rectangle( 600, 40, 120, 20 ), "StartAngle", startAngle, -450, 450, true); - endAngle = GuiSliderBar(new Rectangle( 600, 70, 120, 20 ), "EndAngle", endAngle, -450, 450, true); - - innerRadius = GuiSliderBar(new Rectangle( 600, 140, 120, 20 ), "InnerRadius", innerRadius, 0, 100, true); - outerRadius = GuiSliderBar(new Rectangle( 600, 170, 120, 20 ), "OuterRadius", outerRadius, 0, 200, true); - - segments = GuiSliderBar(new Rectangle( 600, 240, 120, 20 ), "Segments", segments, 0, 100, true); - - drawRing = GuiCheckBox(new Rectangle( 600, 320, 20, 20 ), "Draw Ring", drawRing); - drawRingLines = GuiCheckBox(new Rectangle( 600, 350, 20, 20 ), "Draw RingLines", drawRingLines); - drawCircleLines = GuiCheckBox(new Rectangle( 600, 380, 20, 20 ), "Draw CircleLines", drawCircleLines);*/ - //------------------------------------------------------------------------------ - - minSegments = (int)MathF.Ceiling((endAngle - startAngle) / 90); - Color color = (segments >= minSegments) ? Color.Maroon : Color.DarkGray; - DrawText($"MODE: {((segments >= minSegments) ? "MANUAL" : "AUTO")}", 600, 270, 10, color); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shapes/EasingsBallAnim.cs b/Examples/Shapes/EasingsBallAnim.cs index 1052e8a..5aae342 100644 --- a/Examples/Shapes/EasingsBallAnim.cs +++ b/Examples/Shapes/EasingsBallAnim.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [shapes] example - easings ball anim +* raylib [shapes] example - easings ball * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* Example originally created with raylib 2.5, last time updated with raylib 2.5 +* +* 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,114 +18,138 @@ using Examples.Shared; namespace Examples.Shapes; -public class EasingsBallAnim +public partial class EasingsBallAnim : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shapes / Easings Ball Anim"; + + public string Title => "raylib [shapes] example - easings ball"; + + // Ball variable value to be animated with easings + private int ballPositionX; + private int ballRadius; + private float ballAlpha; + + private int state; + private int framesCounter; + + public void Init() + { + ballPositionX = -100; + ballRadius = 20; + ballAlpha = 0.0f; + + state = 0; + framesCounter = 0; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (state == 0) // Move ball position X with easing + { + framesCounter++; + ballPositionX = (int)Easings.EaseElasticOut(framesCounter, -100, screenWidth / 2.0f + 100, 120); + + if (framesCounter >= 120) + { + framesCounter = 0; + state = 1; + } + } + else if (state == 1) // Increase ball radius with easing + { + framesCounter++; + ballRadius = (int)Easings.EaseElasticIn(framesCounter, 20, 500, 200); + + if (framesCounter >= 200) + { + framesCounter = 0; + state = 2; + } + } + else if (state == 2) // Change ball alpha with easing (background color blending) + { + framesCounter++; + ballAlpha = Easings.EaseCubicOut(framesCounter, 0.0f, 1.0f, 200); + + if (framesCounter >= 200) + { + framesCounter = 0; + state = 3; + } + } + else if (state == 3) // Reset state to play again + { + if (IsKeyPressed(KeyboardKey.Enter)) + { + // Reset required variables to play again + ballPositionX = -100; + ballRadius = 20; + ballAlpha = 0.0f; + state = 0; + } + } + + if (IsKeyPressed(KeyboardKey.R)) + { + framesCounter = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + if (state >= 2) + { + DrawRectangle(0, 0, screenWidth, screenHeight, Color.Green); + } + + DrawCircle(ballPositionX, 200, ballRadius, Fade(Color.Red, 1.0f - ballAlpha)); + + if (state == 3) + { + DrawText("PRESS [ENTER] TO PLAY AGAIN!", 240, 200, 20, Color.Black); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings ball"); - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings ball anim"); - - // Ball variable value to be animated with easings - int ballPositionX = -100; - int ballRadius = 20; - float ballAlpha = 0.0f; - - int state = 0; - int framesCounter = 0; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new EasingsBallAnim(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (state == 0) // Move ball position X with easing - { - framesCounter += 1; - ballPositionX = (int)Easings.EaseElasticOut(framesCounter, -100, screenWidth / 2 + 100, 120); - - if (framesCounter >= 120) - { - framesCounter = 0; - state = 1; - } - } - // Increase ball radius with easing - else if (state == 1) - { - framesCounter += 1; - ballRadius = (int)Easings.EaseElasticIn(framesCounter, 20, 500, 200); - - if (framesCounter >= 200) - { - framesCounter = 0; - state = 2; - } - } - // Change ball alpha with easing (background color blending) - else if (state == 2) - { - framesCounter += 1; - ballAlpha = Easings.EaseCubicOut(framesCounter, 0.0f, 1.0f, 200); - - if (framesCounter >= 200) - { - framesCounter = 0; - state = 3; - } - } - // Reset state to play again - else if (state == 3) - { - if (IsKeyPressed(KeyboardKey.Enter)) - { - // Reset required variables to play again - ballPositionX = -100; - ballRadius = 20; - ballAlpha = 0.0f; - state = 0; - } - } - - if (IsKeyPressed(KeyboardKey.R)) - { - framesCounter = 0; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - if (state >= 2) - { - DrawRectangle(0, 0, screenWidth, screenHeight, Color.Green); - } - - DrawCircle(ballPositionX, 200, ballRadius, ColorAlpha(Color.Red, 1.0f - ballAlpha)); - - if (state == 3) - { - DrawText("PRESS [ENTER] TO PLAY AGAIN!", 240, 200, 20, Color.Black); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } } - diff --git a/Examples/Shapes/EasingsBoxAnim.cs b/Examples/Shapes/EasingsBoxAnim.cs index ce07558..6ce381e 100644 --- a/Examples/Shapes/EasingsBoxAnim.cs +++ b/Examples/Shapes/EasingsBoxAnim.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [shapes] example - easings box anim +* raylib [shapes] example - easings box * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* Example originally created with raylib 2.5, last time updated with raylib 2.5 +* +* 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -15,129 +19,152 @@ using Examples.Shared; namespace Examples.Shapes; -public class EasingsBoxAnim +public partial class EasingsBoxAnim : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shapes / Easings Box Anim"; + + public string Title => "raylib [shapes] example - easings box"; + + // Box variables to be animated with easings + private Rectangle rec; + private float rotation; + private float alpha; + + private int state; + private int framesCounter; + + public void Init() + { + rec = new(GetScreenWidth() / 2.0f, -100, 100, 100); + rotation = 0.0f; + alpha = 1.0f; + + state = 0; + framesCounter = 0; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + switch (state) + { + case 0: // Move box down to center of screen + framesCounter++; + + // NOTE: Remember that 3rd parameter of easing function refers to + // desired value variation, do not confuse it with expected final value! + rec.Y = Easings.EaseElasticOut(framesCounter, -100, GetScreenHeight() / 2.0f + 100, 120); + + if (framesCounter >= 120) + { + framesCounter = 0; + state = 1; + } + break; + case 1: // Scale box to an horizontal bar + framesCounter++; + rec.Height = Easings.EaseBounceOut(framesCounter, 100, -90, 120); + rec.Width = Easings.EaseBounceOut(framesCounter, 100, GetScreenWidth(), 120); + + if (framesCounter >= 120) + { + framesCounter = 0; + state = 2; + } + break; + case 2: // Rotate horizontal bar rectangle + framesCounter++; + rotation = Easings.EaseQuadOut(framesCounter, 0.0f, 270.0f, 240); + + if (framesCounter >= 240) + { + framesCounter = 0; + state = 3; + } + break; + case 3: // Increase bar size to fill all screen + framesCounter++; + rec.Height = Easings.EaseCircOut(framesCounter, 10, GetScreenWidth(), 120); + + if (framesCounter >= 120) + { + framesCounter = 0; + state = 4; + } + break; + case 4: // Fade out animation + framesCounter++; + alpha = Easings.EaseSineOut(framesCounter, 1.0f, -1.0f, 160); + + if (framesCounter >= 160) + { + framesCounter = 0; + state = 5; + } + break; + default: + break; + } + + // Reset animation at any moment + if (IsKeyPressed(KeyboardKey.Space)) + { + rec = new Rectangle(GetScreenWidth() / 2.0f, -100, 100, 100); + rotation = 0.0f; + alpha = 1.0f; + state = 0; + framesCounter = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawRectanglePro( + rec, + new Vector2(rec.Width / 2, rec.Height / 2), + rotation, + Fade(Color.Black, alpha) + ); + DrawText("PRESS [SPACE] TO RESET BOX ANIMATION!", 10, GetScreenHeight() - 25, 20, Color.LightGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings box"); - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings box anim"); - - // Box variables to be animated with easings - Rectangle rec = new(GetScreenWidth() / 2, -100, 100, 100); - float rotation = 0.0f; - float alpha = 1.0f; - - int state = 0; - int framesCounter = 0; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new EasingsBoxAnim(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - switch (state) - { - // Move box down to center of screen - case 0: - framesCounter += 1; - - // NOTE: Remember that 3rd parameter of easing function refers to - // desired value variation, do not confuse it with expected final value! - rec.Y = Easings.EaseElasticOut(framesCounter, -100, GetScreenHeight() / 2 + 100, 120); - - if (framesCounter >= 120) - { - framesCounter = 0; - state = 1; - } - break; - // Scale box to an horizontal bar - case 1: - framesCounter += 1; - rec.Height = Easings.EaseBounceOut(framesCounter, 100, -90, 120); - rec.Width = Easings.EaseBounceOut(framesCounter, 100, GetScreenWidth(), 120); - - if (framesCounter >= 120) - { - framesCounter = 0; - state = 2; - } - break; - // Rotate horizontal bar rectangle - case 2: - framesCounter += 1; - rotation = Easings.EaseQuadOut(framesCounter, 0.0f, 270.0f, 240); - - if (framesCounter >= 240) - { - framesCounter = 0; - state = 3; - } - break; - // Increase bar size to fill all screen - case 3: - framesCounter += 1; - rec.Height = Easings.EaseCircOut(framesCounter, 10, GetScreenWidth(), 120); - - if (framesCounter >= 120) - { - framesCounter = 0; - state = 4; - } - break; - // Fade out animation - case 4: - framesCounter++; - alpha = Easings.EaseSineOut(framesCounter, 1.0f, -1.0f, 160); - - if (framesCounter >= 160) - { - framesCounter = 0; - state = 5; - } - break; - default: - break; - } - - // Reset animation at any moment - if (IsKeyPressed(KeyboardKey.Space)) - { - rec = new Rectangle(GetScreenWidth() / 2, -100, 100, 100); - rotation = 0.0f; - alpha = 1.0f; - state = 0; - framesCounter = 0; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawRectanglePro( - rec, - new Vector2(rec.Width / 2, rec.Height / 2), - rotation, - ColorAlpha(Color.Black, alpha) - ); - DrawText("PRESS [SPACE] TO RESET BOX ANIMATION!", 10, GetScreenHeight() - 25, 20, Color.LightGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shapes/EasingsRectangleArray.cs b/Examples/Shapes/EasingsRectangleArray.cs index 6e74f52..f660c0b 100644 --- a/Examples/Shapes/EasingsRectangleArray.cs +++ b/Examples/Shapes/EasingsRectangleArray.cs @@ -1,14 +1,18 @@ /******************************************************************************************* * -* raylib [shapes] example - easings rectangle array +* raylib [shapes] example - easings rectangles +* +* Example complexity rating: [★★★☆] 3/4 * * NOTE: This example requires 'easings.h' library, provided on raylib/src. Just copy -* the library to same directory as example or make sure it's available on include path. +* the library to same directory as example or make sure it's available on include path * -* This example has been created using raylib 2.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 2.0, last time updated with raylib 2.5 * -* Copyright (c) 2014-2019 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -18,8 +22,11 @@ using Examples.Shared; namespace Examples.Shapes; -public class EasingsRectangleArray +public partial class EasingsRectangleArray : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + public const int RecsWidth = 50; public const int RecsHeight = 50; public const int MaxRecsX = 800 / RecsWidth; @@ -28,112 +35,133 @@ public class EasingsRectangleArray // At 60 fps = 4 seconds public const int PlayTimeInFrames = 240; - public static int Main() + public string Name => "Shapes / Easings Rectangle Array"; + + public string Title => "raylib [shapes] example - easings rectangles"; + + private Rectangle[] recs; + private float rotation; + private int framesCounter; + private int state; // Rectangles animation state: 0-Playing, 1-Finished + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + recs = new Rectangle[MaxRecsX * MaxRecsY]; - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings rectangle array"); - - Rectangle[] recs = new Rectangle[MaxRecsX * MaxRecsY]; - - for (int y = 0; y < MaxRecsY; y++) + for (var y = 0; y < MaxRecsY; y++) { - for (int x = 0; x < MaxRecsX; x++) + for (var x = 0; x < MaxRecsX; x++) { - recs[y * MaxRecsX + x].X = RecsWidth / 2 + RecsWidth * x; - recs[y * MaxRecsX + x].Y = RecsHeight / 2 + RecsHeight * y; + recs[y * MaxRecsX + x].X = RecsWidth / 2.0f + RecsWidth * x; + recs[y * MaxRecsX + x].Y = RecsHeight / 2.0f + RecsHeight * y; recs[y * MaxRecsX + x].Width = RecsWidth; recs[y * MaxRecsX + x].Height = RecsHeight; } } - float rotation = 0.0f; - int framesCounter = 0; + rotation = 0.0f; + framesCounter = 0; + state = 0; + } - // Rectangles animation state: 0-Playing, 1-Finished - int state = 0; + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (state == 0) + { + framesCounter++; - SetTargetFPS(60); + for (var i = 0; i < MaxRecsX * MaxRecsY; i++) + { + recs[i].Height = Easings.EaseCircOut(framesCounter, RecsHeight, -RecsHeight, PlayTimeInFrames); + recs[i].Width = Easings.EaseCircOut(framesCounter, RecsWidth, -RecsWidth, PlayTimeInFrames); + + if (recs[i].Height < 0) + { + recs[i].Height = 0; + } + if (recs[i].Width < 0) + { + recs[i].Width = 0; + } + + // Finish playing + if ((recs[i].Height == 0) && (recs[i].Width == 0)) + { + state = 1; + } + rotation = Easings.EaseLinearIn(framesCounter, 0.0f, 360.0f, PlayTimeInFrames); + } + } + else if ((state == 1) && IsKeyPressed(KeyboardKey.Space)) + { + // When animation has finished, press space to restart + framesCounter = 0; + + for (var i = 0; i < MaxRecsX * MaxRecsY; i++) + { + recs[i].Height = RecsHeight; + recs[i].Width = RecsWidth; + } + + state = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + if (state == 0) + { + for (var i = 0; i < MaxRecsX * MaxRecsY; i++) + { + DrawRectanglePro( + recs[i], + new Vector2(recs[i].Width / 2, recs[i].Height / 2), + rotation, + Color.Red + ); + } + } + else if (state == 1) + { + DrawText("PRESS [SPACE] TO PLAY AGAIN!", 240, 200, 20, Color.Gray); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings rectangles"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new EasingsRectangleArray(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (state == 0) - { - framesCounter++; - - for (int i = 0; i < MaxRecsX * MaxRecsY; i++) - { - recs[i].Height = Easings.EaseCircOut(framesCounter, RecsHeight, -RecsHeight, PlayTimeInFrames); - recs[i].Width = Easings.EaseCircOut(framesCounter, RecsWidth, -RecsWidth, PlayTimeInFrames); - - if (recs[i].Height < 0) - { - recs[i].Height = 0; - } - if (recs[i].Width < 0) - { - recs[i].Width = 0; - } - - // Finish playing - if ((recs[i].Height == 0) && (recs[i].Width == 0)) - { - state = 1; - } - rotation = Easings.EaseLinearIn(framesCounter, 0.0f, 360.0f, PlayTimeInFrames); - } - } - else if ((state == 1) && IsKeyPressed(KeyboardKey.Space)) - { - // When animation has finished, press space to restart - framesCounter = 0; - - for (int i = 0; i < MaxRecsX * MaxRecsY; i++) - { - recs[i].Height = RecsHeight; - recs[i].Width = RecsWidth; - } - - state = 0; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - if (state == 0) - { - for (int i = 0; i < MaxRecsX * MaxRecsY; i++) - { - DrawRectanglePro( - recs[i], - new Vector2(recs[i].Width / 2, recs[i].Height / 2), - rotation, - Color.Red - ); - } - } - else if (state == 1) - { - DrawText("PRESS [SPACE] TO PLAY AGAIN!", 240, 200, 20, Color.Gray); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shapes/FollowingEyes.cs b/Examples/Shapes/FollowingEyes.cs index 679ac3e..cea7b62 100644 --- a/Examples/Shapes/FollowingEyes.cs +++ b/Examples/Shapes/FollowingEyes.cs @@ -2,10 +2,14 @@ * * raylib [shapes] example - following eyes * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) +* Example originally created with raylib 2.5, last time updated with raylib 2.5 +* +* 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) 2013-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -16,92 +20,127 @@ using static Raylib_cs.Raylib; namespace Examples.Shapes; -public class FollowingEyes +public partial class FollowingEyes : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shapes / Following Eyes"; + + public string Title => "raylib [shapes] example - following eyes"; + + private Vector2 scleraLeftPosition; + private Vector2 scleraRightPosition; + private float scleraRadius; + + private Vector2 irisLeftPosition; + private Vector2 irisRightPosition; + private float irisRadius; + + private float angle; + private float dx, dy, dxx, dyy; + + public void Init() + { + scleraLeftPosition = new(GetScreenWidth() / 2.0f - 100.0f, GetScreenHeight() / 2.0f); + scleraRightPosition = new(GetScreenWidth() / 2.0f + 100.0f, GetScreenHeight() / 2.0f); + scleraRadius = 80; + + irisLeftPosition = new(GetScreenWidth() / 2.0f - 100.0f, GetScreenHeight() / 2.0f); + irisRightPosition = new(GetScreenWidth() / 2.0f + 100.0f, GetScreenHeight() / 2.0f); + irisRadius = 24; + + angle = 0.0f; + dx = 0.0f; + dy = 0.0f; + dxx = 0.0f; + dyy = 0.0f; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + irisLeftPosition = GetMousePosition(); + irisRightPosition = GetMousePosition(); + + // Check not inside the left eye sclera + if (!CheckCollisionPointCircle(irisLeftPosition, scleraLeftPosition, scleraRadius - irisRadius)) + { + dx = irisLeftPosition.X - scleraLeftPosition.X; + dy = irisLeftPosition.Y - scleraLeftPosition.Y; + + angle = MathF.Atan2(dy, dx); + + dxx = (scleraRadius - irisRadius) * MathF.Cos(angle); + dyy = (scleraRadius - irisRadius) * MathF.Sin(angle); + + irisLeftPosition.X = scleraLeftPosition.X + dxx; + irisLeftPosition.Y = scleraLeftPosition.Y + dyy; + } + + // Check not inside the right eye sclera + if (!CheckCollisionPointCircle(irisRightPosition, scleraRightPosition, scleraRadius - irisRadius)) + { + dx = irisRightPosition.X - scleraRightPosition.X; + dy = irisRightPosition.Y - scleraRightPosition.Y; + + angle = MathF.Atan2(dy, dx); + + dxx = (scleraRadius - irisRadius) * MathF.Cos(angle); + dyy = (scleraRadius - irisRadius) * MathF.Sin(angle); + + irisRightPosition.X = scleraRightPosition.X + dxx; + irisRightPosition.Y = scleraRightPosition.Y + dyy; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawCircleV(scleraLeftPosition, scleraRadius, Color.LightGray); + DrawCircleV(irisLeftPosition, irisRadius, Color.Brown); + DrawCircleV(irisLeftPosition, 10, Color.Black); + + DrawCircleV(scleraRightPosition, scleraRadius, Color.LightGray); + DrawCircleV(irisRightPosition, irisRadius, Color.DarkGreen); + DrawCircleV(irisRightPosition, 10, Color.Black); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - following eyes"); - Vector2 scleraLeftPosition = new(GetScreenWidth() / 2 - 100, GetScreenHeight() / 2); - Vector2 scleraRightPosition = new(GetScreenWidth() / 2 + 100, GetScreenHeight() / 2); - float scleraRadius = 80; - - Vector2 irisLeftPosition = new(GetScreenWidth() / 2 - 100, GetScreenHeight() / 2); - Vector2 irisRightPosition = new(GetScreenWidth() / 2 + 100, GetScreenHeight() / 2); - float irisRadius = 24; - - float angle = 0.0f; - float dx = 0.0f, dy = 0.0f, dxx = 0.0f, dyy = 0.0f; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new FollowingEyes(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - irisLeftPosition = GetMousePosition(); - irisRightPosition = GetMousePosition(); - - // Check not inside the left eye sclera - if (!CheckCollisionPointCircle(irisLeftPosition, scleraLeftPosition, scleraRadius - 20)) - { - dx = irisLeftPosition.X - scleraLeftPosition.X; - dy = irisLeftPosition.Y - scleraLeftPosition.Y; - - angle = MathF.Atan2(dy, dx); - - dxx = (scleraRadius - irisRadius) * MathF.Cos(angle); - dyy = (scleraRadius - irisRadius) * MathF.Sin(angle); - - irisLeftPosition.X = scleraLeftPosition.X + dxx; - irisLeftPosition.Y = scleraLeftPosition.Y + dyy; - } - - // Check not inside the right eye sclera - if (!CheckCollisionPointCircle(irisRightPosition, scleraRightPosition, scleraRadius - 20)) - { - dx = irisRightPosition.X - scleraRightPosition.X; - dy = irisRightPosition.Y - scleraRightPosition.Y; - - angle = MathF.Atan2(dy, dx); - - dxx = (scleraRadius - irisRadius) * MathF.Cos(angle); - dyy = (scleraRadius - irisRadius) * MathF.Sin(angle); - - irisRightPosition.X = scleraRightPosition.X + dxx; - irisRightPosition.Y = scleraRightPosition.Y + dyy; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawCircleV(scleraLeftPosition, scleraRadius, Color.LightGray); - DrawCircleV(irisLeftPosition, irisRadius, Color.Brown); - DrawCircleV(irisLeftPosition, 10, Color.Black); - - DrawCircleV(scleraRightPosition, scleraRadius, Color.LightGray); - DrawCircleV(irisRightPosition, irisRadius, Color.DarkGreen); - DrawCircleV(irisRightPosition, 10, Color.Black); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shapes/LinesBezier.cs b/Examples/Shapes/LinesBezier.cs index 7861de4..0f8daa8 100644 --- a/Examples/Shapes/LinesBezier.cs +++ b/Examples/Shapes/LinesBezier.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [shapes] example - Cubic-bezier lines +* raylib [shapes] example - lines bezier * -* This example has been created using raylib 1.7 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 * -* Copyright (c) 2017 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.7, last time updated with raylib 1.7 +* +* 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) 2017-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,54 +18,110 @@ using static Raylib_cs.Raylib; namespace Examples.Shapes; -public class LinesBezier +public partial class LinesBezier : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shapes / Lines Bezier"; + + public string Title => "raylib [shapes] example - lines bezier"; + + public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint; + + private Vector2 startPoint; + private Vector2 endPoint; + private bool moveStartPoint; + private bool moveEndPoint; + + public void Init() + { + startPoint = new(30, 30); + endPoint = new(screenWidth - 30, screenHeight - 30); + moveStartPoint = false; + moveEndPoint = false; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + var mouse = GetMousePosition(); + + if (CheckCollisionPointCircle(mouse, startPoint, 10.0f) && IsMouseButtonDown(MouseButton.Left)) + { + moveStartPoint = true; + } + else if (CheckCollisionPointCircle(mouse, endPoint, 10.0f) && IsMouseButtonDown(MouseButton.Left)) + { + moveEndPoint = true; + } + + if (moveStartPoint) + { + startPoint = mouse; + if (IsMouseButtonReleased(MouseButton.Left)) + { + moveStartPoint = false; + } + } + + if (moveEndPoint) + { + endPoint = mouse; + if (IsMouseButtonReleased(MouseButton.Left)) + { + moveEndPoint = false; + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("MOVE START-END POINTS WITH MOUSE", 15, 20, 20, Color.Gray); + + // Draw line Cubic Bezier, in-out interpolation (easing), no control points + DrawLineBezier(startPoint, endPoint, 4.0f, Color.Blue); + + // Draw start-end spline circles with some details + DrawCircleV(startPoint, CheckCollisionPointCircle(mouse, startPoint, 10.0f) ? 14.0f : 8.0f, moveStartPoint ? Color.Red : Color.Blue); + DrawCircleV(endPoint, CheckCollisionPointCircle(mouse, endPoint, 10.0f) ? 14.0f : 8.0f, moveEndPoint ? Color.Red : Color.Blue); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - SetConfigFlags(ConfigFlags.Msaa4xHint); - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - cubic-bezier lines"); + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - lines bezier"); - Vector2 start = new(0, 0); - Vector2 end = new(screenWidth, screenHeight); - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new LinesBezier(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (IsMouseButtonDown(MouseButton.Left)) - { - start = GetMousePosition(); - } - else if (IsMouseButtonDown(MouseButton.Right)) - { - end = GetMousePosition(); - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("USE MOUSE LEFT-RIGHT CLICK to DEFINE LINE START and END POINTS", 15, 20, 20, Color.Gray); - DrawLineBezier(start, end, 2.0f, Color.Red); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shapes/LogoRaylibAnim.cs b/Examples/Shapes/LogoRaylibAnim.cs index 7309e5b..4c2f022 100644 --- a/Examples/Shapes/LogoRaylibAnim.cs +++ b/Examples/Shapes/LogoRaylibAnim.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [shapes] example - raylib logo animation +* raylib [shapes] example - logo raylib anim * -* This example has been created using raylib 1.4 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2014 Ramon Santamaria (@raysan5) +* Example originally created with raylib 2.5, last time updated with raylib 4.0 +* +* 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -13,179 +17,207 @@ using static Raylib_cs.Raylib; namespace Examples.Shapes; -public class LogoRaylibAnim +public partial class LogoRaylibAnim : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shapes / Logo Raylib Anim"; + + public string Title => "raylib [shapes] example - logo raylib anim"; + + private int logoPositionX; + private int logoPositionY; + + private int framesCounter; + private int lettersCount; + + private int topSideRecWidth; + private int leftSideRecHeight; + + private int bottomSideRecWidth; + private int rightSideRecHeight; + + private int state; // Tracking animation states (State Machine) + private float alpha; // Useful for fading + + private Color outline; + + public void Init() + { + logoPositionX = screenWidth / 2 - 128; + logoPositionY = screenHeight / 2 - 128; + + framesCounter = 0; + lettersCount = 0; + + topSideRecWidth = 16; + leftSideRecHeight = 16; + + bottomSideRecWidth = 16; + rightSideRecHeight = 16; + + state = 0; + alpha = 1.0f; + + outline = new(139, 71, 135, 255); + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (state == 0) // State 0: Small box blinking + { + framesCounter++; + + if (framesCounter == 120) + { + state = 1; + framesCounter = 0; // Reset counter... will be used later... + } + } + else if (state == 1) // State 1: Top and left bars growing + { + topSideRecWidth += 4; + leftSideRecHeight += 4; + + if (topSideRecWidth == 256) + { + state = 2; + } + } + else if (state == 2) // State 2: Bottom and right bars growing + { + bottomSideRecWidth += 4; + rightSideRecHeight += 4; + + if (bottomSideRecWidth == 256) + { + state = 3; + } + } + else if (state == 3) // State 3: Letters appearing (one by one) + { + framesCounter++; + + // Every 12 frames, one more letter! + if (framesCounter / 12 != 0) + { + lettersCount++; + framesCounter = 0; + } + + // When all letters have appeared, just fade out everything + if (lettersCount >= 10) + { + alpha -= 0.02f; + + if (alpha <= 0.0f) + { + alpha = 0.0f; + state = 4; + } + } + } + else if (state == 4) // State 4: Reset and Replay + { + if (IsKeyPressed(KeyboardKey.R)) + { + framesCounter = 0; + lettersCount = 0; + + topSideRecWidth = 16; + leftSideRecHeight = 16; + + bottomSideRecWidth = 16; + rightSideRecHeight = 16; + + alpha = 1.0f; + state = 0; // Return to State 0 + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + if (state == 0) + { + if ((framesCounter / 15) % 2 != 0) + { + DrawRectangle(logoPositionX, logoPositionY, 16, 16, outline); + } + } + else if (state == 1) + { + DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, outline); + DrawRectangle(logoPositionX, logoPositionY, 16, leftSideRecHeight, outline); + } + else if (state == 2) + { + DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, outline); + DrawRectangle(logoPositionX, logoPositionY, 16, leftSideRecHeight, outline); + + DrawRectangle(logoPositionX + 240, logoPositionY, 16, rightSideRecHeight, outline); + DrawRectangle(logoPositionX, logoPositionY + 240, bottomSideRecWidth, 16, outline); + } + else if (state == 3) + { + var outlineFade = Fade(outline, alpha); + DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, outlineFade); + DrawRectangle(logoPositionX, logoPositionY + 16, 16, leftSideRecHeight - 32, outlineFade); + + DrawRectangle(logoPositionX + 240, logoPositionY + 16, 16, rightSideRecHeight - 32, outlineFade); + DrawRectangle(logoPositionX, logoPositionY + 240, bottomSideRecWidth, 16, outlineFade); + + var whiteFade = Fade(Color.RayWhite, alpha); + DrawRectangle(screenWidth / 2 - 112, screenHeight / 2 - 112, 224, 224, whiteFade); + + var label = Fade(new Color(155, 79, 151, 255), alpha); + var text = "raylib".SubText(0, lettersCount); + DrawText(text, screenWidth / 2 - 44, screenHeight / 2 + 28, 50, label); + + DrawText("cs".SubText(0, lettersCount), screenWidth / 2 - 44, screenHeight / 2 + 58, 50, label); + } + else if (state == 4) + { + DrawText("[R] REPLAY", 340, 200, 20, Color.Gray); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - logo raylib anim"); - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - raylib logo animation"); - - int logoPositionX = screenWidth / 2 - 128; - int logoPositionY = screenHeight / 2 - 128; - - int framesCounter = 0; - int lettersCount = 0; - - int topSideRecWidth = 16; - int leftSideRecHeight = 16; - - int bottomSideRecWidth = 16; - int rightSideRecHeight = 16; - - // Tracking animation states (State Machine) - int state = 0; - - // Useful for fading - float alpha = 1.0f; - - Color outline = new(139, 71, 135, 255); - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new LogoRaylibAnim(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // State 0: Small box blinking - if (state == 0) - { - framesCounter++; - - // Reset counter... will be used later... - if (framesCounter == 120) - { - state = 1; - framesCounter = 0; - } - } - // State 1: Top and left bars growing - else if (state == 1) - { - topSideRecWidth += 4; - leftSideRecHeight += 4; - - if (topSideRecWidth == 256) - { - state = 2; - } - } - // State 2: Bottom and right bars growing - else if (state == 2) - { - bottomSideRecWidth += 4; - rightSideRecHeight += 4; - - if (bottomSideRecWidth == 256) - { - state = 3; - } - } - // State 3: Letters appearing (one by one) - else if (state == 3) - { - framesCounter++; - - // Every 12 frames, one more letter! - if (framesCounter / 12 != 0) - { - lettersCount++; - framesCounter = 0; - } - - // When all letters have appeared, just fade out everything - if (lettersCount >= 10) - { - alpha -= 0.02f; - - if (alpha <= 0.0f) - { - alpha = 0.0f; - state = 4; - } - } - } - // State 4: Reset and Replay - else if (state == 4) - { - if (IsKeyPressed(KeyboardKey.R)) - { - framesCounter = 0; - lettersCount = 0; - - topSideRecWidth = 16; - leftSideRecHeight = 16; - - bottomSideRecWidth = 16; - rightSideRecHeight = 16; - - // Return to State 0 - alpha = 1.0f; - state = 0; - } - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - if (state == 0) - { - if ((framesCounter / 15) % 2 != 0) - { - DrawRectangle(logoPositionX, logoPositionY, 16, 16, outline); - } - } - else if (state == 1) - { - DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, outline); - DrawRectangle(logoPositionX, logoPositionY, 16, leftSideRecHeight, outline); - } - else if (state == 2) - { - DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, outline); - DrawRectangle(logoPositionX, logoPositionY, 16, leftSideRecHeight, outline); - - DrawRectangle(logoPositionX + 240, logoPositionY, 16, rightSideRecHeight, outline); - DrawRectangle(logoPositionX, logoPositionY + 240, bottomSideRecWidth, 16, outline); - } - else if (state == 3) - { - Color outlineFade = ColorAlpha(outline, alpha); - DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, outlineFade); - DrawRectangle(logoPositionX, logoPositionY + 16, 16, leftSideRecHeight - 32, outlineFade); - - DrawRectangle(logoPositionX + 240, logoPositionY + 16, 16, rightSideRecHeight - 32, outlineFade); - DrawRectangle(logoPositionX, logoPositionY + 240, bottomSideRecWidth, 16, outlineFade); - - Color whiteFade = ColorAlpha(Color.RayWhite, alpha); - DrawRectangle(screenWidth / 2 - 112, screenHeight / 2 - 112, 224, 224, whiteFade); - - Color label = ColorAlpha(new Color(155, 79, 151, 255), alpha); - string text = "raylib".SubText(0, lettersCount); - DrawText(text, screenWidth / 2 - 44, screenHeight / 2 + 28, 50, label); - - DrawText("cs".SubText(0, lettersCount), screenWidth / 2 - 44, screenHeight / 2 + 58, 50, label); - } - else if (state == 4) - { - DrawText("[R] REPLAY", 340, 200, 20, Color.Gray); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shapes/LogoRaylibShape.cs b/Examples/Shapes/LogoRaylibShape.cs index 341f330..660f849 100644 --- a/Examples/Shapes/LogoRaylibShape.cs +++ b/Examples/Shapes/LogoRaylibShape.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [shapes] example - Draw raylib logo using basic shapes +* raylib [shapes] example - logo raylib * -* This example has been created using raylib 1.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 * -* Copyright (c) 2014 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.0, last time updated with raylib 1.0 +* +* 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -13,47 +17,64 @@ using static Raylib_cs.Raylib; namespace Examples.Shapes; -public class LogoRaylibShape +public partial class LogoRaylibShape : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Shapes / Logo Raylib Shape"; + + public string Title => "raylib [shapes] example - logo raylib"; + + public void Init() + { + } + + public void Update() + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawRectangle(screenWidth / 2 - 128, screenHeight / 2 - 128, 256, 256, new Color(139, 71, 135, 255)); + DrawRectangle(screenWidth / 2 - 112, screenHeight / 2 - 112, 224, 224, Color.RayWhite); + DrawText("raylib", screenWidth / 2 - 44, screenHeight / 2 + 28, 50, new Color(155, 79, 151, 255)); + DrawText("cs", screenWidth / 2 - 44, screenHeight / 2 + 58, 50, new Color(155, 79, 151, 255)); + + DrawText("this is NOT a texture!", 350, 370, 10, Color.Gray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - logo raylib"); - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - raylib logo using shapes"); - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new LogoRaylibShape(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update your variables here - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawRectangle(screenWidth / 2 - 128, screenHeight / 2 - 128, 256, 256, new Color(139, 71, 135, 255)); - DrawRectangle(screenWidth / 2 - 112, screenHeight / 2 - 112, 224, 224, Color.RayWhite); - DrawText("raylib", screenWidth / 2 - 44, screenHeight / 2 + 28, 50, new Color(155, 79, 151, 255)); - DrawText("cs", screenWidth / 2 - 44, screenHeight / 2 + 58, 50, new Color(155, 79, 151, 255)); - - DrawText("this is NOT a texture!", 350, 370, 10, Color.Gray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shapes/RectangleScaling.cs b/Examples/Shapes/RectangleScaling.cs index 36c279b..5fbfc84 100644 --- a/Examples/Shapes/RectangleScaling.cs +++ b/Examples/Shapes/RectangleScaling.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [shapes] example - rectangle scaling by mouse +* raylib [shapes] example - rectangle scaling * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 2.5, last time updated with raylib 2.5 * * Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2018 Vlad Adrian (@demizdor) and 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) 2018-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -16,105 +20,145 @@ using static Raylib_cs.Raylib; namespace Examples.Shapes; -public class RectangleScaling +public partial class RectangleScaling : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + public const int MOUSE_SCALE_MARK_SIZE = 12; + public string Name => "Shapes / Rectangle Scaling"; + + public string Title => "raylib [shapes] example - rectangle scaling"; + + private Rectangle rec; + + private Vector2 mousePosition; + + private bool mouseScaleReady; + private bool mouseScaleMode; + + public void Init() + { + rec = new(100, 100, 200, 80); + + mousePosition = new(0, 0); + + mouseScaleReady = false; + mouseScaleMode = false; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + mousePosition = GetMousePosition(); + + Rectangle area = new( + rec.X + rec.Width - MOUSE_SCALE_MARK_SIZE, + rec.Y + rec.Height - MOUSE_SCALE_MARK_SIZE, + MOUSE_SCALE_MARK_SIZE, + MOUSE_SCALE_MARK_SIZE + ); + + if (CheckCollisionPointRec(mousePosition, area)) + { + mouseScaleReady = true; + if (IsMouseButtonPressed(MouseButton.Left)) + { + mouseScaleMode = true; + } + } + else + { + mouseScaleReady = false; + } + + if (mouseScaleMode) + { + mouseScaleReady = true; + + rec.Width = (mousePosition.X - rec.X); + rec.Height = (mousePosition.Y - rec.Y); + + // Check minimum rec size + if (rec.Width < MOUSE_SCALE_MARK_SIZE) + { + rec.Width = MOUSE_SCALE_MARK_SIZE; + } + if (rec.Height < MOUSE_SCALE_MARK_SIZE) + { + rec.Height = MOUSE_SCALE_MARK_SIZE; + } + + // Check maximum rec size + if (rec.Width > (GetScreenWidth() - rec.X)) + { + rec.Width = GetScreenWidth() - rec.X; + } + if (rec.Height > (GetScreenHeight() - rec.Y)) + { + rec.Height = GetScreenHeight() - rec.Y; + } + + if (IsMouseButtonReleased(MouseButton.Left)) + { + mouseScaleMode = false; + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("Scale rectangle dragging from bottom-right corner!", 10, 10, 20, Color.Gray); + + DrawRectangleRec(rec, Fade(Color.Green, 0.5f)); + + if (mouseScaleReady) + { + DrawRectangleLinesEx(rec, 1, Color.Red); + DrawTriangle( + new Vector2(rec.X + rec.Width - MOUSE_SCALE_MARK_SIZE, rec.Y + rec.Height), + new Vector2(rec.X + rec.Width, rec.Y + rec.Height), + new Vector2(rec.X + rec.Width, rec.Y + rec.Height - MOUSE_SCALE_MARK_SIZE), + Color.Red + ); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rectangle scaling"); - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rectangle scaling mouse"); - - Rectangle rec = new(100, 100, 200, 80); - Vector2 mousePosition = new(0, 0); - - bool mouseScaleReady = false; - bool mouseScaleMode = false; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new RectangleScaling(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - mousePosition = GetMousePosition(); - - Rectangle area = new( - rec.X + rec.Width - MOUSE_SCALE_MARK_SIZE, - rec.Y + rec.Height - MOUSE_SCALE_MARK_SIZE, - MOUSE_SCALE_MARK_SIZE, - MOUSE_SCALE_MARK_SIZE - ); - - if (CheckCollisionPointRec(mousePosition, rec) && - CheckCollisionPointRec(mousePosition, area)) - { - mouseScaleReady = true; - if (IsMouseButtonPressed(MouseButton.Left)) - { - mouseScaleMode = true; - } - } - else - { - mouseScaleReady = false; - } - - if (mouseScaleMode) - { - mouseScaleReady = true; - - rec.Width = (mousePosition.X - rec.X); - rec.Height = (mousePosition.Y - rec.Y); - - if (rec.Width < MOUSE_SCALE_MARK_SIZE) - { - rec.Width = MOUSE_SCALE_MARK_SIZE; - } - if (rec.Height < MOUSE_SCALE_MARK_SIZE) - { - rec.Height = MOUSE_SCALE_MARK_SIZE; - } - - if (IsMouseButtonReleased(MouseButton.Left)) - { - mouseScaleMode = false; - } - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("Scale rectangle dragging from bottom-right corner!", 10, 10, 20, Color.Gray); - DrawRectangleRec(rec, ColorAlpha(Color.Green, 0.5f)); - - if (mouseScaleReady) - { - DrawRectangleLinesEx(rec, 1, Color.Red); - DrawTriangle( - new Vector2(rec.X + rec.Width - MOUSE_SCALE_MARK_SIZE, rec.Y + rec.Height), - new Vector2(rec.X + rec.Width, rec.Y + rec.Height), - new Vector2(rec.X + rec.Width, rec.Y + rec.Height - MOUSE_SCALE_MARK_SIZE), - Color.Red - ); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Shared/Easings.cs b/Examples/Shared/Easings.cs index 57cc879..4652f41 100644 --- a/Examples/Shared/Easings.cs +++ b/Examples/Shared/Easings.cs @@ -132,26 +132,26 @@ public static class Easings // Back Easing functions public static float EaseBackIn(float t, float b, float c, float d) { - float s = 1.70158f; - float postFix = t /= d; + var s = 1.70158f; + var postFix = t /= d; return (c * (postFix) * t * ((s + 1) * t - s) + b); } public static float EaseBackOut(float t, float b, float c, float d) { - float s = 1.70158f; + var s = 1.70158f; return (c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b); } public static float EaseBackInOut(float t, float b, float c, float d) { - float s = 1.70158f; + var s = 1.70158f; if ((t /= d / 2) < 1) { return (c / 2 * (t * t * (((s *= (1.525f)) + 1) * t - s)) + b); } - float postFix = t -= 2; + var postFix = t -= 2; return (c / 2 * ((postFix) * t * (((s *= (1.525f)) + 1) * t + s) + 2) + b); } @@ -164,17 +164,17 @@ public static class Easings } else if (t < (2 / 2.75f)) { - float postFix = t -= (1.5f / 2.75f); + var postFix = t -= (1.5f / 2.75f); return (c * (7.5625f * (postFix) * t + 0.75f) + b); } else if (t < (2.5 / 2.75)) { - float postFix = t -= (2.25f / 2.75f); + var postFix = t -= (2.25f / 2.75f); return (c * (7.5625f * (postFix) * t + 0.9375f) + b); } else { - float postFix = t -= (2.625f / 2.75f); + var postFix = t -= (2.625f / 2.75f); return (c * (7.5625f * (postFix) * t + 0.984375f) + b); } } @@ -208,10 +208,10 @@ public static class Easings return (b + c); } - float p = d * 0.3f; - float a = c; - float s = p / 4; - float postFix = a * MathF.Pow(2, 10 * (t -= 1)); + var p = d * 0.3f; + var a = c; + var s = p / 4; + var postFix = a * MathF.Pow(2, 10 * (t -= 1)); return (-(postFix * MathF.Sin((t * d - s) * (2 * MathF.PI) / p)) + b); } @@ -227,9 +227,9 @@ public static class Easings return (b + c); } - float p = d * 0.3f; - float a = c; - float s = p / 4; + var p = d * 0.3f; + var a = c; + var s = p / 4; return (a * MathF.Pow(2, -10 * t) * MathF.Sin((t * d - s) * (2 * MathF.PI) / p) + c + b); } @@ -245,11 +245,11 @@ public static class Easings return (b + c); } - float p = d * (0.3f * 1.5f); - float a = c; - float s = p / 4; + var p = d * (0.3f * 1.5f); + var a = c; + var s = p / 4; - float postFix = 0f; + var postFix = 0f; if (t < 1) { postFix = a * MathF.Pow(2, 10 * (t -= 1)); diff --git a/Examples/Shared/PbrLights.cs b/Examples/Shared/PbrLights.cs index 90d1966..1b64576 100644 --- a/Examples/Shared/PbrLights.cs +++ b/Examples/Shared/PbrLights.cs @@ -54,12 +54,12 @@ public class PbrLights ); light.Intensity = intensity; - string enabledName = "lights[" + lightsCount + "].enabled"; - string typeName = "lights[" + lightsCount + "].type"; - string posName = "lights[" + lightsCount + "].position"; - string targetName = "lights[" + lightsCount + "].target"; - string colorName = "lights[" + lightsCount + "].color"; - string intensityName = "lights[" + lightsCount + "].intensity"; + var enabledName = "lights[" + lightsCount + "].enabled"; + var typeName = "lights[" + lightsCount + "].type"; + var posName = "lights[" + lightsCount + "].position"; + var targetName = "lights[" + lightsCount + "].target"; + var colorName = "lights[" + lightsCount + "].color"; + var intensityName = "lights[" + lightsCount + "].intensity"; light.EnabledLoc = GetShaderLocation(shader, enabledName); light.TypeLoc = GetShaderLocation(shader, typeName); diff --git a/Examples/Shared/Rlights.cs b/Examples/Shared/Rlights.cs index 729b25f..21ba825 100644 --- a/Examples/Shared/Rlights.cs +++ b/Examples/Shared/Rlights.cs @@ -43,11 +43,11 @@ public static class Rlights light.Target = target; light.Color = color; - string enabledName = "lights[" + lightsCount + "].enabled"; - string typeName = "lights[" + lightsCount + "].type"; - string posName = "lights[" + lightsCount + "].position"; - string targetName = "lights[" + lightsCount + "].target"; - string colorName = "lights[" + lightsCount + "].color"; + var enabledName = "lights[" + lightsCount + "].enabled"; + var typeName = "lights[" + lightsCount + "].type"; + var posName = "lights[" + lightsCount + "].position"; + var targetName = "lights[" + lightsCount + "].target"; + var colorName = "lights[" + lightsCount + "].color"; light.EnabledLoc = GetShaderLocation(shader, enabledName); light.TypeLoc = GetShaderLocation(shader, typeName); @@ -78,7 +78,7 @@ public static class Rlights Raylib.SetShaderValue(shader, light.TargetLoc, light.Target, ShaderUniformDataType.Vec3); // Send to shader light color values - float[] color = new[] + var color = new[] { (float)light.Color.R / (float)255, (float)light.Color.G / (float)255, diff --git a/Examples/Text/CodepointsLoading.cs b/Examples/Text/CodepointsLoading.cs index cc41443..1846dd1 100644 --- a/Examples/Text/CodepointsLoading.cs +++ b/Examples/Text/CodepointsLoading.cs @@ -1,13 +1,15 @@ /******************************************************************************************* * -* raylib [text] example - Codepoints loading +* raylib [text] example - codepoints loading * -* Example originally created with raylib 4.2, last time updated with raylib 2.5 +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 4.2, last time updated with raylib 4.2 * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * -* Copyright (c) 2022-2023 Ramon Santamaria (@raysan5) +* Copyright (c) 2022-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -19,32 +21,37 @@ using static Raylib_cs.Raylib; namespace Examples.Text; -class CodepointsLoading +public partial class CodepointsLoading : IExample { - public unsafe static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + // Text to be displayed, must be UTF-8 (save this code file as UTF-8) + // NOTE: It can contain all the required text for the game, + // this text will be scanned to get all the required codepoints + private const string text = + "いろはにほへと ちりぬるを\nわかよたれそ つねならむ\nうゐのおくやま けふこえて\nあさきゆめみし ゑひもせす"; + + public string Name => "Text / Codepoints Loading"; + + public string Title => "raylib [text] example - codepoints loading"; + + private List codepoints; + private int[] codepointsNoDuplicates; + private Font font; + private bool showFontAtlas; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [text] example - codepoints loading"); - - // Text to be displayed, must be UTF-8 (save this code file as UTF-8) - // NOTE: It can contain all the required text for the game, - // this text will be scanned to get all the required codepoints - const string text = - "いろはにほへと ちりぬるを\nわかよたれそ つねならむ\nうゐのおくやま けふこえて\nあさきゆめみし ゑひもせす"; - // Get codepoints from text - List codepoints = GetCodePoints(text); + codepoints = GetCodePoints(text); // Remove duplicate codepoints to generate smaller font atlas - int[] codepointsNoDuplicates = codepoints.Distinct().ToArray(); + codepointsNoDuplicates = codepoints.Distinct().ToArray(); // Load font containing all the provided codepoint glyphs // A texture font atlas is automatically generated - Font font = LoadFontEx( + font = LoadFontEx( "resources/fonts/DotGothic16-Regular.ttf", 36, codepointsNoDuplicates, @@ -54,64 +61,58 @@ class CodepointsLoading // Set bilinear scale filter for better font scaling SetTextureFilter(font.Texture, TextureFilter.Bilinear); - bool showFontAtlas = false; + SetTextLineSpacing(20); // Set line spacing for multiline text (when line breaks are included '\n') - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- + showFontAtlas = false; + } - // Main game loop - while (!WindowShouldClose()) + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KeyboardKey.Space)) { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyPressed(KeyboardKey.Space)) - { - showFontAtlas = !showFontAtlas; - } - //---------------------------------------------------------------------------------- + showFontAtlas = !showFontAtlas; + } + //---------------------------------------------------------------------------------- - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); - ClearBackground(Color.RayWhite); + ClearBackground(Color.RayWhite); - DrawRectangle(0, 0, GetScreenWidth(), 70, Color.Black); - DrawText($"Total codepoints contained in provided text: {codepoints.Count}", 10, 10, 20, Color.Green); - DrawText( - $"Total codepoints required for font atlas (duplicates excluded): {codepointsNoDuplicates.Length}", - 10, - 40, - 20, - Color.Green - ); + DrawRectangle(0, 0, GetScreenWidth(), 70, Color.Black); + DrawText($"Total codepoints contained in provided text: {codepoints.Count}", 10, 10, 20, Color.Green); + DrawText( + $"Total codepoints required for font atlas (duplicates excluded): {codepointsNoDuplicates.Length}", + 10, + 40, + 20, + Color.Green + ); - if (showFontAtlas) - { - // Draw generated font texture atlas containing provided codepoints - DrawTexture(font.Texture, 150, 100, Color.Black); - DrawRectangleLines(150, 100, font.Texture.Width, font.Texture.Height, Color.Black); - } - else - { - // Draw provided text with laoded font, containing all required codepoint glyphs - DrawTextEx(font, text, new Vector2(160, 110), 48, 5, Color.Black); - } - - DrawText("Press SPACE to toggle font atlas view!", 10, GetScreenHeight() - 30, 20, Color.Gray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + if (showFontAtlas) + { + // Draw generated font texture atlas containing provided codepoints + DrawTexture(font.Texture, 150, 100, Color.Black); + DrawRectangleLines(150, 100, font.Texture.Width, font.Texture.Height, Color.Black); + } + else + { + // Draw provided text with loaded font, containing all required codepoint glyphs + DrawTextEx(font, text, new Vector2(160, 110), 48, 5, Color.Black); } - // De-Initialization - //-------------------------------------------------------------------------------------- - UnloadFont(font); + DrawText("Press SPACE to toggle font atlas view!", 10, GetScreenHeight() - 30, 20, Color.Gray); - CloseWindow(); - //-------------------------------------------------------------------------------------- + EndDrawing(); + //---------------------------------------------------------------------------------- + } - return 0; + public void Unload() + { + UnloadFont(font); // Unload font } private static List GetCodePoints(string text) @@ -119,14 +120,42 @@ class CodepointsLoading List codePoints = new(); StringInfo stringInfo = new(text); - TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(text); + var enumerator = StringInfo.GetTextElementEnumerator(text); while (enumerator.MoveNext()) { - int codePoint = char.ConvertToUtf32(enumerator.Current.ToString(), 0); + var codePoint = char.ConvertToUtf32(enumerator.Current.ToString(), 0); codePoints.Add(codePoint); } return codePoints; } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [text] example - codepoints loading"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + var game = new CodepointsLoading(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + game.Update(); + } + + game.Unload(); + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; + } } diff --git a/Examples/Text/FontFilters.cs b/Examples/Text/FontFilters.cs index 63cd9c3..e7e5260 100644 --- a/Examples/Text/FontFilters.cs +++ b/Examples/Text/FontFilters.cs @@ -1,15 +1,19 @@ /******************************************************************************************* * -* raylib [text] example - Font filters +* raylib [text] example - font filters * -* After font loading, font texture atlas filter could be configured for a softer +* Example complexity rating: [★★☆☆] 2/4 +* +* NOTE: After font loading, font texture atlas filter could be configured for a softer * display of the font when scaling it to different sizes, that way, it's not required * to generate multiple fonts at multiple sizes (as long as the scaling is not very different) * -* This example has been created using raylib 1.3.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.3, last time updated with raylib 4.2 * -* Copyright (c) 2015 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -18,129 +22,164 @@ using static Raylib_cs.Raylib; namespace Examples.Text; -public class FontFilters +public partial class FontFilters : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Text / Font Filters"; + + public string Title => "raylib [text] example - font filters"; + + private string msg; + private Font font; + private float fontSize; + private Vector2 fontPosition; + private Vector2 textSize; + private TextureFilter currentFontFilter; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [text] example - font filters"); - - string msg = "Loaded Font"; + msg = "Loaded Font"; // NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required) // TTF Font loading with custom generation parameters - Font font = LoadFontEx("resources/fonts/KAISG.ttf", 96, null, 0); + font = LoadFontEx("resources/fonts/KAISG.ttf", 96, null, 0); // Generate mipmap levels to use trilinear filtering - // NOTE: On 2D drawing it won't be noticeable, it looks like TEXTURE_FILTER_BILINEAR + // NOTE: On 2D drawing it won't be noticeable, it looks like FILTER_BILINEAR GenTextureMipmaps(ref font.Texture); - float fontSize = font.BaseSize; - Vector2 fontPosition = new(40, screenHeight / 2 - 80); - Vector2 textSize = new(0.0f, 0.0f); + fontSize = font.BaseSize; + fontPosition = new(40, screenHeight / 2 - 80); + textSize = new(0.0f, 0.0f); // Setup texture scaling filter SetTextureFilter(font.Texture, TextureFilter.Point); - TextureFilter currentFontFilter = TextureFilter.Point; + currentFontFilter = TextureFilter.Point; // TEXTURE_FILTER_POINT + } - SetTargetFPS(60); + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + fontSize += GetMouseWheelMove() * 4.0f; + + // Choose font texture filter method + if (IsKeyPressed(KeyboardKey.One)) + { + SetTextureFilter(font.Texture, TextureFilter.Point); + currentFontFilter = TextureFilter.Point; + } + else if (IsKeyPressed(KeyboardKey.Two)) + { + SetTextureFilter(font.Texture, TextureFilter.Bilinear); + currentFontFilter = TextureFilter.Bilinear; + } + else if (IsKeyPressed(KeyboardKey.Three)) + { + // NOTE: Trilinear filter won't be noticed on 2D drawing + SetTextureFilter(font.Texture, TextureFilter.Trilinear); + currentFontFilter = TextureFilter.Trilinear; + } + + textSize = MeasureTextEx(font, msg, fontSize, 0); + + if (IsKeyDown(KeyboardKey.Left)) + { + fontPosition.X -= 10; + } + else if (IsKeyDown(KeyboardKey.Right)) + { + fontPosition.X += 10; + } + +#if BROWSER + // NOTE: drag-and-drop font loading is not supported in the browser host; default loaded font is kept. +#else + // Load a dropped TTF file dynamically (at current fontSize) + if (IsFileDropped()) + { + var files = Raylib.GetDroppedFiles(); + + // NOTE: We only support first ttf file dropped + if (IsFileExtension(files[0], ".ttf")) + { + UnloadFont(font); + font = LoadFontEx(files[0], (int)fontSize, null, 0); + } + } +#endif + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("Use mouse wheel to change font size", 20, 20, 10, Color.Gray); + DrawText("Use KEY_RIGHT and KEY_LEFT to move text", 20, 40, 10, Color.Gray); + DrawText("Use 1, 2, 3 to change texture filter", 20, 60, 10, Color.Gray); + DrawText("Drop a new TTF font for dynamic loading", 20, 80, 10, Color.DarkGray); + + DrawTextEx(font, msg, fontPosition, fontSize, 0, Color.Black); + + // TODO: It seems texSize measurement is not accurate due to chars offsets... + //DrawRectangleLines((int)fontPosition.X, (int)fontPosition.Y, (int)textSize.X, (int)textSize.Y, Color.Red); + + DrawRectangle(0, screenHeight - 80, screenWidth, 80, Color.LightGray); + DrawText($"Font size: {fontSize:00.00}", 20, screenHeight - 50, 10, Color.DarkGray); + DrawText($"Text size: [{textSize.X:00.00}, {textSize.Y:00.00}]", 20, screenHeight - 30, 10, Color.DarkGray); + DrawText("CURRENT TEXTURE FILTER:", 250, 400, 20, Color.Gray); + + if (currentFontFilter == TextureFilter.Point) + { + DrawText("POINT", 570, 400, 20, Color.Black); + } + else if (currentFontFilter == TextureFilter.Bilinear) + { + DrawText("BILINEAR", 570, 400, 20, Color.Black); + } + else if (currentFontFilter == TextureFilter.Trilinear) + { + DrawText("TRILINEAR", 570, 400, 20, Color.Black); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadFont(font); // Font unloading + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [text] example - font filters"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new FontFilters(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - fontSize += GetMouseWheelMove() * 4.0f; - - // Choose font texture filter method - if (IsKeyPressed(KeyboardKey.One)) - { - SetTextureFilter(font.Texture, TextureFilter.Point); - currentFontFilter = TextureFilter.Point; - } - else if (IsKeyPressed(KeyboardKey.Two)) - { - SetTextureFilter(font.Texture, TextureFilter.Bilinear); - currentFontFilter = TextureFilter.Bilinear; - } - else if (IsKeyPressed(KeyboardKey.Three)) - { - // NOTE: Trilinear filter won't be noticed on 2D drawing - SetTextureFilter(font.Texture, TextureFilter.Trilinear); - currentFontFilter = TextureFilter.Trilinear; - } - - textSize = MeasureTextEx(font, msg, fontSize, 0); - - if (IsKeyDown(KeyboardKey.Left)) - { - fontPosition.X -= 10; - } - else if (IsKeyDown(KeyboardKey.Right)) - { - fontPosition.X += 10; - } - - // Load a dropped TTF file dynamically (at current fontSize) - if (IsFileDropped()) - { - string[] files = Raylib.GetDroppedFiles(); - - // NOTE: We only support first ttf file dropped - if (IsFileExtension(files[0], ".ttf")) - { - UnloadFont(font); - font = LoadFontEx(files[0], (int)fontSize, null, 0); - } - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("Use mouse wheel to change font size", 20, 20, 10, Color.Gray); - DrawText("Use KEY_RIGHT and KEY_LEFT to move text", 20, 40, 10, Color.Gray); - DrawText("Use 1, 2, 3 to change texture filter", 20, 60, 10, Color.Gray); - DrawText("Drop a new TTF font for dynamic loading", 20, 80, 10, Color.DarkGray); - - DrawTextEx(font, msg, fontPosition, fontSize, 0, Color.Black); - - DrawRectangle(0, screenHeight - 80, screenWidth, 80, Color.LightGray); - DrawText("CURRENT TEXTURE FILTER:", 250, 400, 20, Color.Gray); - - if (currentFontFilter == TextureFilter.Point) - { - DrawText("POINT", 570, 400, 20, Color.Black); - } - else if (currentFontFilter == TextureFilter.Point) - { - DrawText("BILINEAR", 570, 400, 20, Color.Black); - } - else if (currentFontFilter == TextureFilter.Trilinear) - { - DrawText("TRILINEAR", 570, 400, 20, Color.Black); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadFont(font); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } } - diff --git a/Examples/Text/FontLoading.cs b/Examples/Text/FontLoading.cs index b7c37c5..15d842a 100644 --- a/Examples/Text/FontLoading.cs +++ b/Examples/Text/FontLoading.cs @@ -1,20 +1,24 @@ /******************************************************************************************* * -* raylib [text] example - Font loading +* raylib [text] example - font loading * -* raylib can load fonts from multiple file formats: +* Example complexity rating: [★☆☆☆] 1/4 +* +* NOTE: raylib can load fonts from multiple input file formats: * * - TTF/OTF > Sprite font atlas is generated on loading, user can configure * some of the generation parameters (size, characters to include) * - BMFonts > Angel code font fileformat, sprite font image must be provided -* together with the .fnt file, font generation cna not be configured +* together with the .fnt file, font generation can not be configured * - XNA Spritefont > Sprite font image, following XNA Spritefont conventions, * Characters in image must follow some spacing and order rules * -* This example has been created using raylib 2.6 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.4, last time updated with raylib 3.0 * -* Copyright (c) 2016-2019 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) 2016-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -23,78 +27,105 @@ using static Raylib_cs.Raylib; namespace Examples.Text; -public class FontLoading +public partial class FontLoading : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Text / Font Loading"; + + public string Title => "raylib [text] example - font loading"; + + private string msg; + private Font fontBm; + private Font fontTtf; + private bool useTtf; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [text] example - font loading"); - // Define characters to draw // NOTE: raylib supports UTF-8 encoding, following list is actually codified as UTF8 internally - string msg = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHI\nJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmn\nopqrstuvwxyz{|}~¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓ\nÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷\nøùúûüýþÿ"; + msg = "!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHI\nJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmn\nopqrstuvwxyz{|}~¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓ\nÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷\nøùúûüýþÿ"; // NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required) // BMFont (AngelCode) : Font data and image atlas have been generated using external program - Font fontBm = LoadFont("resources/fonts/pixantiqua.fnt"); + fontBm = LoadFont("resources/fonts/pixantiqua.fnt"); // Requires "resources/fonts/pixantiqua.png" // TTF font : Font data and atlas are generated directly from TTF // NOTE: We define a font base size of 32 pixels tall and up-to 250 characters - Font fontTtf = LoadFontEx("resources/fonts/pixantiqua.ttf", 32, null, 250); + fontTtf = LoadFontEx("resources/fonts/pixantiqua.ttf", 32, null, 250); - bool useTtf = false; + SetTextLineSpacing(16); // Set line spacing for multiline text (when line breaks are included '\n') - SetTargetFPS(60); + useTtf = false; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyDown(KeyboardKey.Space)) + { + useTtf = true; + } + else + { + useTtf = false; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("Hold SPACE to use TTF generated font", 20, 20, 20, Color.LightGray); + + if (!useTtf) + { + DrawTextEx(fontBm, msg, new Vector2(20.0f, 100.0f), fontBm.BaseSize, 2, Color.Maroon); + DrawText("Using BMFont (Angelcode) imported", 20, GetScreenHeight() - 30, 20, Color.Gray); + } + else + { + DrawTextEx(fontTtf, msg, new Vector2(20.0f, 100.0f), fontTtf.BaseSize, 2, Color.Lime); + DrawText("Using TTF font generated", 20, GetScreenHeight() - 30, 20, Color.Gray); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadFont(fontBm); // AngelCode Font unloading + UnloadFont(fontTtf); // TTF Font unloading + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [text] example - font loading"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new FontLoading(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyDown(KeyboardKey.Space)) - { - useTtf = true; - } - else - { - useTtf = false; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("Hold SPACE to use TTF generated font", 20, 20, 20, Color.LightGray); - - if (!useTtf) - { - DrawTextEx(fontBm, msg, new Vector2(20.0f, 100.0f), fontBm.BaseSize, 2, Color.Maroon); - DrawText("Using BMFont (Angelcode) imported", 20, GetScreenHeight() - 30, 20, Color.Gray); - } - else - { - DrawTextEx(fontTtf, msg, new Vector2(20.0f, 100.0f), fontTtf.BaseSize, 2, Color.Lime); - DrawText("Using TTF font generated", 20, GetScreenHeight() - 30, 20, Color.Gray); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadFont(fontBm); - UnloadFont(fontTtf); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Text/FontSdf.cs b/Examples/Text/FontSdf.cs index 3b01a9c..c323ccf 100644 --- a/Examples/Text/FontSdf.cs +++ b/Examples/Text/FontSdf.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [text] example - TTF loading and usage +* raylib [text] example - font sdf * -* This example has been created using raylib 1.3.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 * -* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.3, last time updated with raylib 4.0 +* +* 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -16,23 +20,44 @@ using static Raylib_cs.Raylib; namespace Examples.Text; -public class FontSdf +public partial class FontSdf : IExample { - public unsafe static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + +#if BROWSER + const int GlslVersion = 100; // WebGL1 needs GLSL ES 100 +#else + private const int GlslVersion = 330; +#endif + + public string Name => "Text / Font SDF"; + + public string Title => "raylib [text] example - font sdf"; + + private string msg; + + private Font fontDefault; + private Font fontSDF; + private Shader shader; + + private Vector2 fontPosition; + private Vector2 textSize; + private float fontSize; + private int currentFont; + + public unsafe void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [text] example - SDF fonts"); - // NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required) - string msg = "Signed Distance Fields"; + + msg = "Signed Distance Fields"; // Loading file to memory - int fileSize = 0; - byte* fileData = LoadFileData("resources/fonts/anonymous_pro_bold.ttf", ref fileSize); + var fileSize = 0; + var fileData = LoadFileData("resources/fonts/anonymous_pro_bold.ttf", ref fileSize); + + // Build the fonts in locals first: taking the address of a struct's field (&font.GlyphCount, + // &font.Recs) is only allowed for a stack local, not a heap field. Assign to the fields after. // Default font generation from TTF font Font fontDefault = new(); @@ -40,121 +65,141 @@ public class FontSdf fontDefault.GlyphCount = 95; // Loading font data from memory data - // Parameters > font size: 16, no chars array provided (0), chars count: 95 (autogenerate chars array) + // Parameters > font size: 16, no glyphs array provided (0), glyphs count: 95 (autogenerate chars array) fontDefault.Glyphs = LoadFontData(fileData, (int)fileSize, 16, null, 95, FontType.Default, &fontDefault.GlyphCount); - // Parameters > chars count: 95, font size: 16, chars padding in image: 4 px, pack method: 0 (default) - Image atlas = GenImageFontAtlas(fontDefault.Glyphs, &fontDefault.Recs, 95, 16, 4, 0); + // Parameters > glyphs count: 95, font size: 16, glyphs padding in image: 4 px, pack method: 0 (default) + var atlas = GenImageFontAtlas(fontDefault.Glyphs, &fontDefault.Recs, 95, 16, 4, 0); fontDefault.Texture = LoadTextureFromImage(atlas); UnloadImage(atlas); + this.fontDefault = fontDefault; // SDF font generation from TTF font Font fontSDF = new(); fontSDF.BaseSize = 16; fontSDF.GlyphCount = 95; - // Parameters > font size: 16, no chars array provided (0), chars count: 0 (defaults to 95) - fontSDF.Glyphs = LoadFontData(fileData, (int)fileSize, 16, null, 0, FontType.Sdf, &fontDefault.GlyphCount); - // Parameters > chars count: 95, font size: 16, chars padding in image: 0 px, pack method: 1 (Skyline algorythm) + // Parameters > font size: 16, no glyphs array provided (0), glyphs count: 0 (defaults to 95) + fontSDF.Glyphs = LoadFontData(fileData, (int)fileSize, 16, null, 0, FontType.Sdf, &fontSDF.GlyphCount); + // Parameters > glyphs count: 95, font size: 16, glyphs padding in image: 0 px, pack method: 1 (Skyline algorythm) atlas = GenImageFontAtlas(fontSDF.Glyphs, &fontSDF.Recs, 95, 16, 0, 1); fontSDF.Texture = LoadTextureFromImage(atlas); UnloadImage(atlas); + this.fontSDF = fontSDF; - // Free memory from loaded file - UnloadFileData(fileData); + UnloadFileData(fileData); // Free memory from loaded file // Load SDF required shader (we use default vertex shader) - Shader shader = LoadShader(null, "resources/shaders/glsl330/sdf.fs"); - // Required for SDF font - SetTextureFilter(fontSDF.Texture, TextureFilter.Bilinear); + shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/sdf.fs"); + SetTextureFilter(fontSDF.Texture, TextureFilter.Bilinear); // Required for SDF font - Vector2 fontPosition = new(40, screenHeight / 2 - 50); - Vector2 textSize = new(0.0f); - float fontSize = 16.0f; - // 0 - fontDefault, 1 - fontSDF - int currentFont = 0; + fontPosition = new(40, screenHeight / 2.0f - 50); + textSize = new(0.0f); + fontSize = 16.0f; + currentFont = 0; // 0 - fontDefault, 1 - fontSDF + } - SetTargetFPS(60); + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + fontSize += GetMouseWheelMove() * 8.0f; + + if (fontSize < 6) + { + fontSize = 6; + } + + if (IsKeyDown(KeyboardKey.Space)) + { + currentFont = 1; + } + else + { + currentFont = 0; + } + + if (currentFont == 0) + { + textSize = MeasureTextEx(fontDefault, msg, fontSize, 0); + } + else + { + textSize = MeasureTextEx(fontSDF, msg, fontSize, 0); + } + + fontPosition.X = GetScreenWidth() / 2 - textSize.X / 2; + fontPosition.Y = GetScreenHeight() / 2 - textSize.Y / 2 + 80; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + if (currentFont == 1) + { + // NOTE: SDF fonts require a custom SDf shader to compute fragment color + BeginShaderMode(shader); // Activate SDF font shader + DrawTextEx(fontSDF, msg, fontPosition, fontSize, 0, Color.Black); + EndShaderMode(); // Activate our default shader for next drawings + + DrawTexture(fontSDF.Texture, 10, 10, Color.Black); + } + else + { + DrawTextEx(fontDefault, msg, fontPosition, fontSize, 0, Color.Black); + DrawTexture(fontDefault.Texture, 10, 10, Color.Black); + } + + if (currentFont == 1) + { + DrawText("SDF!", 320, 20, 80, Color.Red); + } + else + { + DrawText("default font", 315, 40, 30, Color.Gray); + } + + DrawText("FONT SIZE: 16.0", GetScreenWidth() - 240, 20, 20, Color.DarkGray); + DrawText($"RENDER SIZE: {fontSize:00.00}", GetScreenWidth() - 240, 50, 20, Color.DarkGray); + DrawText("Use MOUSE WHEEL to SCALE TEXT!", GetScreenWidth() - 240, 90, 10, Color.DarkGray); + + DrawText("HOLD SPACE to USE SDF FONT VERSION!", 340, GetScreenHeight() - 30, 20, Color.Maroon); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadFont(fontDefault); // Default font unloading + UnloadFont(fontSDF); // SDF font unloading + + UnloadShader(shader); // Unload SDF shader + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [text] example - font sdf"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new FontSdf(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - fontSize += GetMouseWheelMove() * 8.0f; - - if (fontSize < 6) - { - fontSize = 6; - } - - if (IsKeyDown(KeyboardKey.Space)) - { - currentFont = 1; - } - else - { - currentFont = 0; - } - - if (currentFont == 0) - { - textSize = MeasureTextEx(fontDefault, msg, fontSize, 0); - } - else - { - textSize = MeasureTextEx(fontSDF, msg, fontSize, 0); - } - - fontPosition.X = GetScreenWidth() / 2 - textSize.X / 2; - fontPosition.Y = GetScreenHeight() / 2 - textSize.Y / 2 + 80; - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - if (currentFont == 1) - { - // NOTE: SDF fonts require a custom SDf shader to compute fragment color - BeginShaderMode(shader); - DrawTextEx(fontSDF, msg, fontPosition, fontSize, 0, Color.Black); - EndShaderMode(); - - DrawTexture(fontSDF.Texture, 10, 10, Color.Black); - } - else - { - DrawTextEx(fontDefault, msg, fontPosition, fontSize, 0, Color.Black); - DrawTexture(fontDefault.Texture, 10, 10, Color.Black); - } - - if (currentFont == 1) - { - DrawText("SDF!", 320, 20, 80, Color.Red); - } - else - { - DrawText("default font", 315, 40, 30, Color.Gray); - } - - DrawText("FONT SIZE: 16.0", GetScreenWidth() - 240, 20, 20, Color.DarkGray); - DrawText($"RENDER SIZE: {fontSize:2F}", GetScreenWidth() - 240, 50, 20, Color.DarkGray); - DrawText("Use MOUSE WHEEL to SCALE TEXT!", GetScreenWidth() - 240, 90, 10, Color.DarkGray); - - DrawText("PRESS SPACE to USE SDF FONT VERSION!", 340, GetScreenHeight() - 30, 20, Color.Maroon); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadFont(fontDefault); - UnloadFont(fontSDF); - UnloadShader(shader); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Text/FontSpritefont.cs b/Examples/Text/FontSpritefont.cs index eb802a1..91c30d6 100644 --- a/Examples/Text/FontSpritefont.cs +++ b/Examples/Text/FontSpritefont.cs @@ -1,20 +1,25 @@ /******************************************************************************************* * -* raylib [text] example - Sprite font loading +* raylib [text] example - font spritefont +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* NOTE: Sprite fonts should be generated following this conventions: * -* Loaded sprite fonts have been generated following XNA SpriteFont conventions: * - Characters must be ordered starting with character 32 (Space) * - Every character must be contained within the same Rectangle height -* - Every character and every line must be separated the same distance -* - Rectangles must be defined by a magenta color background +* - Every character and every line must be separated by the same distance (margin/padding) +* - Rectangles must be defined by a MAGENTA color background * -* If following this constraints, a font can be provided just by an image, -* this is quite handy to avoid additional information files (like BMFonts use). +* Following those constraints, a font can be provided just by an image, +* this is quite handy to avoid additional font descriptor files (like BMFonts use) * -* This example has been created using raylib 1.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.0, last time updated with raylib 1.0 * -* Copyright (c) 2014 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -23,72 +28,104 @@ using static Raylib_cs.Raylib; namespace Examples.Text; -public class FontSpritefont +public partial class FontSpritefont : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Text / Font Spritefont"; + + public string Title => "raylib [text] example - font spritefont"; + + private string msg1; + private string msg2; + private string msg3; + + private Font font1; + private Font font2; + private Font font3; + + private Vector2 fontPosition1; + private Vector2 fontPosition2; + private Vector2 fontPosition3; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [text] example - sprite font loading"); - - string msg1 = "THIS IS A custom SPRITE FONT..."; - string msg2 = "...and this is ANOTHER CUSTOM font..."; - string msg3 = "...and a THIRD one! GREAT! :D"; + msg1 = "THIS IS A custom SPRITE FONT..."; + msg2 = "...and this is ANOTHER CUSTOM font..."; + msg3 = "...and a THIRD one! GREAT! :D"; // NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required) - Font font1 = LoadFont("resources/fonts/custom_mecha.png"); - Font font2 = LoadFont("resources/fonts/custom_alagard.png"); - Font font3 = LoadFont("resources/fonts/custom_jupiter_crash.png"); + font1 = LoadFont("resources/custom_mecha.png"); // Font loading + font2 = LoadFont("resources/custom_alagard.png"); // Font loading + font3 = LoadFont("resources/custom_jupiter_crash.png"); // Font loading - Vector2 fontPosition1 = new( + fontPosition1 = new( screenWidth / 2 - MeasureTextEx(font1, msg1, font1.BaseSize, -3).X / 2, screenHeight / 2 - font1.BaseSize / 2 - 80 ); - Vector2 fontPosition2 = new( + fontPosition2 = new( screenWidth / 2 - MeasureTextEx(font2, msg2, font2.BaseSize, -2).X / 2, screenHeight / 2 - font2.BaseSize / 2 - 10 ); - Vector2 fontPosition3 = new( + fontPosition3 = new( screenWidth / 2 - MeasureTextEx(font3, msg3, font3.BaseSize, 2).X / 2, screenHeight / 2 - font3.BaseSize / 2 + 50 ); + } - SetTargetFPS(60); + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + // TODO: Update variables here... + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawTextEx(font1, msg1, fontPosition1, font1.BaseSize, -3, Color.White); + DrawTextEx(font2, msg2, fontPosition2, font2.BaseSize, -2, Color.White); + DrawTextEx(font3, msg3, fontPosition3, font3.BaseSize, 2, Color.White); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadFont(font1); // Font unloading + UnloadFont(font2); // Font unloading + UnloadFont(font3); // Font unloading + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [text] example - font spritefont"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new FontSpritefont(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update variables here... - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawTextEx(font1, msg1, fontPosition1, font1.BaseSize, -3, Color.White); - DrawTextEx(font2, msg2, fontPosition2, font2.BaseSize, -2, Color.White); - DrawTextEx(font3, msg3, fontPosition3, font3.BaseSize, 2, Color.White); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadFont(font1); - UnloadFont(font2); - UnloadFont(font3); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Text/FormatText.cs b/Examples/Text/FormatText.cs index b463e12..592b377 100644 --- a/Examples/Text/FormatText.cs +++ b/Examples/Text/FormatText.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [text] example - Text formatting +* raylib [text] example - format text * -* This example has been created using raylib 1.1 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 * -* Copyright (c) 2014 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.1, last time updated with raylib 3.0 +* +* 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -13,49 +17,72 @@ using static Raylib_cs.Raylib; namespace Examples.Text; -public class FormatText +public partial class FormatText : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Text / Format Text"; + + public string Title => "raylib [text] example - format text"; + + private int score; + private int hiscore; + private int lives; + + public void Init() + { + score = 100020; + hiscore = 200450; + lives = 5; + } + + public void Update() + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText($"Score: {score:D8}", 200, 80, 20, Color.Red); + + DrawText($"HiScore: {hiscore:D8}", 200, 120, 20, Color.Green); + + DrawText($"Lives: {lives:D2}", 200, 160, 40, Color.Blue); + + DrawText($"Elapsed Time: {GetFrameTime() * 1000:F2} ms", 200, 220, 20, Color.Black); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [text] example - format text"); - InitWindow(screenWidth, screenHeight, "raylib [text] example - text formatting"); - - int score = 100020; - int hiscore = 200450; - int lives = 5; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new FormatText(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update your variables here - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText($"Score: {score}", 200, 80, 20, Color.Red); - DrawText($"HiScore: {hiscore}", 200, 120, 20, Color.Green); - DrawText($"Lives: {lives}", 200, 160, 40, Color.Blue); - DrawText($"Elapsed Time: {GetFrameTime() * 1000} ms", 200, 220, 20, Color.Black); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Text/InputBox.cs b/Examples/Text/InputBox.cs index d15aee5..6356b5d 100644 --- a/Examples/Text/InputBox.cs +++ b/Examples/Text/InputBox.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [text] example - Input Box +* raylib [text] example - input box * -* This example has been created using raylib 1.7 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2017 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.7, last time updated with raylib 3.5 +* +* 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) 2017-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -13,149 +17,179 @@ using static Raylib_cs.Raylib; namespace Examples.Text; -public class InputBox +public partial class InputBox : IExample { public const int MaxInputChars = 9; + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Text / Input Box"; + + public string Title => "raylib [text] example - input box"; + + private char[] name; + private int letterCount; + + private Rectangle textBox; + private bool mouseOnText; + + private int framesCounter; + + public void Init() + { + // NOTE: One extra space required for null terminator char '\0' + name = new char[MaxInputChars + 1]; + letterCount = 0; + + textBox = new(screenWidth / 2 - 100, 180, 225, 50); + mouseOnText = false; + + framesCounter = 0; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (CheckCollisionPointRec(GetMousePosition(), textBox)) + { + mouseOnText = true; + } + else + { + mouseOnText = false; + } + + if (mouseOnText) + { + // Set the window's cursor to the I-Beam + SetMouseCursor(MouseCursor.IBeam); + + // Get char pressed (unicode character) on the queue + var key = GetCharPressed(); + + // Check if more characters have been pressed on the same frame + while (key > 0) + { + // NOTE: Only allow keys in range [32..125] + if ((key >= 32) && (key <= 125) && (letterCount < MaxInputChars)) + { + name[letterCount] = (char)key; + name[letterCount + 1] = '\0'; // Add null terminator at the end of the string + letterCount++; + } + + key = GetCharPressed(); // Check next character in the queue + } + + if (IsKeyPressed(KeyboardKey.Backspace)) + { + letterCount -= 1; + if (letterCount < 0) + { + letterCount = 0; + } + name[letterCount] = '\0'; + } + } + else + { + SetMouseCursor(MouseCursor.Default); + } + + if (mouseOnText) + { + framesCounter += 1; + } + else + { + framesCounter = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("PLACE MOUSE OVER INPUT BOX!", 240, 140, 20, Color.Gray); + DrawRectangleRec(textBox, Color.LightGray); + + if (mouseOnText) + { + DrawRectangleLines( + (int)textBox.X, + (int)textBox.Y, + (int)textBox.Width, + (int)textBox.Height, + Color.Red + ); + } + else + { + DrawRectangleLines( + (int)textBox.X, + (int)textBox.Y, + (int)textBox.Width, + (int)textBox.Height, + Color.DarkGray + ); + } + + DrawText(new string(name), (int)textBox.X + 5, (int)textBox.Y + 8, 40, Color.Maroon); + DrawText($"INPUT CHARS: {letterCount}/{MaxInputChars}", 315, 250, 20, Color.DarkGray); + + if (mouseOnText) + { + if (letterCount < MaxInputChars) + { + // Draw blinking underscore char + if ((framesCounter / 20 % 2) == 0) + { + DrawText( + "_", + (int)textBox.X + 8 + MeasureText(new string(name), 40), + (int)textBox.Y + 12, + 40, + Color.Maroon + ); + } + } + else + { + DrawText("Press BACKSPACE to delete chars...", 230, 300, 20, Color.Gray); + } + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [text] example - input box"); - // NOTE: One extra space required for line ending char '\0' - char[] name = new char[MaxInputChars]; - int letterCount = 0; - - Rectangle textBox = new(screenWidth / 2 - 100, 180, 225, 50); - bool mouseOnText = false; - - int framesCounter = 0; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new InputBox(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (CheckCollisionPointRec(GetMousePosition(), textBox)) - { - mouseOnText = true; - } - else - { - mouseOnText = false; - } - - if (mouseOnText) - { - // Set the window's cursor to the I-Beam - SetMouseCursor(MouseCursor.IBeam); - - // Check if more characters have been pressed on the same frame - int key = GetCharPressed(); - - while (key > 0) - { - // NOTE: Only allow keys in range [32..125] - if ((key >= 32) && (key <= 125) && (letterCount < MaxInputChars)) - { - name[letterCount] = (char)key; - letterCount++; - } - - // Check next character in the queue - key = GetCharPressed(); - } - - if (IsKeyPressed(KeyboardKey.Backspace)) - { - letterCount -= 1; - if (letterCount < 0) - { - letterCount = 0; - } - name[letterCount] = '\0'; - } - } - else - { - SetMouseCursor(MouseCursor.Default); - } - - if (mouseOnText) - { - framesCounter += 1; - } - else - { - framesCounter = 0; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("PLACE MOUSE OVER INPUT BOX!", 240, 140, 20, Color.Gray); - DrawRectangleRec(textBox, Color.LightGray); - - if (mouseOnText) - { - DrawRectangleLines( - (int)textBox.X, - (int)textBox.Y, - (int)textBox.Width, - (int)textBox.Height, - Color.Red - ); - } - else - { - DrawRectangleLines( - (int)textBox.X, - (int)textBox.Y, - (int)textBox.Width, - (int)textBox.Height, - Color.DarkGray - ); - } - - DrawText(new string(name), (int)textBox.X + 5, (int)textBox.Y + 8, 40, Color.Maroon); - DrawText($"INPUT CHARS: {letterCount}/{MaxInputChars}", 315, 250, 20, Color.DarkGray); - - if (mouseOnText) - { - if (letterCount < MaxInputChars) - { - // Draw blinking underscore char - if ((framesCounter / 20 % 2) == 0) - { - DrawText( - "_", - (int)textBox.X + 8 + MeasureText(new string(name), 40), - (int)textBox.Y + 12, - 40, - Color.Maroon - ); - } - } - else - { - DrawText("Press BACKSPACE to delete chars...", 230, 300, 20, Color.Gray); - } - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- CloseWindow(); diff --git a/Examples/Text/RaylibFonts.cs b/Examples/Text/RaylibFonts.cs index 59c2ef0..ba1da0a 100644 --- a/Examples/Text/RaylibFonts.cs +++ b/Examples/Text/RaylibFonts.cs @@ -1,14 +1,18 @@ /******************************************************************************************* * -* raylib [text] example - raylib font loading and usage +* raylib [text] example - sprite fonts +* +* Example complexity rating: [★☆☆☆] 1/4 * * NOTE: raylib is distributed with some free to use fonts (even for commercial pourposes!) * To view details and credits for those fonts, check raylib license file * -* This example has been created using raylib 1.7 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.7, last time updated with raylib 3.7 * -* Copyright (c) 2017 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) 2017-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -17,32 +21,38 @@ using static Raylib_cs.Raylib; namespace Examples.Text; -public class RaylibFonts +public partial class RaylibFonts : IExample { public const int MaxFonts = 8; - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Text / Raylib Fonts"; + + public string Title => "raylib [text] example - sprite fonts"; + + private Font[] fonts; + private string[] messages; + private int[] spacings; + private Vector2[] positions; + private Color[] colors; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [text] example - raylib fonts"); - // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - Font[] fonts = new Font[MaxFonts]; + fonts = new Font[MaxFonts]; - fonts[0] = LoadFont("resources/fonts/alagard.png"); - fonts[1] = LoadFont("resources/fonts/pixelplay.png"); - fonts[2] = LoadFont("resources/fonts/mecha.png"); - fonts[3] = LoadFont("resources/fonts/setback.png"); - fonts[4] = LoadFont("resources/fonts/romulus.png"); - fonts[5] = LoadFont("resources/fonts/pixantiqua.png"); - fonts[6] = LoadFont("resources/fonts/alpha_beta.png"); - fonts[7] = LoadFont("resources/fonts/jupiter_crash.png"); + fonts[0] = LoadFont("resources/sprite_fonts/alagard.png"); + fonts[1] = LoadFont("resources/sprite_fonts/pixelplay.png"); + fonts[2] = LoadFont("resources/sprite_fonts/mecha.png"); + fonts[3] = LoadFont("resources/sprite_fonts/setback.png"); + fonts[4] = LoadFont("resources/sprite_fonts/romulus.png"); + fonts[5] = LoadFont("resources/sprite_fonts/pixantiqua.png"); + fonts[6] = LoadFont("resources/sprite_fonts/alpha_beta.png"); + fonts[7] = LoadFont("resources/sprite_fonts/jupiter_crash.png"); - string[] messages = new string[MaxFonts] { + messages = new string[MaxFonts] { "ALAGARD FONT designed by Hewett Tsoi", "PIXELPLAY FONT designed by Aleksander Shevchuk", "MECHA FONT designed by Captain Falcon", @@ -53,12 +63,12 @@ public class RaylibFonts "JUPITER_CRASH FONT designed by Brian Kent (AEnigma)" }; - int[] spacings = new int[MaxFonts] { 2, 4, 8, 4, 3, 4, 4, 1 }; - Vector2[] positions = new Vector2[MaxFonts]; + spacings = new int[MaxFonts] { 2, 4, 8, 4, 3, 4, 4, 1 }; + positions = new Vector2[MaxFonts]; - for (int i = 0; i < MaxFonts; i++) + for (var i = 0; i < MaxFonts; i++) { - float halfWidth = MeasureTextEx(fonts[i], messages[i], fonts[i].BaseSize * 2, spacings[i]).X / 2; + var halfWidth = MeasureTextEx(fonts[i], messages[i], fonts[i].BaseSize * 2, spacings[i]).X / 2; positions[i].X = screenWidth / 2 - halfWidth; positions[i].Y = 60 + fonts[i].BaseSize + 45 * i; } @@ -68,7 +78,7 @@ public class RaylibFonts positions[4].Y += 2; positions[7].Y -= 8; - Color[] colors = new Color[MaxFonts] { + colors = new Color[MaxFonts] { Color.Maroon, Color.Orange, Color.DarkGreen, @@ -78,44 +88,61 @@ public class RaylibFonts Color.Gold, Color.Red }; - //-------------------------------------------------------------------------------------- + } - // Main game loop - while (!WindowShouldClose()) + public void Update() + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("free sprite fonts included with raylib", 220, 20, 20, Color.DarkGray); + DrawLine(220, 50, 600, 50, Color.DarkGray); + + for (var i = 0; i < MaxFonts; i++) { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update your variables here - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("free fonts included with raylib", 250, 20, 20, Color.DarkGray); - DrawLine(220, 50, 590, 50, Color.DarkGray); - - for (int i = 0; i < MaxFonts; i++) - { - DrawTextEx(fonts[i], messages[i], positions[i], fonts[i].BaseSize * 2, spacings[i], colors[i]); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + DrawTextEx(fonts[i], messages[i], positions[i], fonts[i].BaseSize * 2, spacings[i], colors[i]); } - // De-Initialization - //-------------------------------------------------------------------------------------- - for (int i = 0; i < MaxFonts; i++) + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + // Fonts unloading + for (var i = 0; i < MaxFonts; i++) { UnloadFont(fonts[i]); } + } - CloseWindow(); + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [text] example - sprite fonts"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + var game = new RaylibFonts(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + game.Update(); + } + + game.Unload(); + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } } - diff --git a/Examples/Text/RectangleBounds.cs b/Examples/Text/RectangleBounds.cs index b5ef7eb..3030298 100644 --- a/Examples/Text/RectangleBounds.cs +++ b/Examples/Text/RectangleBounds.cs @@ -1,153 +1,172 @@ +/******************************************************************************************* +* +* raylib [text] example - rectangle bounds +* +* Example complexity rating: [★★★★] 4/4 +* +* Example originally created with raylib 2.5, last time updated with raylib 4.0 +* +* Example contributed by Vlad Adrian (@demizdor) 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) 2018-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + using System.Numerics; using static Raylib_cs.Raylib; namespace Examples.Text; -public class RectangleBounds +public partial class RectangleBounds : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + // Minimum width and heigh for the container rectangle + private const float minWidth = 60; + private const float minHeight = 60; + private const float maxWidth = screenWidth - 50.0f; + private const float maxHeight = screenHeight - 160.0f; + + public string Name => "Text / Rectangle Bounds"; + + public string Title => "raylib [text] example - rectangle bounds"; + + private string text; + private bool resizing; + private bool wordWrap; + + private Rectangle container; + private Rectangle resizer; + + private Vector2 lastMouse; + private Color borderColor; + private Font font; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + text = "Text cannot escape\tthis container\t...word wrap also works when active so here's " + + "a long text for testing.\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod " + + "tempor incididunt ut labore et dolore magna aliqua. Nec ullamcorper sit amet risus nullam eget felis eget."; - InitWindow(screenWidth, screenHeight, "raylib [text] example - draw text inside a rectangle"); + resizing = false; + wordWrap = true; - string text = ""; - text += "Text cannot escape this container ...word wrap also works when active so here's a long text for testing."; - text += "\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor "; - text += "incididunt ut labore et dolore magna aliqua. Nec ullamcorper sit amet risus nullam eget felis eget."; - - bool resizing = false; - bool wordWrap = true; - - Rectangle container = new(25, 25, screenWidth - 50, screenHeight - 250); - Rectangle resizer = new( + container = new(25.0f, 25.0f, screenWidth - 50.0f, screenHeight - 250.0f); + resizer = new( container.X + container.Width - 17, container.Y + container.Height - 17, 14, 14 ); - // Minimum width and heigh for the container rectangle - const int minWidth = 60; - const int minHeight = 60; - const int maxWidth = screenWidth - 50; - const int maxHeight = screenHeight - 160; + lastMouse = new(0.0f, 0.0f); // Stores last mouse coordinates + borderColor = Color.Maroon; // Container border color + font = GetFontDefault(); // Get default system font + } - Vector2 lastMouse = new(0.0f, 0.0f); - Color borderColor = Color.Maroon; - Font font = GetFontDefault(); - - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KeyboardKey.Space)) { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyPressed(KeyboardKey.Space)) - { - wordWrap = !wordWrap; - } - - Vector2 mouse = GetMousePosition(); - - // Check if the mouse is inside the container and toggle border color - if (CheckCollisionPointRec(mouse, container)) - { - borderColor = ColorAlpha(Color.Maroon, 0.4f); - } - else if (!resizing) - { - borderColor = Color.Maroon; - } - - // Container resizing logic - if (resizing) - { - if (IsMouseButtonReleased(MouseButton.Left)) - { - resizing = false; - } - - int width = (int)(container.Width + (mouse.X - lastMouse.X)); - container.Width = (width > minWidth) ? ((width < maxWidth) ? width : maxWidth) : minWidth; - - int height = (int)(container.Height + (mouse.Y - lastMouse.Y)); - container.Height = (height > minHeight) ? ((height < maxHeight) ? height : maxHeight) : minHeight; - } - else - { - // Check if we're resizing - if (IsMouseButtonDown(MouseButton.Left) && CheckCollisionPointRec(mouse, resizer)) - { - resizing = true; - } - } - - // Move resizer rectangle properly - resizer.X = container.X + container.Width - 17; - resizer.Y = container.Y + container.Height - 17; - - lastMouse = mouse; // Update mouse - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // Draw container border - DrawRectangleLinesEx(container, 3, borderColor); - - // Draw text in container (add some padding) - DrawTextBoxed( - font, - text, - new Rectangle(container.X + 4, container.Y + 4, container.Width - 4, container.Height - 4), - 20.0f, - 2.0f, - wordWrap, - Color.Gray - ); - - DrawRectangleRec(resizer, borderColor); - - // Draw bottom info - DrawRectangle(0, screenHeight - 54, screenWidth, 54, Color.Gray); - DrawRectangleRec(new Rectangle(382, screenHeight - 34, 12, 12), Color.Maroon); - - DrawText("Word Wrap: ", 313, screenHeight - 115, 20, Color.Black); - - if (wordWrap) - { - DrawText("ON", 447, screenHeight - 115, 20, Color.Red); - } - else - { - DrawText("OFF", 447, screenHeight - 115, 20, Color.Black); - } - - DrawText("Press [SPACE] to toggle word wrap", 218, screenHeight - 86, 20, Color.Gray); - DrawText("Click hold & drag the to resize the container", 155, screenHeight - 38, 20, Color.RayWhite); - - EndDrawing(); - //---------------------------------------------------------------------------------- + wordWrap = !wordWrap; } - // De-Initialization - //-------------------------------------------------------------------------------------- - CloseWindow(); - //-------------------------------------------------------------------------------------- + var mouse = GetMousePosition(); - return 0; + // Check if the mouse is inside the container and toggle border color + if (CheckCollisionPointRec(mouse, container)) + { + borderColor = Fade(Color.Maroon, 0.4f); + } + else if (!resizing) + { + borderColor = Color.Maroon; + } + + // Container resizing logic + if (resizing) + { + if (IsMouseButtonReleased(MouseButton.Left)) + { + resizing = false; + } + + var width = container.Width + (mouse.X - lastMouse.X); + container.Width = (width > minWidth) ? ((width < maxWidth) ? width : maxWidth) : minWidth; + + var height = container.Height + (mouse.Y - lastMouse.Y); + container.Height = (height > minHeight) ? ((height < maxHeight) ? height : maxHeight) : minHeight; + } + else + { + // Check if we're resizing + if (IsMouseButtonDown(MouseButton.Left) && CheckCollisionPointRec(mouse, resizer)) + { + resizing = true; + } + } + + // Move resizer rectangle properly + resizer.X = container.X + container.Width - 17; + resizer.Y = container.Y + container.Height - 17; + + lastMouse = mouse; // Update mouse + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawRectangleLinesEx(container, 3, borderColor); // Draw container border + + // Draw text in container (add some padding) + DrawTextBoxed( + font, + text, + new Rectangle(container.X + 4, container.Y + 4, container.Width - 4, container.Height - 4), + 20.0f, + 2.0f, + wordWrap, + Color.Gray + ); + + DrawRectangleRec(resizer, borderColor); // Draw the resize box + + // Draw bottom info + DrawRectangle(0, screenHeight - 54, screenWidth, 54, Color.Gray); + DrawRectangleRec(new Rectangle(382, screenHeight - 34, 12, 12), Color.Maroon); + + DrawText("Word Wrap: ", 313, screenHeight - 115, 20, Color.Black); + + if (wordWrap) + { + DrawText("ON", 447, screenHeight - 115, 20, Color.Red); + } + else + { + DrawText("OFF", 447, screenHeight - 115, 20, Color.Black); + } + + DrawText("Press [SPACE] to toggle word wrap", 218, screenHeight - 86, 20, Color.Gray); + DrawText("Click hold & drag the to resize the container", 155, screenHeight - 38, 20, Color.RayWhite); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { } // Draw text using font inside rectangle limits - static void DrawTextBoxed( + private static void DrawTextBoxed( Font font, string text, Rectangle rec, @@ -161,7 +180,7 @@ public class RectangleBounds } // Draw text using font inside rectangle limits with support for text selection - static unsafe void DrawTextBoxedSelectable( + private static unsafe void DrawTextBoxedSelectable( Font font, string text, Rectangle rec, @@ -175,37 +194,37 @@ public class RectangleBounds Color selectBackTint ) { - int length = text.Length; + var length = text.Length; // Offset between lines (on line break '\n') float textOffsetY = 0; // Offset X to next character to draw - float textOffsetX = 0.0f; + var textOffsetX = 0.0f; // Character rectangle scaling factor - float scaleFactor = fontSize / (float)font.BaseSize; + var scaleFactor = fontSize / (float)font.BaseSize; // Word/character wrapping mechanism variables - bool shouldMeasure = wordWrap; + var shouldMeasure = wordWrap; // Index where to begin drawing (where a line begins) - int startLine = -1; + var startLine = -1; // Index where to stop drawing (where a line ends) - int endLine = -1; + var endLine = -1; // Holds last value of the character position - int lastk = -1; + var lastk = -1; using var textNative = new Utf8Buffer(text); for (int i = 0, k = 0; i < length; i++, k++) { // Get next codepoint from byte string and glyph index in font - int codepointByteCount = 0; - int codepoint = GetCodepoint(&textNative.AsPointer()[i], &codepointByteCount); - int index = GetGlyphIndex(font, codepoint); + var codepointByteCount = 0; + var codepoint = GetCodepoint(&textNative.AsPointer()[i], &codepointByteCount); + var index = GetGlyphIndex(font, codepoint); // 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 @@ -275,7 +294,7 @@ public class RectangleBounds glyphWidth = 0; // Save character position when we switch states - int tmp = lastk; + var tmp = lastk; lastk = k - 1; k = tmp; } @@ -305,7 +324,7 @@ public class RectangleBounds } // Draw selection background - bool isGlyphSelected = false; + var isGlyphSelected = false; if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength))) { DrawRectangleRec( @@ -354,4 +373,32 @@ public class RectangleBounds } } } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [text] example - rectangle bounds"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + var game = new RectangleBounds(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + game.Update(); + } + + game.Unload(); + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; + } } diff --git a/Examples/Text/Unicode.cs b/Examples/Text/Unicode.cs index c8d3c81..b24a9c6 100644 --- a/Examples/Text/Unicode.cs +++ b/Examples/Text/Unicode.cs @@ -1,330 +1,513 @@ /******************************************************************************************* * -* raylib [text] example - Using unicode with raylib +* raylib [text] example - unicode emojis * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★★] 4/4 +* +* Example originally created with raylib 2.5, last time updated with raylib 4.0 * * Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2019 Vlad Adrian (@demizdor) and 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) 2019-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ +using System.Numerics; +using System.Text; using Raylib_cs; using static Raylib_cs.Raylib; -using static Raylib_cs.Color; -namespace Examples.Text +namespace Examples.Text; + +public class Unicode { - public class Unicode - { - const int EMOJI_PER_WIDTH = 8; - const int EMOJI_PER_HEIGHT = 4; + const int EmojiPerWidth = 8; + const int EmojiPerHeight = 4; - // String containing 180 emoji codepoints separated by a '\0' char - string emojiCodepoints = @"\xF0\x9F\x8C\x80\x00\xF0\x9F\x98\x80\x00\xF0\x9F\x98\x82\x00\xF0\x9F\xA4\xA3\x00\xF0\x9F\x98\x83\x00\xF0\x9F\x98\x86\x00\xF0\x9F\x98\x89\x00" - "\xF0\x9F\x98\x8B\x00\xF0\x9F\x98\x8E\x00\xF0\x9F\x98\x8D\x00\xF0\x9F\x98\x98\x00\xF0\x9F\x98\x97\x00\xF0\x9F\x98\x99\x00\xF0\x9F\x98\x9A\x00\xF0\x9F\x99\x82\x00" - "\xF0\x9F\xA4\x97\x00\xF0\x9F\xA4\xA9\x00\xF0\x9F\xA4\x94\x00\xF0\x9F\xA4\xA8\x00\xF0\x9F\x98\x90\x00\xF0\x9F\x98\x91\x00\xF0\x9F\x98\xB6\x00\xF0\x9F\x99\x84\x00" - "\xF0\x9F\x98\x8F\x00\xF0\x9F\x98\xA3\x00\xF0\x9F\x98\xA5\x00\xF0\x9F\x98\xAE\x00\xF0\x9F\xA4\x90\x00\xF0\x9F\x98\xAF\x00\xF0\x9F\x98\xAA\x00\xF0\x9F\x98\xAB\x00" - "\xF0\x9F\x98\xB4\x00\xF0\x9F\x98\x8C\x00\xF0\x9F\x98\x9B\x00\xF0\x9F\x98\x9D\x00\xF0\x9F\xA4\xA4\x00\xF0\x9F\x98\x92\x00\xF0\x9F\x98\x95\x00\xF0\x9F\x99\x83\x00" - "\xF0\x9F\xA4\x91\x00\xF0\x9F\x98\xB2\x00\xF0\x9F\x99\x81\x00\xF0\x9F\x98\x96\x00\xF0\x9F\x98\x9E\x00\xF0\x9F\x98\x9F\x00\xF0\x9F\x98\xA4\x00\xF0\x9F\x98\xA2\x00" - "\xF0\x9F\x98\xAD\x00\xF0\x9F\x98\xA6\x00\xF0\x9F\x98\xA9\x00\xF0\x9F\xA4\xAF\x00\xF0\x9F\x98\xAC\x00\xF0\x9F\x98\xB0\x00\xF0\x9F\x98\xB1\x00\xF0\x9F\x98\xB3\x00" - "\xF0\x9F\xA4\xAA\x00\xF0\x9F\x98\xB5\x00\xF0\x9F\x98\xA1\x00\xF0\x9F\x98\xA0\x00\xF0\x9F\xA4\xAC\x00\xF0\x9F\x98\xB7\x00\xF0\x9F\xA4\x92\x00\xF0\x9F\xA4\x95\x00" - "\xF0\x9F\xA4\xA2\x00\xF0\x9F\xA4\xAE\x00\xF0\x9F\xA4\xA7\x00\xF0\x9F\x98\x87\x00\xF0\x9F\xA4\xA0\x00\xF0\x9F\xA4\xAB\x00\xF0\x9F\xA4\xAD\x00\xF0\x9F\xA7\x90\x00" - "\xF0\x9F\xA4\x93\x00\xF0\x9F\x98\x88\x00\xF0\x9F\x91\xBF\x00\xF0\x9F\x91\xB9\x00\xF0\x9F\x91\xBA\x00\xF0\x9F\x92\x80\x00\xF0\x9F\x91\xBB\x00\xF0\x9F\x91\xBD\x00" - "\xF0\x9F\x91\xBE\x00\xF0\x9F\xA4\x96\x00\xF0\x9F\x92\xA9\x00\xF0\x9F\x98\xBA\x00\xF0\x9F\x98\xB8\x00\xF0\x9F\x98\xB9\x00\xF0\x9F\x98\xBB\x00\xF0\x9F\x98\xBD\x00" - "\xF0\x9F\x99\x80\x00\xF0\x9F\x98\xBF\x00\xF0\x9F\x8C\xBE\x00\xF0\x9F\x8C\xBF\x00\xF0\x9F\x8D\x80\x00\xF0\x9F\x8D\x83\x00\xF0\x9F\x8D\x87\x00\xF0\x9F\x8D\x93\x00" - "\xF0\x9F\xA5\x9D\x00\xF0\x9F\x8D\x85\x00\xF0\x9F\xA5\xA5\x00\xF0\x9F\xA5\x91\x00\xF0\x9F\x8D\x86\x00\xF0\x9F\xA5\x94\x00\xF0\x9F\xA5\x95\x00\xF0\x9F\x8C\xBD\x00" - "\xF0\x9F\x8C\xB6\x00\xF0\x9F\xA5\x92\x00\xF0\x9F\xA5\xA6\x00\xF0\x9F\x8D\x84\x00\xF0\x9F\xA5\x9C\x00\xF0\x9F\x8C\xB0\x00\xF0\x9F\x8D\x9E\x00\xF0\x9F\xA5\x90\x00" - "\xF0\x9F\xA5\x96\x00\xF0\x9F\xA5\xA8\x00\xF0\x9F\xA5\x9E\x00\xF0\x9F\xA7\x80\x00\xF0\x9F\x8D\x96\x00\xF0\x9F\x8D\x97\x00\xF0\x9F\xA5\xA9\x00\xF0\x9F\xA5\x93\x00" - "\xF0\x9F\x8D\x94\x00\xF0\x9F\x8D\x9F\x00\xF0\x9F\x8D\x95\x00\xF0\x9F\x8C\xAD\x00\xF0\x9F\xA5\xAA\x00\xF0\x9F\x8C\xAE\x00\xF0\x9F\x8C\xAF\x00\xF0\x9F\xA5\x99\x00" - "\xF0\x9F\xA5\x9A\x00\xF0\x9F\x8D\xB3\x00\xF0\x9F\xA5\x98\x00\xF0\x9F\x8D\xB2\x00\xF0\x9F\xA5\xA3\x00\xF0\x9F\xA5\x97\x00\xF0\x9F\x8D\xBF\x00\xF0\x9F\xA5\xAB\x00" - "\xF0\x9F\x8D\xB1\x00\xF0\x9F\x8D\x98\x00\xF0\x9F\x8D\x9D\x00\xF0\x9F\x8D\xA0\x00\xF0\x9F\x8D\xA2\x00\xF0\x9F\x8D\xA5\x00\xF0\x9F\x8D\xA1\x00\xF0\x9F\xA5\x9F\x00" - "\xF0\x9F\xA5\xA1\x00\xF0\x9F\x8D\xA6\x00\xF0\x9F\x8D\xAA\x00\xF0\x9F\x8E\x82\x00\xF0\x9F\x8D\xB0\x00\xF0\x9F\xA5\xA7\x00\xF0\x9F\x8D\xAB\x00\xF0\x9F\x8D\xAF\x00" - "\xF0\x9F\x8D\xBC\x00\xF0\x9F\xA5\x9B\x00\xF0\x9F\x8D\xB5\x00\xF0\x9F\x8D\xB6\x00\xF0\x9F\x8D\xBE\x00\xF0\x9F\x8D\xB7\x00\xF0\x9F\x8D\xBB\x00\xF0\x9F\xA5\x82\x00" - "\xF0\x9F\xA5\x83\x00\xF0\x9F\xA5\xA4\x00\xF0\x9F\xA5\xA2\x00\xF0\x9F\x91\x81\x00\xF0\x9F\x91\x85\x00\xF0\x9F\x91\x84\x00\xF0\x9F\x92\x8B\x00\xF0\x9F\x92\x98\x00" - "\xF0\x9F\x92\x93\x00\xF0\x9F\x92\x97\x00\xF0\x9F\x92\x99\x00\xF0\x9F\x92\x9B\x00\xF0\x9F\xA7\xA1\x00\xF0\x9F\x92\x9C\x00\xF0\x9F\x96\xA4\x00\xF0\x9F\x92\x9D\x00" + // Arrays that holds the random emojis + struct EmojiInfo + { + public int Index; // Index inside `emojiCodepoints` + public int Message; // Message index + public Color Color; // Emoji color + } + + static EmojiInfo[] emoji = new EmojiInfo[EmojiPerWidth * EmojiPerHeight]; + + static int hovered = -1; + static int selected = -1; + + struct Message + { + public string Text; + public string Language; + + public Message(string text, string language) + { + Text = text; + Language = language; + } + } + + // String containing 180 emoji codepoints separated by a '\0' char + const string EmojiCodepoints = + "\xF0\x9F\x8C\x80\x00\xF0\x9F\x98\x80\x00\xF0\x9F\x98\x82\x00\xF0\x9F\xA4\xA3\x00\xF0\x9F\x98\x83\x00\xF0\x9F\x98\x86\x00\xF0\x9F\x98\x89\x00" + + "\xF0\x9F\x98\x8B\x00\xF0\x9F\x98\x8E\x00\xF0\x9F\x98\x8D\x00\xF0\x9F\x98\x98\x00\xF0\x9F\x98\x97\x00\xF0\x9F\x98\x99\x00\xF0\x9F\x98\x9A\x00\xF0\x9F\x99\x82\x00" + + "\xF0\x9F\xA4\x97\x00\xF0\x9F\xA4\xA9\x00\xF0\x9F\xA4\x94\x00\xF0\x9F\xA4\xA8\x00\xF0\x9F\x98\x90\x00\xF0\x9F\x98\x91\x00\xF0\x9F\x98\xB6\x00\xF0\x9F\x99\x84\x00" + + "\xF0\x9F\x98\x8F\x00\xF0\x9F\x98\xA3\x00\xF0\x9F\x98\xA5\x00\xF0\x9F\x98\xAE\x00\xF0\x9F\xA4\x90\x00\xF0\x9F\x98\xAF\x00\xF0\x9F\x98\xAA\x00\xF0\x9F\x98\xAB\x00" + + "\xF0\x9F\x98\xB4\x00\xF0\x9F\x98\x8C\x00\xF0\x9F\x98\x9B\x00\xF0\x9F\x98\x9D\x00\xF0\x9F\xA4\xA4\x00\xF0\x9F\x98\x92\x00\xF0\x9F\x98\x95\x00\xF0\x9F\x99\x83\x00" + + "\xF0\x9F\xA4\x91\x00\xF0\x9F\x98\xB2\x00\xF0\x9F\x99\x81\x00\xF0\x9F\x98\x96\x00\xF0\x9F\x98\x9E\x00\xF0\x9F\x98\x9F\x00\xF0\x9F\x98\xA4\x00\xF0\x9F\x98\xA2\x00" + + "\xF0\x9F\x98\xAD\x00\xF0\x9F\x98\xA6\x00\xF0\x9F\x98\xA9\x00\xF0\x9F\xA4\xAF\x00\xF0\x9F\x98\xAC\x00\xF0\x9F\x98\xB0\x00\xF0\x9F\x98\xB1\x00\xF0\x9F\x98\xB3\x00" + + "\xF0\x9F\xA4\xAA\x00\xF0\x9F\x98\xB5\x00\xF0\x9F\x98\xA1\x00\xF0\x9F\x98\xA0\x00\xF0\x9F\xA4\xAC\x00\xF0\x9F\x98\xB7\x00\xF0\x9F\xA4\x92\x00\xF0\x9F\xA4\x95\x00" + + "\xF0\x9F\xA4\xA2\x00\xF0\x9F\xA4\xAE\x00\xF0\x9F\xA4\xA7\x00\xF0\x9F\x98\x87\x00\xF0\x9F\xA4\xA0\x00\xF0\x9F\xA4\xAB\x00\xF0\x9F\xA4\xAD\x00\xF0\x9F\xA7\x90\x00" + + "\xF0\x9F\xA4\x93\x00\xF0\x9F\x98\x88\x00\xF0\x9F\x91\xBF\x00\xF0\x9F\x91\xB9\x00\xF0\x9F\x91\xBA\x00\xF0\x9F\x92\x80\x00\xF0\x9F\x91\xBB\x00\xF0\x9F\x91\xBD\x00" + + "\xF0\x9F\x91\xBE\x00\xF0\x9F\xA4\x96\x00\xF0\x9F\x92\xA9\x00\xF0\x9F\x98\xBA\x00\xF0\x9F\x98\xB8\x00\xF0\x9F\x98\xB9\x00\xF0\x9F\x98\xBB\x00\xF0\x9F\x98\xBD\x00" + + "\xF0\x9F\x99\x80\x00\xF0\x9F\x98\xBF\x00\xF0\x9F\x8C\xBE\x00\xF0\x9F\x8C\xBF\x00\xF0\x9F\x8D\x80\x00\xF0\x9F\x8D\x83\x00\xF0\x9F\x8D\x87\x00\xF0\x9F\x8D\x93\x00" + + "\xF0\x9F\xA5\x9D\x00\xF0\x9F\x8D\x85\x00\xF0\x9F\xA5\xA5\x00\xF0\x9F\xA5\x91\x00\xF0\x9F\x8D\x86\x00\xF0\x9F\xA5\x94\x00\xF0\x9F\xA5\x95\x00\xF0\x9F\x8C\xBD\x00" + + "\xF0\x9F\x8C\xB6\x00\xF0\x9F\xA5\x92\x00\xF0\x9F\xA5\xA6\x00\xF0\x9F\x8D\x84\x00\xF0\x9F\xA5\x9C\x00\xF0\x9F\x8C\xB0\x00\xF0\x9F\x8D\x9E\x00\xF0\x9F\xA5\x90\x00" + + "\xF0\x9F\xA5\x96\x00\xF0\x9F\xA5\xA8\x00\xF0\x9F\xA5\x9E\x00\xF0\x9F\xA7\x80\x00\xF0\x9F\x8D\x96\x00\xF0\x9F\x8D\x97\x00\xF0\x9F\xA5\xA9\x00\xF0\x9F\xA5\x93\x00" + + "\xF0\x9F\x8D\x94\x00\xF0\x9F\x8D\x9F\x00\xF0\x9F\x8D\x95\x00\xF0\x9F\x8C\xAD\x00\xF0\x9F\xA5\xAA\x00\xF0\x9F\x8C\xAE\x00\xF0\x9F\x8C\xAF\x00\xF0\x9F\xA5\x99\x00" + + "\xF0\x9F\xA5\x9A\x00\xF0\x9F\x8D\xB3\x00\xF0\x9F\xA5\x98\x00\xF0\x9F\x8D\xB2\x00\xF0\x9F\xA5\xA3\x00\xF0\x9F\xA5\x97\x00\xF0\x9F\x8D\xBF\x00\xF0\x9F\xA5\xAB\x00" + + "\xF0\x9F\x8D\xB1\x00\xF0\x9F\x8D\x98\x00\xF0\x9F\x8D\x9D\x00\xF0\x9F\x8D\xA0\x00\xF0\x9F\x8D\xA2\x00\xF0\x9F\x8D\xA5\x00\xF0\x9F\x8D\xA1\x00\xF0\x9F\xA5\x9F\x00" + + "\xF0\x9F\xA5\xA1\x00\xF0\x9F\x8D\xA6\x00\xF0\x9F\x8D\xAA\x00\xF0\x9F\x8E\x82\x00\xF0\x9F\x8D\xB0\x00\xF0\x9F\xA5\xA7\x00\xF0\x9F\x8D\xAB\x00\xF0\x9F\x8D\xAF\x00" + + "\xF0\x9F\x8D\xBC\x00\xF0\x9F\xA5\x9B\x00\xF0\x9F\x8D\xB5\x00\xF0\x9F\x8D\xB6\x00\xF0\x9F\x8D\xBE\x00\xF0\x9F\x8D\xB7\x00\xF0\x9F\x8D\xBB\x00\xF0\x9F\xA5\x82\x00" + + "\xF0\x9F\xA5\x83\x00\xF0\x9F\xA5\xA4\x00\xF0\x9F\xA5\xA2\x00\xF0\x9F\x91\x81\x00\xF0\x9F\x91\x85\x00\xF0\x9F\x91\x84\x00\xF0\x9F\x92\x8B\x00\xF0\x9F\x92\x98\x00" + + "\xF0\x9F\x92\x93\x00\xF0\x9F\x92\x97\x00\xF0\x9F\x92\x99\x00\xF0\x9F\x92\x9B\x00\xF0\x9F\xA7\xA1\x00\xF0\x9F\x92\x9C\x00\xF0\x9F\x96\xA4\x00\xF0\x9F\x92\x9D\x00" + "\xF0\x9F\x92\x9F\x00\xF0\x9F\x92\x8C\x00\xF0\x9F\x92\xA4\x00\xF0\x9F\x92\xA2\x00\xF0\x9F\x92\xA3\x00"; - struct { - string text; - string language; - } - const messages[] = { // Array containing all of the emojis messages - {"\x46\x61\x6C\x73\x63\x68\x65\x73\x20\xC3\x9C\x62\x65\x6E\x20\x76\x6F\x6E\x20\x58\x79\x6C\x6F\x70\x68\x6F\x6E\x6D\x75\x73\x69\x6B\x20\x71\x75\xC3\xA4\x6C" - "\x74\x20\x6A\x65\x64\x65\x6E\x20\x67\x72\xC3\xB6\xC3\x9F\x65\x72\x65\x6E\x20\x5A\x77\x65\x72\x67", "German"}, - {"\x42\x65\x69\xC3\x9F\x20\x6E\x69\x63\x68\x74\x20\x69\x6E\x20\x64\x69\x65\x20\x48\x61\x6E\x64\x2C\x20\x64\x69\x65\x20\x64\x69\x63\x68\x20\x66\xC3\xBC\x74" - "\x74\x65\x72\x74\x2E", "German"}, - {"\x41\x75\xC3\x9F\x65\x72\x6F\x72\x64\x65\x6E\x74\x6C\x69\x63\x68\x65\x20\xC3\x9C\x62\x65\x6C\x20\x65\x72\x66\x6F\x72\x64\x65\x72\x6E\x20\x61\x75\xC3\x9F" - "\x65\x72\x6F\x72\x64\x65\x6E\x74\x6C\x69\x63\x68\x65\x20\x4D\x69\x74\x74\x65\x6C\x2E", "German"}, - {"\xD4\xBF\xD6\x80\xD5\xB6\xD5\xA1\xD5\xB4\x20\xD5\xA1\xD5\xBA\xD5\xA1\xD5\xAF\xD5\xAB\x20\xD5\xB8\xD6\x82\xD5\xBF\xD5\xA5\xD5\xAC\x20\xD6\x87\x20\xD5\xAB" - "\xD5\xB6\xD5\xAE\xD5\xAB\x20\xD5\xA1\xD5\xB6\xD5\xB0\xD5\xA1\xD5\xB6\xD5\xA3\xD5\xAB\xD5\xBD\xD5\xBF\x20\xD5\xB9\xD5\xA8\xD5\xB6\xD5\xA5\xD6\x80", "Armenian"}, - {"\xD4\xB5\xD6\x80\xD5\xA2\x20\xD5\xB8\xD6\x80\x20\xD5\xAF\xD5\xA1\xD6\x81\xD5\xAB\xD5\xB6\xD5\xA8\x20\xD5\xA5\xD5\xAF\xD5\xA1\xD6\x82\x20\xD5\xA1\xD5\xB6\xD5" - "\xBF\xD5\xA1\xD5\xBC\x2C\x20\xD5\xAE\xD5\xA1\xD5\xBC\xD5\xA5\xD6\x80\xD5\xA8\x20\xD5\xA1\xD5\xBD\xD5\xA1\xD6\x81\xD5\xAB\xD5\xB6\x2E\x2E\x2E\x20\xC2\xAB\xD4\xBF" - "\xD5\xB8\xD5\xBF\xD5\xA8\x20\xD5\xB4\xD5\xA5\xD6\x80\xD5\xB8\xD5\xB6\xD6\x81\xD5\xAB\xD6\x81\x20\xD5\xA7\x3A\xC2\xBB", "Armenian"}, - {"\xD4\xB3\xD5\xA1\xD5\xBC\xD5\xA8\xD5\x9D\x20\xD5\xA3\xD5\xA1\xD6\x80\xD5\xB6\xD5\xA1\xD5\xB6\x2C\x20\xD5\xB1\xD5\xAB\xD6\x82\xD5\xB6\xD5\xA8\xD5\x9D\x20\xD5" - "\xB1\xD5\xB4\xD5\xBC\xD5\xA1\xD5\xB6", "Armenian"}, - {"\x4A\x65\xC5\xBC\x75\x20\x6B\x6C\xC4\x85\x74\x77\x2C\x20\x73\x70\xC5\x82\xC3\xB3\x64\xC5\xBA\x20\x46\x69\x6E\x6F\x6D\x20\x63\x7A\xC4\x99\xC5\x9B\xC4\x87" - "\x20\x67\x72\x79\x20\x68\x61\xC5\x84\x62\x21", "Polish"}, - {"\x44\x6F\x62\x72\x79\x6D\x69\x20\x63\x68\xC4\x99\x63\x69\x61\x6D\x69\x20\x6A\x65\x73\x74\x20\x70\x69\x65\x6B\xC5\x82\x6F\x20\x77\x79\x62\x72\x75\x6B\x6F" - "\x77\x61\x6E\x65\x2E", "Polish"}, - {"\xC3\x8E\xC8\x9B\x69\x20\x6D\x75\x6C\xC8\x9B\x75\x6D\x65\x73\x63\x20\x63\xC4\x83\x20\x61\x69\x20\x61\x6C\x65\x73\x20\x72\x61\x79\x6C\x69\x62\x2E\x0A\xC8\x98" - "\x69\x20\x73\x70\x65\x72\x20\x73\xC4\x83\x20\x61\x69\x20\x6F\x20\x7A\x69\x20\x62\x75\x6E\xC4\x83\x21", "Romanian"}, - {"\xD0\xAD\xD1\x85\x2C\x20\xD1\x87\xD1\x83\xD0\xB6\xD0\xB0\xD0\xBA\x2C\x20\xD0\xBE\xD0\xB1\xD1\x89\xD0\xB8\xD0\xB9\x20\xD1\x81\xD1\x8A\xD1\x91\xD0\xBC\x20" - "\xD1\x86\xD0\xB5\xD0\xBD\x20\xD1\x88\xD0\xBB\xD1\x8F\xD0\xBF\x20\x28\xD1\x8E\xD1\x84\xD1\x82\xD1\x8C\x29\x20\xD0\xB2\xD0\xB4\xD1\x80\xD1\x8B\xD0\xB7\xD0\xB3\x21", "Russian"}, - {"\xD0\xAF\x20\xD0\xBB\xD1\x8E\xD0\xB1\xD0\xBB\xD1\x8E\x20\x72\x61\x79\x6C\x69\x62\x21", "Russian"}, - {"\xD0\x9C\xD0\xBE\xD0\xBB\xD1\x87\xD0\xB8\x2C\x20\xD1\x81\xD0\xBA\xD1\x80\xD1\x8B\xD0\xB2\xD0\xB0\xD0\xB9\xD1\x81\xD1\x8F\x20\xD0\xB8\x20\xD1\x82\xD0\xB0\xD0\xB8" - "\x0A\xD0\x98\x20\xD1\x87\xD1\x83\xD0\xB2\xD1\x81\xD1\x82\xD0\xB2\xD0\xB0\x20\xD0\xB8\x20\xD0\xBC\xD0\xB5\xD1\x87\xD1\x82\xD1\x8B\x20\xD1\x81\xD0\xB2\xD0\xBE\xD0\xB8\x20" - "\xE2\x80\x93\x0A\xD0\x9F\xD1\x83\xD1\x81\xD0\xBA\xD0\xB0\xD0\xB9\x20\xD0\xB2\x20\xD0\xB4\xD1\x83\xD1\x88\xD0\xB5\xD0\xB2\xD0\xBD\xD0\xBE\xD0\xB9\x20\xD0\xB3\xD0\xBB\xD1" - "\x83\xD0\xB1\xD0\xB8\xD0\xBD\xD0\xB5\x0A\xD0\x98\x20\xD0\xB2\xD1\x81\xD1\x85\xD0\xBE\xD0\xB4\xD1\x8F\xD1\x82\x20\xD0\xB8\x20\xD0\xB7\xD0\xB0\xD0\xB9\xD0\xB4\xD1\x83\xD1" - "\x82\x20\xD0\xBE\xD0\xBD\xD0\xB5\x0A\xD0\x9A\xD0\xB0\xD0\xBA\x20\xD0\xB7\xD0\xB2\xD0\xB5\xD0\xB7\xD0\xB4\xD1\x8B\x20\xD1\x8F\xD1\x81\xD0\xBD\xD1\x8B\xD0\xB5\x20\xD0\xB2" - "\x20\xD0\xBD\xD0\xBE\xD1\x87\xD0\xB8\x2D\x0A\xD0\x9B\xD1\x8E\xD0\xB1\xD1\x83\xD0\xB9\xD1\x81\xD1\x8F\x20\xD0\xB8\xD0\xBC\xD0\xB8\x20\xE2\x80\x93\x20\xD0\xB8\x20\xD0\xBC" - "\xD0\xBE\xD0\xBB\xD1\x87\xD0\xB8\x2E", "Russian"}, - {"\x56\x6F\x69\x78\x20\x61\x6D\x62\x69\x67\x75\xC3\xAB\x20\x64\xE2\x80\x99\x75\x6E\x20\x63\xC5\x93\x75\x72\x20\x71\x75\x69\x20\x61\x75\x20\x7A\xC3\xA9\x70" - "\x68\x79\x72\x20\x70\x72\xC3\xA9\x66\xC3\xA8\x72\x65\x20\x6C\x65\x73\x20\x6A\x61\x74\x74\x65\x73\x20\x64\x65\x20\x6B\x69\x77\x69", "French"}, - {"\x42\x65\x6E\x6A\x61\x6D\xC3\xAD\x6E\x20\x70\x69\x64\x69\xC3\xB3\x20\x75\x6E\x61\x20\x62\x65\x62\x69\x64\x61\x20\x64\x65\x20\x6B\x69\x77\x69\x20\x79\x20" - "\x66\x72\x65\x73\x61\x3B\x20\x4E\x6F\xC3\xA9\x2C\x20\x73\x69\x6E\x20\x76\x65\x72\x67\xC3\xBC\x65\x6E\x7A\x61\x2C\x20\x6C\x61\x20\x6D\xC3\xA1\x73\x20\x65\x78" - "\x71\x75\x69\x73\x69\x74\x61\x20\x63\x68\x61\x6D\x70\x61\xC3\xB1\x61\x20\x64\x65\x6C\x20\x6D\x65\x6E\xC3\xBA\x2E", "Spanish"}, - {"\xCE\xA4\xCE\xB1\xCF\x87\xCE\xAF\xCF\x83\xCF\x84\xCE\xB7\x20\xCE\xB1\xCE\xBB\xCF\x8E\xCF\x80\xCE\xB7\xCE\xBE\x20\xCE\xB2\xCE\xB1\xCF\x86\xCE\xAE\xCF\x82\x20" - "\xCF\x88\xCE\xB7\xCE\xBC\xCE\xAD\xCE\xBD\xCE\xB7\x20\xCE\xB3\xCE\xB7\x2C\x20\xCE\xB4\xCF\x81\xCE\xB1\xCF\x83\xCE\xBA\xCE\xB5\xCE\xBB\xCE\xAF\xCE\xB6\xCE\xB5\xCE" - "\xB9\x20\xCF\x85\xCF\x80\xCE\xAD\xCF\x81\x20\xCE\xBD\xCF\x89\xCE\xB8\xCF\x81\xCE\xBF\xCF\x8D\x20\xCE\xBA\xCF\x85\xCE\xBD\xCF\x8C\xCF\x82", "Greek"}, - {"\xCE\x97\x20\xCE\xBA\xCE\xB1\xCE\xBB\xCF\x8D\xCF\x84\xCE\xB5\xCF\x81\xCE\xB7\x20\xCE\xAC\xCE\xBC\xCF\x85\xCE\xBD\xCE\xB1\x20\xCE\xB5\xCE\xAF\xCE\xBD" - "\xCE\xB1\xCE\xB9\x20\xCE\xB7\x20\xCE\xB5\xCF\x80\xCE\xAF\xCE\xB8\xCE\xB5\xCF\x83\xCE\xB7\x2E", "Greek"}, - {"\xCE\xA7\xCF\x81\xCF\x8C\xCE\xBD\xCE\xB9\xCE\xB1\x20\xCE\xBA\xCE\xB1\xCE\xB9\x20\xCE\xB6\xCE\xB1\xCE\xBC\xCE\xAC\xCE\xBD\xCE\xB9\xCE\xB1\x21", "Greek"}, - {"\xCE\xA0\xCF\x8E\xCF\x82\x20\xCF\x84\xCE\xB1\x20\xCF\x80\xCE\xB1\xCF\x82\x20\xCF\x83\xCE\xAE\xCE\xBC\xCE\xB5\xCF\x81\xCE\xB1\x3B", "Greek"}, - - {"\xE6\x88\x91\xE8\x83\xBD\xE5\x90\x9E\xE4\xB8\x8B\xE7\x8E\xBB\xE7\x92\x83\xE8\x80\x8C\xE4\xB8\x8D\xE4\xBC\xA4\xE8\xBA\xAB\xE4\xBD\x93\xE3\x80\x82", "Chinese"}, - {"\xE4\xBD\xA0\xE5\x90\x83\xE4\xBA\x86\xE5\x90\x97\xEF\xBC\x9F", "Chinese"}, - {"\xE4\xB8\x8D\xE4\xBD\x9C\xE4\xB8\x8D\xE6\xAD\xBB\xE3\x80\x82", "Chinese"}, - {"\xE6\x9C\x80\xE8\xBF\x91\xE5\xA5\xBD\xE5\x90\x97\xEF\xBC\x9F", "Chinese"}, - {"\xE5\xA1\x9E\xE7\xBF\x81\xE5\xA4\xB1\xE9\xA9\xAC\xEF\xBC\x8C\xE7\x84\x89\xE7\x9F\xA5\xE9\x9D\x9E\xE7\xA6\x8F\xE3\x80\x82", "Chinese"}, - {"\xE5\x8D\x83\xE5\x86\x9B\xE6\x98\x93\xE5\xBE\x97\x2C\x20\xE4\xB8\x80\xE5\xB0\x86\xE9\x9A\xBE\xE6\xB1\x82", "Chinese"}, - {"\xE4\xB8\x87\xE4\xBA\x8B\xE5\xBC\x80\xE5\xA4\xB4\xE9\x9A\xBE\xE3\x80\x82", "Chinese"}, - {"\xE9\xA3\x8E\xE6\x97\xA0\xE5\xB8\xB8\xE9\xA1\xBA\xEF\xBC\x8C\xE5\x85\xB5\xE6\x97\xA0\xE5\xB8\xB8\xE8\x83\x9C\xE3\x80\x82", "Chinese"}, - {"\xE6\xB4\xBB\xE5\x88\xB0\xE8\x80\x81\xEF\xBC\x8C\xE5\xAD\xA6\xE5\x88\xB0\xE8\x80\x81\xE3\x80\x82", "Chinese"}, - {"\xE4\xB8\x80\xE8\xA8\x80\xE6\x97\xA2\xE5\x87\xBA\xEF\xBC\x8C\xE9\xA9\xB7\xE9\xA9\xAC\xE9\x9A\xBE\xE8\xBF\xBD\xE3\x80\x82", "Chinese"}, - {"\xE8\xB7\xAF\xE9\x81\xA5\xE7\x9F\xA5\xE9\xA9\xAC\xE5\x8A\x9B\xEF\xBC\x8C\xE6\x97\xA5\xE4\xB9\x85\xE8\xA7\x81\xE4\xBA\xBA\xE5\xBF\x83", "Chinese"}, - {"\xE6\x9C\x89\xE7\x90\x86\xE8\xB5\xB0\xE9\x81\x8D\xE5\xA4\xA9\xE4\xB8\x8B\xEF\xBC\x8C\xE6\x97\xA0\xE7\x90\x86\xE5\xAF\xB8\xE6\xAD\xA5\xE9\x9A\xBE\xE8\xA1\x8C\xE3\x80\x82", "Chinese"}, - - {"\xE7\x8C\xBF\xE3\x82\x82\xE6\x9C\xA8\xE3\x81\x8B\xE3\x82\x89\xE8\x90\xBD\xE3\x81\xA1\xE3\x82\x8B", "Japanese"}, - {"\xE4\xBA\x80\xE3\x81\xAE\xE7\x94\xB2\xE3\x82\x88\xE3\x82\x8A\xE5\xB9\xB4\xE3\x81\xAE\xE5\x8A\x9F", "Japanese"}, - {"\xE3\x81\x86\xE3\x82\x89\xE3\x82\x84\xE3\x81\xBE\xE3\x81\x97\x20\x20\xE6\x80\x9D\xE3\x81\xB2\xE5\x88\x87\xE3\x82\x8B\xE6\x99\x82\x20\x20\xE7\x8C\xAB\xE3\x81\xAE\xE6\x81\x8B", "Japanese"}, - {"\xE8\x99\x8E\xE7\xA9\xB4\xE3\x81\xAB\xE5\x85\xA5\xE3\x82\x89\xE3\x81\x9A\xE3\x82\x93\xE3\x81\xB0\xE8\x99\x8E\xE5\xAD\x90\xE3\x82\x92\xE5\xBE\x97\xE3\x81\x9A\xE3\x80\x82", "Japanese"}, - {"\xE4\xBA\x8C\xE5\x85\x8E\xE3\x82\x92\xE8\xBF\xBD\xE3\x81\x86\xE8\x80\x85\xE3\x81\xAF\xE4\xB8\x80\xE5\x85\x8E\xE3\x82\x92\xE3\x82\x82\xE5\xBE\x97\xE3\x81\x9A\xE3\x80\x82", "Japanese"}, - {"\xE9\xA6\xAC\xE9\xB9\xBF\xE3\x81\xAF\xE6\xAD\xBB\xE3\x81\xAA\xE3\x81\xAA\xE3\x81\x8D\xE3\x82\x83\xE6\xB2\xBB\xE3\x82\x89\xE3\x81\xAA\xE3\x81\x84\xE3\x80\x82", "Japanese"}, - {"\xE6\x9E\xAF\xE9\x87\x8E\xE8\xB7\xAF\xE3\x81\xAB\xE3\x80\x80\xE5\xBD\xB1\xE3\x81\x8B\xE3\x81\x95\xE3\x81\xAA\xE3\x82\x8A\xE3\x81\xA6\xE3\x80\x80\xE3\x82\x8F\xE3\x81\x8B\xE3\x82\x8C\xE3\x81\x91\xE3\x82\x8A", "Japanese"}, - {"\xE7\xB9\xB0\xE3\x82\x8A\xE8\xBF\x94\xE3\x81\x97\xE9\xBA\xA6\xE3\x81\xAE\xE7\x95\x9D\xE7\xB8\xAB\xE3\x81\xB5\xE8\x83\xA1\xE8\x9D\xB6\xE5\x93\x89", "Japanese"}, - - {"\xEC\x95\x84\xEB\x93\x9D\xED\x95\x9C\x20\xEB\xB0\x94\xEB\x8B\xA4\x20\xEC\x9C\x84\xEC\x97\x90\x20\xEA\xB0\x88\xEB\xA7\xA4\xEA\xB8\xB0\x20\xEB\x91\x90\xEC\x97\x87\x20" - "\xEB\x82\xA0\xEC\x95\x84\x20\xEB\x8F\x88\xEB\x8B\xA4\x2E\x0A\xEB\x84\x88\xED\x9B\x8C\xEB\x84\x88\xED\x9B\x8C\x20\xEC\x8B\x9C\xEB\xA5\xBC\x20\xEC\x93\xB4\xEB\x8B\xA4\x2E" - "\x20\xEB\xAA\xA8\xEB\xA5\xB4\xEB\x8A\x94\x20\xEB\x82\x98\xEB\x9D\xBC\x20\xEA\xB8\x80\xEC\x9E\x90\xEB\x8B\xA4\x2E\x0A\xEB\x84\x90\xEB\x94\xB0\xEB\x9E\x80\x20\xED\x95\x98" - "\xEB\x8A\x98\x20\xEB\xB3\xB5\xED\x8C\x90\xEC\x97\x90\x20\xEB\x82\x98\xEB\x8F\x84\x20\xEA\xB0\x99\xEC\x9D\xB4\x20\xEC\x8B\x9C\xEB\xA5\xBC\x20\xEC\x93\xB4\xEB\x8B\xA4\x2E", "Korean"}, - {"\xEC\xA0\x9C\x20\xEB\x88\x88\xEC\x97\x90\x20\xEC\x95\x88\xEA\xB2\xBD\xEC\x9D\xB4\xEB\x8B\xA4", "Korean"}, - {"\xEA\xBF\xA9\x20\xEB\xA8\xB9\xEA\xB3\xA0\x20\xEC\x95\x8C\x20\xEB\xA8\xB9\xEB\x8A\x94\xEB\x8B\xA4", "Korean"}, - {"\xEB\xA1\x9C\xEB\xA7\x88\xEB\x8A\x94\x20\xED\x95\x98\xEB\xA3\xA8\xEC\x95\x84\xEC\xB9\xA8\xEC\x97\x90\x20\xEC\x9D\xB4\xEB\xA3\xA8\xEC\x96\xB4\xEC\xA7\x84\x20\xEA\xB2\x83\xEC\x9D\xB4" - "\x20\xEC\x95\x84\xEB\x8B\x88\xEB\x8B\xA4", "Korean"}, - {"\xEA\xB3\xA0\xEC\x83\x9D\x20\xEB\x81\x9D\xEC\x97\x90\x20\xEB\x82\x99\xEC\x9D\xB4\x20\xEC\x98\xA8\xEB\x8B\xA4", "Korean"}, - {"\xEA\xB0\x9C\xEC\xB2\x9C\xEC\x97\x90\xEC\x84\x9C\x20\xEC\x9A\xA9\x20\xEB\x82\x9C\xEB\x8B\xA4", "Korean"}, - {"\xEC\x95\x88\xEB\x85\x95\xED\x95\x98\xEC\x84\xB8\xEC\x9A\x94\x3F", "Korean"}, - {"\xEB\xA7\x8C\xEB\x82\x98\xEC\x84\x9C\x20\xEB\xB0\x98\xEA\xB0\x91\xEC\x8A\xB5\xEB\x8B\x88\xEB\x8B\xA4", "Korean"}, - {"\xED\x95\x9C\xEA\xB5\xAD\xEB\xA7\x90\x20\xED\x95\x98\xEC\x8B\xA4\x20\xEC\xA4\x84\x20\xEC\x95\x84\xEC\x84\xB8\xEC\x9A\x94\x3F", "Korean"}, + // Array containing all of the emojis messages + static Message[] messages = new Message[] + { + new Message("\x46\x61\x6C\x73\x63\x68\x65\x73\x20\xC3\x9C\x62\x65\x6E\x20\x76\x6F\x6E\x20\x58\x79\x6C\x6F\x70\x68\x6F\x6E\x6D\x75\x73\x69\x6B\x20\x71\x75\xC3\xA4\x6C\x74\x20\x6A\x65\x64\x65\x6E\x20\x67\x72\xC3\xB6\xC3\x9F\x65\x72\x65\x6E\x20\x5A\x77\x65\x72\x67", "German"), + new Message("\x42\x65\x69\xC3\x9F\x20\x6E\x69\x63\x68\x74\x20\x69\x6E\x20\x64\x69\x65\x20\x48\x61\x6E\x64\x2C\x20\x64\x69\x65\x20\x64\x69\x63\x68\x20\x66\xC3\xBC\x74\x74\x65\x72\x74\x2E", "German"), + new Message("\x41\x75\xC3\x9F\x65\x72\x6F\x72\x64\x65\x6E\x74\x6C\x69\x63\x68\x65\x20\xC3\x9C\x62\x65\x6C\x20\x65\x72\x66\x6F\x72\x64\x65\x72\x6E\x20\x61\x75\xC3\x9F\x65\x72\x6F\x72\x64\x65\x6E\x74\x6C\x69\x63\x68\x65\x20\x4D\x69\x74\x74\x65\x6C\x2E", "German"), + new Message("\xD4\xBF\xD6\x80\xD5\xB6\xD5\xA1\xD5\xB4\x20\xD5\xA1\xD5\xBA\xD5\xA1\xD5\xAF\xD5\xAB\x20\xD5\xB8\xD6\x82\xD5\xBF\xD5\xA5\xD5\xAC\x20\xD6\x87\x20\xD5\xAB\xD5\xB6\xD5\xAE\xD5\xAB\x20\xD5\xA1\xD5\xB6\xD5\xB0\xD5\xA1\xD5\xB6\xD5\xA3\xD5\xAB\xD5\xBD\xD5\xBF\x20\xD5\xB9\xD5\xA8\xD5\xB6\xD5\xA5\xD6\x80", "Armenian"), + new Message("\xD4\xB5\xD6\x80\xD5\xA2\x20\xD5\xB8\xD6\x80\x20\xD5\xAF\xD5\xA1\xD6\x81\xD5\xAB\xD5\xB6\xD5\xA8\x20\xD5\xA5\xD5\xAF\xD5\xA1\xD6\x82\x20\xD5\xA1\xD5\xB6\xD5\xBF\xD5\xA1\xD5\xBC\x2C\x20\xD5\xAE\xD5\xA1\xD5\xBC\xD5\xA5\xD6\x80\xD5\xA8\x20\xD5\xA1\xD5\xBD\xD5\xA1\xD6\x81\xD5\xAB\xD5\xB6\x2E\x2E\x2E\x20\xC2\xAB\xD4\xBF\xD5\xB8\xD5\xBF\xD5\xA8\x20\xD5\xB4\xD5\xA5\xD6\x80\xD5\xB8\xD5\xB6\xD6\x81\xD5\xAB\xD6\x81\x20\xD5\xA7\x3A\xC2\xBB", "Armenian"), + new Message("\xD4\xB3\xD5\xA1\xD5\xBC\xD5\xA8\xD5\x9D\x20\xD5\xA3\xD5\xA1\xD6\x80\xD5\xB6\xD5\xA1\xD5\xB6\x2C\x20\xD5\xB1\xD5\xAB\xD6\x82\xD5\xB6\xD5\xA8\xD5\x9D\x20\xD5\xB1\xD5\xB4\xD5\xBC\xD5\xA1\xD5\xB6", "Armenian"), + new Message("\x4A\x65\xC5\xBC\x75\x20\x6B\x6C\xC4\x85\x74\x77\x2C\x20\x73\x70\xC5\x82\xC3\xB3\x64\xC5\xBA\x20\x46\x69\x6E\x6F\x6D\x20\x63\x7A\xC4\x99\xC5\x9B\xC4\x87\x20\x67\x72\x79\x20\x68\x61\xC5\x84\x62\x21", "Polish"), + new Message("\x44\x6F\x62\x72\x79\x6D\x69\x20\x63\x68\xC4\x99\x63\x69\x61\x6D\x69\x20\x6A\x65\x73\x74\x20\x70\x69\x65\x6B\xC5\x82\x6F\x20\x77\x79\x62\x72\x75\x6B\x6F\x77\x61\x6E\x65\x2E", "Polish"), + new Message("\xC3\x8E\xC8\x9B\x69\x20\x6D\x75\x6C\xC8\x9B\x75\x6D\x65\x73\x63\x20\x63\xC4\x83\x20\x61\x69\x20\x61\x6C\x65\x73\x20\x72\x61\x79\x6C\x69\x62\x2E\x0A\xC8\x98\x69\x20\x73\x70\x65\x72\x20\x73\xC4\x83\x20\x61\x69\x20\x6F\x20\x7A\x69\x20\x62\x75\x6E\xC4\x83\x21", "Romanian"), + new Message("\xD0\xAD\xD1\x85\x2C\x20\xD1\x87\xD1\x83\xD0\xB6\xD0\xB0\xD0\xBA\x2C\x20\xD0\xBE\xD0\xB1\xD1\x89\xD0\xB8\xD0\xB9\x20\xD1\x81\xD1\x8A\xD1\x91\xD0\xBC\x20\xD1\x86\xD0\xB5\xD0\xBD\x20\xD1\x88\xD0\xBB\xD1\x8F\xD0\xBF\x20\x28\xD1\x8E\xD1\x84\xD1\x82\xD1\x8C\x29\x20\xD0\xB2\xD0\xB4\xD1\x80\xD1\x8B\xD0\xB7\xD0\xB3\x21", "Russian"), + new Message("\xD0\xAF\x20\xD0\xBB\xD1\x8E\xD0\xB1\xD0\xBB\xD1\x8E\x20\x72\x61\x79\x6C\x69\x62\x21", "Russian"), + new Message("\xD0\x9C\xD0\xBE\xD0\xBB\xD1\x87\xD0\xB8\x2C\x20\xD1\x81\xD0\xBA\xD1\x80\xD1\x8B\xD0\xB2\xD0\xB0\xD0\xB9\xD1\x81\xD1\x8F\x20\xD0\xB8\x20\xD1\x82\xD0\xB0\xD0\xB8\x0A\xD0\x98\x20\xD1\x87\xD1\x83\xD0\xB2\xD1\x81\xD1\x82\xD0\xB2\xD0\xB0\x20\xD0\xB8\x20\xD0\xBC\xD0\xB5\xD1\x87\xD1\x82\xD1\x8B\x20\xD1\x81\xD0\xB2\xD0\xBE\xD0\xB8\x20\xE2\x80\x93\x0A\xD0\x9F\xD1\x83\xD1\x81\xD0\xBA\xD0\xB0\xD0\xB9\x20\xD0\xB2\x20\xD0\xB4\xD1\x83\xD1\x88\xD0\xB5\xD0\xB2\xD0\xBD\xD0\xBE\xD0\xB9\x20\xD0\xB3\xD0\xBB\xD1\x83\xD0\xB1\xD0\xB8\xD0\xBD\xD0\xB5\x0A\xD0\x98\x20\xD0\xB2\xD1\x81\xD1\x85\xD0\xBE\xD0\xB4\xD1\x8F\xD1\x82\x20\xD0\xB8\x20\xD0\xB7\xD0\xB0\xD0\xB9\xD0\xB4\xD1\x83\xD1\x82\x20\xD0\xBE\xD0\xBD\xD0\xB5\x0A\xD0\x9A\xD0\xB0\xD0\xBA\x20\xD0\xB7\xD0\xB2\xD0\xB5\xD0\xB7\xD0\xB4\xD1\x8B\x20\xD1\x8F\xD1\x81\xD0\xBD\xD1\x8B\xD0\xB5\x20\xD0\xB2\x20\xD0\xBD\xD0\xBE\xD1\x87\xD0\xB8\x2D\x0A\xD0\x9B\xD1\x8E\xD0\xB1\xD1\x83\xD0\xB9\xD1\x81\xD1\x8F\x20\xD0\xB8\xD0\xBC\xD0\xB8\x20\xE2\x80\x93\x20\xD0\xB8\x20\xD0\xBC\xD0\xBE\xD0\xBB\xD1\x87\xD0\xB8\x2E", "Russian"), + new Message("\x56\x6F\x69\x78\x20\x61\x6D\x62\x69\x67\x75\xC3\xAB\x20\x64\xE2\x80\x99\x75\x6E\x20\x63\xC5\x93\x75\x72\x20\x71\x75\x69\x20\x61\x75\x20\x7A\xC3\xA9\x70\x68\x79\x72\x20\x70\x72\xC3\xA9\x66\xC3\xA8\x72\x65\x20\x6C\x65\x73\x20\x6A\x61\x74\x74\x65\x73\x20\x64\x65\x20\x6B\x69\x77\x69", "French"), + new Message("\x42\x65\x6E\x6A\x61\x6D\xC3\xAD\x6E\x20\x70\x69\x64\x69\xC3\xB3\x20\x75\x6E\x61\x20\x62\x65\x62\x69\x64\x61\x20\x64\x65\x20\x6B\x69\x77\x69\x20\x79\x20\x66\x72\x65\x73\x61\x3B\x20\x4E\x6F\xC3\xA9\x2C\x20\x73\x69\x6E\x20\x76\x65\x72\x67\xC3\xBC\x65\x6E\x7A\x61\x2C\x20\x6C\x61\x20\x6D\xC3\xA1\x73\x20\x65\x78\x71\x75\x69\x73\x69\x74\x61\x20\x63\x68\x61\x6D\x70\x61\xC3\xB1\x61\x20\x64\x65\x6C\x20\x6D\x65\x6E\xC3\xBA\x2E", "Spanish"), + new Message("\xCE\xA4\xCE\xB1\xCF\x87\xCE\xAF\xCF\x83\xCF\x84\xCE\xB7\x20\xCE\xB1\xCE\xBB\xCF\x8E\xCF\x80\xCE\xB7\xCE\xBE\x20\xCE\xB2\xCE\xB1\xCF\x86\xCE\xAE\xCF\x82\x20\xCF\x88\xCE\xB7\xCE\xBC\xCE\xAD\xCE\xBD\xCE\xB7\x20\xCE\xB3\xCE\xB7\x2C\x20\xCE\xB4\xCF\x81\xCE\xB1\xCF\x83\xCE\xBA\xCE\xB5\xCE\xBB\xCE\xAF\xCE\xB6\xCE\xB5\xCE\xB9\x20\xCF\x85\xCF\x80\xCE\xAD\xCF\x81\x20\xCE\xBD\xCF\x89\xCE\xB8\xCF\x81\xCE\xBF\xCF\x8D\x20\xCE\xBA\xCF\x85\xCE\xBD\xCF\x8C\xCF\x82", "Greek"), + new Message("\xCE\x97\x20\xCE\xBA\xCE\xB1\xCE\xBB\xCF\x8D\xCF\x84\xCE\xB5\xCF\x81\xCE\xB7\x20\xCE\xAC\xCE\xBC\xCF\x85\xCE\xBD\xCE\xB1\x20\xCE\xB5\xCE\xAF\xCE\xBD\xCE\xB1\xCE\xB9\x20\xCE\xB7\x20\xCE\xB5\xCF\x80\xCE\xAF\xCE\xB8\xCE\xB5\xCF\x83\xCE\xB7\x2E", "Greek"), + new Message("\xCE\xA7\xCF\x81\xCF\x8C\xCE\xBD\xCE\xB9\xCE\xB1\x20\xCE\xBA\xCE\xB1\xCE\xB9\x20\xCE\xB6\xCE\xB1\xCE\xBC\xCE\xAC\xCE\xBD\xCE\xB9\xCE\xB1\x21", "Greek"), + new Message("\xCE\xA0\xCF\x8E\xCF\x82\x20\xCF\x84\xCE\xB1\x20\xCF\x80\xCE\xB1\xCF\x82\x20\xCF\x83\xCE\xAE\xCE\xBC\xCE\xB5\xCF\x81\xCE\xB1\x3B", "Greek"), + new Message("\xE6\x88\x91\xE8\x83\xBD\xE5\x90\x9E\xE4\xB8\x8B\xE7\x8E\xBB\xE7\x92\x83\xE8\x80\x8C\xE4\xB8\x8D\xE4\xBC\xA4\xE8\xBA\xAB\xE4\xBD\x93\xE3\x80\x82", "Chinese"), + new Message("\xE4\xBD\xA0\xE5\x90\x83\xE4\xBA\x86\xE5\x90\x97\xEF\xBC\x9F", "Chinese"), + new Message("\xE4\xB8\x8D\xE4\xBD\x9C\xE4\xB8\x8D\xE6\xAD\xBB\xE3\x80\x82", "Chinese"), + new Message("\xE6\x9C\x80\xE8\xBF\x91\xE5\xA5\xBD\xE5\x90\x97\xEF\xBC\x9F", "Chinese"), + new Message("\xE5\xA1\x9E\xE7\xBF\x81\xE5\xA4\xB1\xE9\xA9\xAC\xEF\xBC\x8C\xE7\x84\x89\xE7\x9F\xA5\xE9\x9D\x9E\xE7\xA6\x8F\xE3\x80\x82", "Chinese"), + new Message("\xE5\x8D\x83\xE5\x86\x9B\xE6\x98\x93\xE5\xBE\x97\x2C\x20\xE4\xB8\x80\xE5\xB0\x86\xE9\x9A\xBE\xE6\xB1\x82", "Chinese"), + new Message("\xE4\xB8\x87\xE4\xBA\x8B\xE5\xBC\x80\xE5\xA4\xB4\xE9\x9A\xBE\xE3\x80\x82", "Chinese"), + new Message("\xE9\xA3\x8E\xE6\x97\xA0\xE5\xB8\xB8\xE9\xA1\xBA\xEF\xBC\x8C\xE5\x85\xB5\xE6\x97\xA0\xE5\xB8\xB8\xE8\x83\x9C\xE3\x80\x82", "Chinese"), + new Message("\xE6\xB4\xBB\xE5\x88\xB0\xE8\x80\x81\xEF\xBC\x8C\xE5\xAD\xA6\xE5\x88\xB0\xE8\x80\x81\xE3\x80\x82", "Chinese"), + new Message("\xE4\xB8\x80\xE8\xA8\x80\xE6\x97\xA2\xE5\x87\xBA\xEF\xBC\x8C\xE9\xA9\xB7\xE9\xA9\xAC\xE9\x9A\xBE\xE8\xBF\xBD\xE3\x80\x82", "Chinese"), + new Message("\xE8\xB7\xAF\xE9\x81\xA5\xE7\x9F\xA5\xE9\xA9\xAC\xE5\x8A\x9B\xEF\xBC\x8C\xE6\x97\xA5\xE4\xB9\x85\xE8\xA7\x81\xE4\xBA\xBA\xE5\xBF\x83", "Chinese"), + new Message("\xE6\x9C\x89\xE7\x90\x86\xE8\xB5\xB0\xE9\x81\x8D\xE5\xA4\xA9\xE4\xB8\x8B\xEF\xBC\x8C\xE6\x97\xA0\xE7\x90\x86\xE5\xAF\xB8\xE6\xAD\xA5\xE9\x9A\xBE\xE8\xA1\x8C\xE3\x80\x82", "Chinese"), + new Message("\xE7\x8C\xBF\xE3\x82\x82\xE6\x9C\xA8\xE3\x81\x8B\xE3\x82\x89\xE8\x90\xBD\xE3\x81\xA1\xE3\x82\x8B", "Japanese"), + new Message("\xE4\xBA\x80\xE3\x81\xAE\xE7\x94\xB2\xE3\x82\x88\xE3\x82\x8A\xE5\xB9\xB4\xE3\x81\xAE\xE5\x8A\x9F", "Japanese"), + new Message("\xE3\x81\x86\xE3\x82\x89\xE3\x82\x84\xE3\x81\xBE\xE3\x81\x97\x20\x20\xE6\x80\x9D\xE3\x81\xB2\xE5\x88\x87\xE3\x82\x8B\xE6\x99\x82\x20\x20\xE7\x8C\xAB\xE3\x81\xAE\xE6\x81\x8B", "Japanese"), + new Message("\xE8\x99\x8E\xE7\xA9\xB4\xE3\x81\xAB\xE5\x85\xA5\xE3\x82\x89\xE3\x81\x9A\xE3\x82\x93\xE3\x81\xB0\xE8\x99\x8E\xE5\xAD\x90\xE3\x82\x92\xE5\xBE\x97\xE3\x81\x9A\xE3\x80\x82", "Japanese"), + new Message("\xE4\xBA\x8C\xE5\x85\x8E\xE3\x82\x92\xE8\xBF\xBD\xE3\x81\x86\xE8\x80\x85\xE3\x81\xAF\xE4\xB8\x80\xE5\x85\x8E\xE3\x82\x92\xE3\x82\x82\xE5\xBE\x97\xE3\x81\x9A\xE3\x80\x82", "Japanese"), + new Message("\xE9\xA6\xAC\xE9\xB9\xBF\xE3\x81\xAF\xE6\xAD\xBB\xE3\x81\xAA\xE3\x81\xAA\xE3\x81\x8D\xE3\x82\x83\xE6\xB2\xBB\xE3\x82\x89\xE3\x81\xAA\xE3\x81\x84\xE3\x80\x82", "Japanese"), + new Message("\xE6\x9E\xAF\xE9\x87\x8E\xE8\xB7\xAF\xE3\x81\xAB\xE3\x80\x80\xE5\xBD\xB1\xE3\x81\x8B\xE3\x81\x95\xE3\x81\xAA\xE3\x82\x8A\xE3\x81\xA6\xE3\x80\x80\xE3\x82\x8F\xE3\x81\x8B\xE3\x82\x8C\xE3\x81\x91\xE3\x82\x8A", "Japanese"), + new Message("\xE7\xB9\xB0\xE3\x82\x8A\xE8\xBF\x94\xE3\x81\x97\xE9\xBA\xA6\xE3\x81\xAE\xE7\x95\x9D\xE7\xB8\xAB\xE3\x81\xB5\xE8\x83\xA1\xE8\x9D\xB6\xE5\x93\x89", "Japanese"), + new Message("\xEC\x95\x84\xEB\x93\x9D\xED\x95\x9C\x20\xEB\xB0\x94\xEB\x8B\xA4\x20\xEC\x9C\x84\xEC\x97\x90\x20\xEA\xB0\x88\xEB\xA7\xA4\xEA\xB8\xB0\x20\xEB\x91\x90\xEC\x97\x87\x20\xEB\x82\xA0\xEC\x95\x84\x20\xEB\x8F\x88\xEB\x8B\xA4\x2E\x0A\xEB\x84\x88\xED\x9B\x8C\xEB\x84\x88\xED\x9B\x8C\x20\xEC\x8B\x9C\xEB\xA5\xBC\x20\xEC\x93\xB4\xEB\x8B\xA4\x2E\x20\xEB\xAA\xA8\xEB\xA5\xB4\xEB\x8A\x94\x20\xEB\x82\x98\xEB\x9D\xBC\x20\xEA\xB8\x80\xEC\x9E\x90\xEB\x8B\xA4\x2E\x0A\xEB\x84\x90\xEB\x94\xB0\xEB\x9E\x80\x20\xED\x95\x98\xEB\x8A\x98\x20\xEB\xB3\xB5\xED\x8C\x90\xEC\x97\x90\x20\xEB\x82\x98\xEB\x8F\x84\x20\xEA\xB0\x99\xEC\x9D\xB4\x20\xEC\x8B\x9C\xEB\xA5\xBC\x20\xEC\x93\xB4\xEB\x8B\xA4\x2E", "Korean"), + new Message("\xEC\xA0\x9C\x20\xEB\x88\x88\xEC\x97\x90\x20\xEC\x95\x88\xEA\xB2\xBD\xEC\x9D\xB4\xEB\x8B\xA4", "Korean"), + new Message("\xEA\xBF\xA9\x20\xEB\xA8\xB9\xEA\xB3\xA0\x20\xEC\x95\x8C\x20\xEB\xA8\xB9\xEB\x8A\x94\xEB\x8B\xA4", "Korean"), + new Message("\xEB\xA1\x9C\xEB\xA7\x88\xEB\x8A\x94\x20\xED\x95\x98\xEB\xA3\xA8\xEC\x95\x84\xEC\xB9\xA8\xEC\x97\x90\x20\xEC\x9D\xB4\xEB\xA3\xA8\xEC\x96\xB4\xEC\xA7\x84\x20\xEA\xB2\x83\xEC\x9D\xB4\x20\xEC\x95\x84\xEB\x8B\x88\xEB\x8B\xA4", "Korean"), + new Message("\xEA\xB3\xA0\xEC\x83\x9D\x20\xEB\x81\x9D\xEC\x97\x90\x20\xEB\x82\x99\xEC\x9D\xB4\x20\xEC\x98\xA8\xEB\x8B\xA4", "Korean"), + new Message("\xEA\xB0\x9C\xEC\xB2\x9C\xEC\x97\x90\xEC\x84\x9C\x20\xEC\x9A\xA9\x20\xEB\x82\x9C\xEB\x8B\xA4", "Korean"), + new Message("\xEC\x95\x88\xEB\x85\x95\xED\x95\x98\xEC\x84\xB8\xEC\x9A\x94\x3F", "Korean"), + new Message("\xEB\xA7\x8C\xEB\x82\x98\xEC\x84\x9C\x20\xEB\xB0\x98\xEA\xB0\x91\xEC\x8A\xB5\xEB\x8B\x88\xEB\x8B\xA4", "Korean"), + new Message("\xED\x95\x9C\xEA\xB5\xAD\xEB\xA7\x90\x20\xED\x95\x98\xEC\x8B\xA4\x20\xEC\xA4\x84\x20\xEC\x95\x84\xEC\x84\xB8\xEC\x9A\x94\x3F", "Korean"), }; - //-------------------------------------------------------------------------------------- - // Module functions declaration - //-------------------------------------------------------------------------------------- - static void RandomizeEmoji(void); // Fills the emoji array with random emojis - - //-------------------------------------------------------------------------------------- - // Global variables - //-------------------------------------------------------------------------------------- - // Arrays that holds the random emojis - struct emoji + public static int Main() { - int index; // Index inside `emojiCodepoints` - int message; // Message index - Color color; // Emoji color - } - emoji[EMOJI_PER_WIDTH * EMOJI_PER_HEIGHT] = { 0 }; + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; -static int hovered = -1, selected = -1; + SetConfigFlags(ConfigFlags.Msaa4xHint | ConfigFlags.VSyncHint); + InitWindow(screenWidth, screenHeight, "raylib [text] example - unicode emojis"); -int main(int argc, char** argv) -{ - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + // Load the font resources + // NOTE: fontAsian is for asian languages, + // fontEmoji is the emojis and fontDefault is used for everything else + Font fontDefault = LoadFont("resources/fonts/dejavu.fnt"); // Requires "resources/fonts/dejavu.png" + Font fontAsian = LoadFont("resources/fonts/noto_cjk.fnt"); // Requires "resources/fonts/noto_cjk.png" + Font fontEmoji = LoadFont("resources/fonts/symbola.fnt"); // Requires "resources/fonts/symbola.png" - SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT); - InitWindow(screenWidth, screenHeight, "raylib [text] example - unicode"); + Vector2 hoveredPos = new(0.0f, 0.0f); + Vector2 selectedPos = new(0.0f, 0.0f); - // Load the font resources - // NOTE: fontAsian is for asian languages, - // fontEmoji is the emojis and fontDefault is used for everything else - Font fontDefault = LoadFont("resources/fonts/dejavu.fnt"); - Font fontAsian = LoadFont("resources/fonts/notoCJK.fnt"); - Font fontEmoji = LoadFont("resources/fonts/emoji.fnt"); + // Set a random set of emojis when starting up + RandomizeEmoji(); - Vector2 hoveredPos = new(0.0f, 0.0f); - Vector2 selectedPos = new(0.0f, 0.0f); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- - // Set a random set of emojis when starting up - RandomizeEmoji(); - - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- - - // Main loop - while (!WindowShouldClose()) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - // Add a new set of emojis when SPACE is pressed - if (IsKeyPressed(KEY_SPACE)) RandomizeEmoji(); - - // Set the selected emoji and copy its text to clipboard - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) && (hovered != -1) && (hovered != selected)) + // Main loop + while (!WindowShouldClose()) // Detect window close button or ESC key { - selected = hovered; - selectedPos = hoveredPos; - SetClipboardText(messages[emoji[selected].message].text); + // Update + //---------------------------------------------------------------------------------- + // Add a new set of emojis when SPACE is pressed + if (IsKeyPressed(KeyboardKey.Space)) + { + RandomizeEmoji(); + } + + // Set the selected emoji + if (IsMouseButtonPressed(MouseButton.Left) && (hovered != -1) && (hovered != selected)) + { + selected = hovered; + selectedPos = hoveredPos; + } + + Vector2 mouse = GetMousePosition(); + Vector2 position = new(28.8f, 10.0f); + hovered = -1; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + // Draw random emojis in the background + //------------------------------------------------------------------------------ + for (int i = 0; i < emoji.Length; i++) + { + string txt = GetEmojiAt(emoji[i].Index); + Rectangle emojiRect = new(position.X, position.Y, fontEmoji.BaseSize, fontEmoji.BaseSize); + + if (!CheckCollisionPointRec(mouse, emojiRect)) + { + DrawTextEx(fontEmoji, txt, position, fontEmoji.BaseSize, 1.0f, selected == i ? emoji[i].Color : Fade(Color.LightGray, 0.4f)); + } + else + { + DrawTextEx(fontEmoji, txt, position, fontEmoji.BaseSize, 1.0f, emoji[i].Color); + hovered = i; + hoveredPos = position; + } + + if ((i != 0) && (i % EmojiPerWidth == 0)) + { + position.Y += fontEmoji.BaseSize + 24.25f; + position.X = 28.8f; + } + else + { + position.X += fontEmoji.BaseSize + 28.8f; + } + } + //------------------------------------------------------------------------------ + + // Draw the message when a emoji is selected + //------------------------------------------------------------------------------ + if (selected != -1) + { + int message = emoji[selected].Message; + const int horizontalPadding = 20; + const int verticalPadding = 30; + Font font = fontDefault; + + // Set correct font for asian languages + if ((messages[message].Language == "Chinese") || + (messages[message].Language == "Korean") || + (messages[message].Language == "Japanese")) + { + font = fontAsian; + } + + // Calculate size for the message box (approximate the height and width) + Vector2 sz = MeasureTextEx(font, messages[message].Text, font.BaseSize, 1.0f); + if (sz.X > 300) + { + sz.Y *= sz.X / 300; + sz.X = 300; + } + else if (sz.X < 160) + { + sz.X = 160; + } + + Rectangle msgRect = new(selectedPos.X - 38.8f, selectedPos.Y, 2 * horizontalPadding + sz.X, 2 * verticalPadding + sz.Y); + msgRect.Y -= msgRect.Height; + + // Coordinates for the chat bubble triangle + Vector2 a = new(selectedPos.X, msgRect.Y + msgRect.Height); + Vector2 b = new(a.X + 8, a.Y + 10); + Vector2 c = new(a.X + 10, a.Y); + + // Don't go outside the screen + if (msgRect.X < 10) + { + msgRect.X += 28; + } + + if (msgRect.Y < 10) + { + msgRect.Y = selectedPos.Y + 84; + a.Y = msgRect.Y; + c.Y = a.Y; + b.Y = a.Y - 10; + + // Swap values so we can actually render the triangle :( + Vector2 tmp = a; + a = b; + b = tmp; + } + + if (msgRect.X + msgRect.Width > screenWidth) + { + msgRect.X -= (msgRect.X + msgRect.Width) - screenWidth + 10; + } + + // Draw chat bubble + DrawRectangleRec(msgRect, emoji[selected].Color); + DrawTriangle(a, b, c, emoji[selected].Color); + + // Draw the main text message + Rectangle textRect = new(msgRect.X + (float)horizontalPadding / 2, msgRect.Y + (float)verticalPadding / 2, msgRect.Width - horizontalPadding, msgRect.Height); + DrawTextBoxed(font, messages[message].Text, textRect, font.BaseSize, 1.0f, true, Color.White); + + // Draw the info text below the main message + int size = Encoding.UTF8.GetByteCount(messages[message].Text); + int length = GetCodepointCount(messages[message].Text); + string info = $"{messages[message].Language} {length} characters {size} bytes"; + sz = MeasureTextEx(GetFontDefault(), info, 10, 1.0f); + + DrawText(info, (int)(textRect.X + textRect.Width - sz.X), (int)(msgRect.Y + msgRect.Height - sz.Y - 2), 10, Color.RayWhite); + } + //------------------------------------------------------------------------------ + + // Draw the info text + DrawText("These emojis have something to tell you, click each to find out!", (screenWidth - 650) / 2, screenHeight - 40, 20, Color.Gray); + DrawText("Each emoji is a unicode character from a font, not a texture... Press [SPACEBAR] to refresh", (screenWidth - 484) / 2, screenHeight - 16, 10, Color.Gray); + + EndDrawing(); + //---------------------------------------------------------------------------------- } - Vector2 mouse = GetMousePosition(); - Vector2 pos = new(28.8f, 10.0f); - hovered = -1; - //---------------------------------------------------------------------------------- + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadFont(fontDefault); // Unload font resource + UnloadFont(fontAsian); // Unload font resource + UnloadFont(fontEmoji); // Unload font resource - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- - ClearBackground(RAYWHITE); + return 0; + } - // Draw random emojis in the background - //------------------------------------------------------------------------------ - for (int i = 0; i < SIZEOF(emoji); ++i) + // Fills the emoji array with random emoji (only those emojis present in fontEmoji) + static void RandomizeEmoji() + { + hovered = selected = -1; + int start = GetRandomValue(45, 360); + + for (int i = 0; i < emoji.Length; i++) { - const char* txt = ref[emoji[i].index]; - Rectangle emojiRect = new(pos.X, pos.Y, fontEmoji.BaseSize, fontEmoji.BaseSize); + // 0-179 emoji codepoints (from emoji char array) each 4bytes + null char + emoji[i].Index = GetRandomValue(0, 179) * 5; - if (!CheckCollisionPointRec(mouse, emojiRect)) + // Generate a random color for this emoji + emoji[i].Color = Fade(ColorFromHSV((start * (i + 1)) % 360, 0.6f, 0.85f), 0.8f); + + // Set a random message for this emoji + emoji[i].Message = GetRandomValue(0, messages.Length - 1); + } + } + + // Get the emoji codepoint (4 bytes + null) located at the given byte index inside EmojiCodepoints + static string GetEmojiAt(int index) + { + // Each emoji takes 4 bytes followed by a null separator, codepoints are stored as raw bytes + var sb = new StringBuilder(); + for (int i = index; (i < EmojiCodepoints.Length) && (EmojiCodepoints[i] != '\0'); i++) + { + sb.Append(EmojiCodepoints[i]); + } + return sb.ToString(); + } + + //-------------------------------------------------------------------------------------- + // Module Functions Definition + //-------------------------------------------------------------------------------------- + + // Draw text using font inside rectangle limits + static void DrawTextBoxed(Font font, string text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint) + { + DrawTextBoxedSelectable(font, text, rec, fontSize, spacing, wordWrap, tint, 0, 0, Color.White, Color.White); + } + + // Draw text using font inside rectangle limits with support for text selection + static unsafe void DrawTextBoxedSelectable(Font font, string text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint) + { + int length = text.Length; // Total length in bytes of the text, scanned by codepoints in loop + + float textOffsetY = 0.0f; // Offset between lines (on line break '\n') + float textOffsetX = 0.0f; // Offset X to next character to draw + + float scaleFactor = fontSize / (float)font.BaseSize; // Character rectangle scaling factor + + // Word/character wrapping mechanism variables + const int MeasureState = 0; + const int DrawState = 1; + int state = wordWrap ? MeasureState : DrawState; + + int startLine = -1; // Index where to begin drawing (where a line begins) + int endLine = -1; // Index where to stop drawing (where a line ends) + int lastk = -1; // Holds last value of the character position + + using var textNative = new Utf8Buffer(text); + + for (int i = 0, k = 0; i < length; i++, k++) + { + // Get next codepoint from byte string and glyph index in font + int codepointByteCount = 0; + int codepoint = GetCodepoint(&textNative.AsPointer()[i], &codepointByteCount); + int index = GetGlyphIndex(font, codepoint); + + // 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) { - DrawTextEx(fontEmoji, txt, pos, fontEmoji.BaseSize, 1.0, selected == i ? emoji[i].Color : ColorAlpha(LIGHTGRAY, 0.4f)); + codepointByteCount = 1; + } + + i += (codepointByteCount - 1); + + float glyphWidth = 0; + if (codepoint != '\n') + { + glyphWidth = (font.Glyphs[index].AdvanceX == 0) ? + font.Recs[index].Width * scaleFactor : + font.Glyphs[index].AdvanceX * scaleFactor; + + if (i + 1 < length) + { + glyphWidth = glyphWidth + spacing; + } + } + + // NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container + // We store this info in startLine and endLine, then we change states, draw the text between those two variables + // and change states again and again recursively until the end of the text (or until we get outside of the container) + // When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately + // and begin drawing on the next line before we can get outside the container + if (state == MeasureState) + { + // TODO: There are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more + // Ref: http://jkorpela.fi/chars/spaces.html + if ((codepoint == ' ') || (codepoint == '\t') || (codepoint == '\n')) + { + endLine = i; + } + + if ((textOffsetX + glyphWidth) > rec.Width) + { + endLine = (endLine < 1) ? i : endLine; + if (i == endLine) + { + endLine -= codepointByteCount; + } + if ((startLine + codepointByteCount) == endLine) + { + endLine = (i - codepointByteCount); + } + + state = 1 - state; + } + else if ((i + 1) == length) + { + endLine = i; + state = 1 - state; + } + else if (codepoint == '\n') + { + state = 1 - state; + } + + if (state == DrawState) + { + textOffsetX = 0; + i = startLine; + glyphWidth = 0; + + // Save character position when we switch states + int tmp = lastk; + lastk = k - 1; + k = tmp; + } } else { - DrawTextEx(fontEmoji, txt, pos, fontEmoji.BaseSize, 1.0, emoji[i].Color); - hovered = i; - hoveredPos = pos; + if (codepoint == '\n') + { + if (!wordWrap) + { + textOffsetY += (font.BaseSize + font.BaseSize / 2) * scaleFactor; + textOffsetX = 0; + } + } + else + { + if (!wordWrap && ((textOffsetX + glyphWidth) > rec.Width)) + { + textOffsetY += (font.BaseSize + font.BaseSize / 2) * scaleFactor; + textOffsetX = 0; + } + + // When text overflows rectangle height limit, just stop drawing + if ((textOffsetY + font.BaseSize * scaleFactor) > rec.Height) + { + break; + } + + // Draw selection background + bool isGlyphSelected = false; + if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength))) + { + DrawRectangleRec(new Rectangle(rec.X + textOffsetX - 1, rec.Y + textOffsetY, glyphWidth, (float)font.BaseSize * scaleFactor), selectBackTint); + isGlyphSelected = true; + } + + // Draw current character glyph + if ((codepoint != ' ') && (codepoint != '\t')) + { + DrawTextCodepoint(font, codepoint, new Vector2(rec.X + textOffsetX, rec.Y + textOffsetY), fontSize, isGlyphSelected ? selectTint : tint); + } + } + + if (wordWrap && (i == endLine)) + { + textOffsetY += (font.BaseSize + font.BaseSize / 2) * scaleFactor; + textOffsetX = 0; + startLine = endLine; + endLine = -1; + glyphWidth = 0; + selectStart += lastk - k; + k = lastk; + + state = 1 - state; + } } - if ((i != 0) && (i % EMOJI_PER_WIDTH == 0)) { pos.Y += fontEmoji.BaseSize + 24.25f; pos.X = 28.8f; } - else pos.X += fontEmoji.BaseSize + 28.8f; + textOffsetX += glyphWidth; } - //------------------------------------------------------------------------------ - - // Draw the message when a emoji is selected - //------------------------------------------------------------------------------ - if (selected != -1) - { - const int message = emoji[selected].message; - const int horizontalPadding = 20, verticalPadding = 30; - Font* font = ref; - - // Set correct font for asian languages - if (TextIsEqual(messages[message].language, "Chinese") || - TextIsEqual(messages[message].language, "Korean") || - TextIsEqual(messages[message].language, "Japanese")) font = ref; - - // Calculate size for the message box (approximate the height and width) - Vector2 sz = MeasureTextEx(*font, messages[message].text, font->baseSize, 1.0f); - if (sz.X > 300) { sz.Y *= sz.X / 300; sz.X = 300; } - else if (sz.X < 160) sz.X = 160; - - Rectangle msgRect = new(selectedPos.X - 38.8f, selectedPos.Y, 2 * horizontalPadding + sz.X, 2 * verticalPadding + sz.Y); - msgRect.Y -= msgRect.Height; - - // Coordinates for the chat bubble triangle - Vector2 a = new(selectedPos.X, msgRect.Y + msgRect.Height); - Vector2 b = new(a.X + 8, a.Y + 10); - Vector2 c = new(a.X + 10, a.Y); - - // Don't go outside the screen - if (msgRect.X < 10) msgRect.X += 28; - if (msgRect.Y < 10) - { - msgRect.Y = selectedPos.Y + 84; - a.Y = msgRect.Y; - c.Y = a.Y; - b.Y = a.Y - 10; - - // Swap values so we can actually render the triangle :( - Vector2 tmp = a; - a = b; - b = tmp; - } - if (msgRect.X + msgRect.Width > screenWidth) msgRect.X -= (msgRect.X + msgRect.Width) - screenWidth + 10; - - // Draw chat bubble - DrawRectangleRec(msgRect, emoji[selected].Color); - DrawTriangle(a, b, c, emoji[selected].Color); - - // Draw the main text message - Rectangle textRect = new(msgRect.X + horizontalPadding / 2, msgRect.Y + verticalPadding / 2, msgRect.Width - horizontalPadding, msgRect.Height); - DrawTextRec(*font, messages[message].text, textRect, font->baseSize, 1.0f, true, WHITE); - - // Draw the info text below the main message - int size = strlen(messages[message].text); - int len = GetCodepointsCount(messages[message].text); - const char* info = TextFormat("%s %u characters %i bytes", messages[message].language, len, size); - sz = MeasureTextEx(GetFontDefault(), info, 10, 1.0f); - Vector2 pos = new(textRect.X + textRect.Width - sz.X, msgRect.Y + msgRect.Height - sz.Y - 2); - DrawText(info, pos.X, pos.Y, 10, RAYWHITE); - } - //------------------------------------------------------------------------------ - - // Draw the info text - DrawText("These emojis have something to tell you, click each to find out!", (screenWidth - 650) / 2, screenHeight - 40, 20, GRAY); - DrawText("Each emoji is a unicode character from a font, not a texture... Press [SPACEBAR] to refresh", (screenWidth - 484) / 2, screenHeight - 16, 10, GRAY); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - UnloadFont(fontDefault); // Unload font resource - UnloadFont(fontAsian); // Unload font resource - UnloadFont(fontEmoji); // Unload font resource - - CloseWindow(); // Close window and OpenGL context - //-------------------------------------------------------------------------------------- - - return 0; -} - -// Fills the emoji array with random emoji (only those emojis present in fontEmoji) -static void RandomizeEmoji(void) -{ - hovered = selected = -1; - int start = GetRandomValue(45, 360); - - for (int i = 0; i < SIZEOF(emoji); ++i) - { - // 0-179 emoji codepoints (from emoji char array) each 4bytes + null char - emoji[i].index = GetRandomValue(0, 179) * 5; - - // Generate a random color for this emoji - Vector3 hsv = new((start * (i + 1)) % 360, 0.6f, 0.85f); - emoji[i].Color = ColorAlpha(ColorFromHSV(hsv), 0.8f); - - // Set a random message for this emoji - emoji[i].message = GetRandomValue(0, SIZEOF(messages) - 1); } } -} - -} diff --git a/Examples/Text/WritingAnim.cs b/Examples/Text/WritingAnim.cs index 94ed451..a5c9382 100644 --- a/Examples/Text/WritingAnim.cs +++ b/Examples/Text/WritingAnim.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [text] example - Text Writing Animation +* raylib [text] example - writing anim * -* This example has been created using raylib 1.4 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2016 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.4, last time updated with raylib 1.4 +* +* 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) 2016-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -13,60 +17,85 @@ using static Raylib_cs.Raylib; namespace Examples.Text; -public class WritingAnim +public partial class WritingAnim : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Text / Writing Animation"; + + public string Title => "raylib [text] example - writing anim"; + + private string message; + private int framesCounter; + + public void Init() + { + message = "This sample illustrates a text writing\nanimation effect! Check it out! ;)"; + + framesCounter = 0; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyDown(KeyboardKey.Space)) + { + framesCounter += 8; + } + else + { + framesCounter += 1; + } + + if (IsKeyPressed(KeyboardKey.Enter)) + { + framesCounter = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText(message.SubText(0, framesCounter / 10), 210, 160, 20, Color.Maroon); + + DrawText("PRESS [ENTER] to RESTART!", 240, 260, 20, Color.LightGray); + DrawText("HOLD [SPACE] to SPEED UP!", 239, 300, 20, Color.LightGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [text] example - writing anim"); - InitWindow(screenWidth, screenHeight, "raylib [text] example - text writing anim"); - - string message = "This sample illustrates a text writing\nanimation effect! Check it out! ;)"; - int framesCounter = 0; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new WritingAnim(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyDown(KeyboardKey.Space)) - { - framesCounter += 8; - } - else - { - framesCounter += 1; - } - - if (IsKeyPressed(KeyboardKey.Enter)) - { - framesCounter = 0; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText(message.SubText(0, framesCounter / 10), 210, 160, 20, Color.Maroon); - - DrawText("PRESS [ENTER] to RESTART!", 240, 260, 20, Color.LightGray); - DrawText("PRESS [SPACE] to SPEED UP!", 239, 300, 20, Color.LightGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/BackgroundScrolling.cs b/Examples/Textures/BackgroundScrolling.cs index 3aa27d4..1f91529 100644 --- a/Examples/Textures/BackgroundScrolling.cs +++ b/Examples/Textures/BackgroundScrolling.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [textures] example - Background scrolling +* raylib [textures] example - background scrolling * -* This example has been created using raylib 2.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 * -* Copyright (c) 2019 Ramon Santamaria (@raysan5) +* Example originally created with raylib 2.0, last time updated with raylib 2.5 +* +* 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) 2019-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,101 +18,126 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class BackgroundScrolling +public partial class BackgroundScrolling : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Textures / Background Scrolling"; + + public string Title => "raylib [textures] example - background scrolling"; + + private Texture2D background; + private Texture2D midground; + private Texture2D foreground; + + private float scrollingBack; + private float scrollingMid; + private float scrollingFore; + + public void Init() + { + // NOTE: Be careful, background width must be equal or bigger than screen width + // if not, texture should be draw more than two times for scrolling effect + background = LoadTexture("resources/cyberpunk_street_background.png"); + midground = LoadTexture("resources/cyberpunk_street_midground.png"); + foreground = LoadTexture("resources/cyberpunk_street_foreground.png"); + + scrollingBack = 0.0f; + scrollingMid = 0.0f; + scrollingFore = 0.0f; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + scrollingBack -= 0.1f; + scrollingMid -= 0.5f; + scrollingFore -= 1.0f; + + // NOTE: Texture is scaled twice its size, so it sould be considered on scrolling + if (scrollingBack <= -background.Width * 2) + { + scrollingBack = 0; + } + if (scrollingMid <= -midground.Width * 2) + { + scrollingMid = 0; + } + if (scrollingFore <= -foreground.Width * 2) + { + scrollingFore = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(GetColor(0x052c46ff)); + + // Draw background image twice + // NOTE: Texture is scaled twice its size + DrawTextureEx(background, new Vector2(scrollingBack, 20), 0.0f, 2.0f, Color.White); + DrawTextureEx( + background, + new Vector2(background.Width * 2 + scrollingBack, 20), + 0.0f, + 2.0f, + Color.White + ); + + // Draw midground image twice + DrawTextureEx(midground, new Vector2(scrollingMid, 20), 0.0f, 2.0f, Color.White); + DrawTextureEx(midground, new Vector2(midground.Width * 2 + scrollingMid, 20), 0.0f, 2.0f, Color.White); + + // Draw foreground image twice + DrawTextureEx(foreground, new Vector2(scrollingFore, 70), 0.0f, 2.0f, Color.White); + DrawTextureEx( + foreground, + new Vector2(foreground.Width * 2 + scrollingFore, 70), + 0.0f, + 2.0f, + Color.White + ); + + DrawText("BACKGROUND SCROLLING & PARALLAX", 10, 10, 20, Color.Red); + DrawText("(c) Cyberpunk Street Environment by Luis Zuno (@ansimuz)", screenWidth - 330, screenHeight - 20, 10, Color.RayWhite); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(background); // Unload background texture + UnloadTexture(midground); // Unload midground texture + UnloadTexture(foreground); // Unload foreground texture + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [textures] example - background scrolling"); - // NOTE: Be careful, background width must be equal or bigger than screen width - // if not, texture should be draw more than two times for scrolling effect - Texture2D background = LoadTexture("resources/cyberpunk_street_background.png"); - Texture2D midground = LoadTexture("resources/cyberpunk_street_midground.png"); - Texture2D foreground = LoadTexture("resources/cyberpunk_street_foreground.png"); - - float scrollingBack = 0.0f; - float scrollingMid = 0.0f; - float scrollingFore = 0.0f; - - SetTargetFPS(60); // Set our game to run at 60 frames-per-second + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new BackgroundScrolling(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - scrollingBack -= 0.1f; - scrollingMid -= 0.5f; - scrollingFore -= 1.0f; - - // NOTE: Texture is scaled twice its size, so it sould be considered on scrolling - if (scrollingBack <= -background.Width * 2) - { - scrollingBack = 0; - } - if (scrollingMid <= -midground.Width * 2) - { - scrollingMid = 0; - } - if (scrollingFore <= -foreground.Width * 2) - { - scrollingFore = 0; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(GetColor(0x052c46ff)); - - // Draw background image twice - // NOTE: Texture is scaled twice its size - DrawTextureEx(background, new Vector2(scrollingBack, 20), 0.0f, 2.0f, Color.White); - DrawTextureEx( - background, - new Vector2(background.Width * 2 + scrollingBack, 20), - 0.0f, - 2.0f, - Color.White - ); - - // Draw midground image twice - DrawTextureEx(midground, new Vector2(scrollingMid, 20), 0.0f, 2.0f, Color.White); - DrawTextureEx(midground, new Vector2(midground.Width * 2 + scrollingMid, 20), 0.0f, 2.0f, Color.White); - - // Draw foreground image twice - DrawTextureEx(foreground, new Vector2(scrollingFore, 70), 0.0f, 2.0f, Color.White); - DrawTextureEx( - foreground, - new Vector2(foreground.Width * 2 + scrollingFore, 70), - 0.0f, - 2.0f, - Color.White - ); - - DrawText("BACKGROUND SCROLLING & PARALLAX", 10, 10, 20, Color.Red); - - int x = screenWidth - 330; - int y = screenHeight - 20; - DrawText("(c) Cyberpunk Street Environment by Luis Zuno (@ansimuz)", x, y, 10, Color.RayWhite); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(background); - UnloadTexture(midground); - UnloadTexture(foreground); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/BlendModes.cs b/Examples/Textures/BlendModes.cs index 49a26b9..a8f5665 100644 --- a/Examples/Textures/BlendModes.cs +++ b/Examples/Textures/BlendModes.cs @@ -2,14 +2,18 @@ * * raylib [textures] example - blend modes * +* Example complexity rating: [★☆☆☆] 1/4 +* * NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) * -* This example has been created using raylib 3.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 3.5, last time updated with raylib 3.5 * * Example contributed by Karlo Licudine (@accidentalrebel) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2020 Karlo Licudine (@accidentalrebel) +* 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) 2020-2025 Karlo Licudine (@accidentalrebel) * ********************************************************************************************/ @@ -17,99 +21,128 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class BlendModes +public partial class BlendModes : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + private const int blendCountMax = 4; + + public string Name => "Textures / Blend Modes"; + + public string Title => "raylib [textures] example - blend modes"; + + private Texture2D bgTexture; + private Texture2D fgTexture; + + private BlendMode blendMode; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [textures] example - blend modes"); - // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - Image bgImage = LoadImage("resources/cyberpunk_street_background.png"); - Texture2D bgTexture = LoadTextureFromImage(bgImage); + var bgImage = LoadImage("resources/cyberpunk_street_background.png"); + bgTexture = LoadTextureFromImage(bgImage); - Image fgImage = LoadImage("resources/cyberpunk_street_foreground.png"); - Texture2D fgTexture = LoadTextureFromImage(fgImage); + var fgImage = LoadImage("resources/cyberpunk_street_foreground.png"); + fgTexture = LoadTextureFromImage(fgImage); // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM UnloadImage(bgImage); UnloadImage(fgImage); - const int blendCountMax = 4; - BlendMode blendMode = 0; + blendMode = 0; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KeyboardKey.Space)) + { + if ((int)blendMode >= (blendCountMax - 1)) + { + blendMode = 0; + } + else + { + blendMode++; + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + var bgX = screenWidth / 2 - bgTexture.Width / 2; + var bgY = screenHeight / 2 - bgTexture.Height / 2; + DrawTexture(bgTexture, bgX, bgY, Color.White); + + // Apply the blend mode and then draw the foreground texture + BeginBlendMode(blendMode); + var fgX = screenWidth / 2 - fgTexture.Width / 2; + var fgY = screenHeight / 2 - fgTexture.Height / 2; + DrawTexture(fgTexture, fgX, fgY, Color.White); + EndBlendMode(); + + // Draw the texts + DrawText("Press SPACE to change blend modes.", 310, 350, 10, Color.Gray); + + switch (blendMode) + { + case BlendMode.Alpha: + DrawText("Current: BLEND_ALPHA", (screenWidth / 2) - 60, 370, 10, Color.Gray); + break; + case BlendMode.Additive: + DrawText("Current: BLEND_ADDITIVE", (screenWidth / 2) - 60, 370, 10, Color.Gray); + break; + case BlendMode.Multiplied: + DrawText("Current: BLEND_MULTIPLIED", (screenWidth / 2) - 60, 370, 10, Color.Gray); + break; + case BlendMode.AddColors: + DrawText("Current: BLEND_ADD_COLORS", (screenWidth / 2) - 60, 370, 10, Color.Gray); + break; + default: + break; + } + + var text = "(c) Cyberpunk Street Environment by Luis Zuno (@ansimuz)"; + DrawText(text, screenWidth - 330, screenHeight - 20, 10, Color.Gray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(fgTexture); // Unload foreground texture + UnloadTexture(bgTexture); // Unload background texture + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [textures] example - blend modes"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //--------------------------------------------------------------------------------------- + + var game = new BlendModes(); + game.Init(); // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyPressed(KeyboardKey.Space)) - { - if ((int)blendMode >= (blendCountMax - 1)) - { - blendMode = 0; - } - else - { - blendMode++; - } - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - int bgX = screenWidth / 2 - bgTexture.Width / 2; - int bgY = screenHeight / 2 - bgTexture.Height / 2; - DrawTexture(bgTexture, bgX, bgY, Color.White); - - // Apply the blend mode and then draw the foreground texture - BeginBlendMode(blendMode); - int fgX = screenWidth / 2 - fgTexture.Width / 2; - int fgY = screenHeight / 2 - fgTexture.Height / 2; - DrawTexture(fgTexture, fgX, fgY, Color.White); - EndBlendMode(); - - // Draw the texts - DrawText("Press SPACE to change blend modes.", 310, 350, 10, Color.Gray); - - switch (blendMode) - { - case BlendMode.Alpha: - DrawText("Current: BLEND_ALPHA", (screenWidth / 2) - 60, 370, 10, Color.Gray); - break; - case BlendMode.Additive: - DrawText("Current: BLEND_ADDITIVE", (screenWidth / 2) - 60, 370, 10, Color.Gray); - break; - case BlendMode.Multiplied: - DrawText("Current: BLEND_MULTIPLIED", (screenWidth / 2) - 60, 370, 10, Color.Gray); - break; - case BlendMode.AddColors: - DrawText("Current: BLEND_ADD_COLORS", (screenWidth / 2) - 60, 370, 10, Color.Gray); - break; - default: - break; - } - - string text = "(c) Cyberpunk Street Environment by Luis Zuno (@ansimuz)"; - DrawText(text, screenWidth - 330, screenHeight - 20, 10, Color.Gray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(fgTexture); - UnloadTexture(bgTexture); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/Bunnymark.cs b/Examples/Textures/Bunnymark.cs index b610692..22304c5 100644 --- a/Examples/Textures/Bunnymark.cs +++ b/Examples/Textures/Bunnymark.cs @@ -1,12 +1,15 @@ /******************************************************************************************* * -* raylib [textures] example - Bunnymark +* raylib [textures] example - bunnymark * -* This example has been created using raylib 1.6 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5), 2024 Moritz Voss (@thygrrr) +* Example originally created with raylib 1.6, last time updated with raylib 2.5 * +* 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) 2014-2025 Ramon Santamaria (@raysan5), 2024 Moritz Voss (@thygrrr) * ********************************************************************************************/ @@ -16,8 +19,11 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public static class Bunnymark +public partial class Bunnymark : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + // limits private const int MaxBunnies = 500_000; private const int BunnyIncrement = 500; @@ -28,6 +34,10 @@ public static class Bunnymark // This is the maximum amount of elements (quads) per batch private const int MAX_BATCH_ELEMENTS = Rlgl.DEFAULT_BATCH_BUFFER_ELEMENTS; + public string Name => "Textures / Bunnymark"; + + public string Title => "raylib [textures] example - bunnymark"; + private record struct Bunny() { public Vector2 Position { get; set; } = GetMousePosition(); @@ -46,99 +56,123 @@ public static class Bunnymark GetRandomValue(100, 240), 255); } + private Texture2D texBunny; + private Vector2 halfSize; + private Bunny[] bunnies; + private int bunniesCount; + + public void Init() + { + // Load bunny texture + texBunny = LoadTexture("resources/wabbit_alpha.png"); + halfSize = new Vector2(texBunny.Width, texBunny.Height) / 2; + + // Initialize bunnies storage + bunnies = new Bunny[MaxBunnies]; + bunniesCount = 0; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + Span bunnies = this.bunnies; + + if (IsMouseButtonDown(MouseButton.Left) && bunniesCount < MaxBunnies) + { + // Add a range of new bunnies + foreach (ref var bunny in bunnies[bunniesCount..(bunniesCount + BunnyIncrement)]) + { + bunny = new(); + } + bunniesCount += BunnyIncrement; + } + else if (IsMouseButtonDown(MouseButton.Right)) + { + // Remove the oldest bunnies, shifting them back in the span + if (bunniesCount > BunnyDecrement) + { + bunnies[BunnyDecrement..bunniesCount].CopyTo(bunnies); + } + bunniesCount = Math.Max(0, bunniesCount - BunnyDecrement); + } + + // Update bunnies + foreach (ref var bunny in bunnies[..bunniesCount]) + { + // Integrate position + bunny.Position += bunny.Speed; + + // Bounce bunnies off the screen borders + bunny.Speed *= (bunny.Position + halfSize) switch + { + { X: < 0 or > screenWidth, Y: < 40 or > screenHeight } => new(-1, -1), + { X: < 0 or > screenWidth } => new(-1, 1), + { Y: < 40 or > screenHeight } => new(1, -1), + _ => Vector2.One, + }; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + foreach (var bunny in bunnies[..bunniesCount]) + { + // NOTE: When internal batch buffer limit is reached (MAX_BATCH_ELEMENTS), + // a draw call is launched and buffer starts being filled again; + // before issuing a draw call, updated vertex data from internal CPU buffer is send to GPU... + // Process of sending data is costly and it could happen that GPU data has not been completely + // processed for drawing while new data is tried to be sent (updating current in-use buffers) + // it could generates a stall and consequently a frame drop, limiting the number of drawn bunnies + DrawTexture(texBunny, (int)bunny.Position.X, (int)bunny.Position.Y, bunny.Color); + } + + DrawRectangle(0, 0, screenWidth, 40, Color.Black); + DrawText($"bunnies: {bunniesCount}", 120, 10, 20, Color.Green); + DrawText($"batched draw calls: {1 + bunniesCount / MAX_BATCH_ELEMENTS}", 320, 10, 20, Color.Maroon); + DrawText("Left Mouse: Add Bunnies!!! :D", 10, 400, 20, Color.LightGray); + DrawText("Right Mouse: Remove Bunnies", 10, 420, 20, Color.LightGray); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texBunny); +#if BROWSER + bunnies = null; + bunniesCount = 0; +#endif + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [textures] example - bunnymark"); - // Load bunny texture - Texture2D texBunny = LoadTexture("resources/wabbit_alpha.png"); - Vector2 halfSize = new Vector2(texBunny.Width, texBunny.Height) / 2; - - // Initialize bunnies storage - Span bunnies = new Bunny[MaxBunnies]; - int bunniesCount = 0; - SetTargetFPS(TARGET_FPS); //-------------------------------------------------------------------------------------- + var game = new Bunnymark(); + game.Init(); + // Main game loop while (!WindowShouldClose()) { - // Update - //---------------------------------------------------------------------------------- - if (IsMouseButtonDown(MouseButton.Left) && bunniesCount < MaxBunnies) - { - // Add a range of new bunnies - foreach (ref var bunny in bunnies[bunniesCount..(bunniesCount + BunnyIncrement)]) - { - bunny = new(); - } - bunniesCount += BunnyIncrement; - } - else if (IsMouseButtonDown(MouseButton.Right)) - { - // Remove the oldest bunnies, shifting them back in the span - if (bunniesCount > BunnyDecrement) - { - bunnies[BunnyDecrement..bunniesCount].CopyTo(bunnies); - } - bunniesCount = Math.Max(0, bunniesCount - BunnyDecrement); - } - - // Update bunnies - foreach (ref var bunny in bunnies[..bunniesCount]) - { - // Integrate position - bunny.Position += bunny.Speed; - - // Bounce bunnies off the screen borders - bunny.Speed *= (bunny.Position + halfSize) switch - { - { X: < 0 or > screenWidth, Y: < 40 or > screenHeight } => new(-1, -1), - { X: < 0 or > screenWidth } => new(-1, 1), - { Y: < 40 or > screenHeight } => new(1, -1), - _ => Vector2.One, - }; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - foreach (var bunny in bunnies[..bunniesCount]) - { - // NOTE: When internal batch buffer limit is reached (MAX_BATCH_ELEMENTS), - // a draw call is launched and buffer starts being filled again; - // before issuing a draw call, updated vertex data from internal CPU buffer is send to GPU... - // Process of sending data is costly, and it could happen that GPU data has not been completely - // processed for drawing while new data is tried to be sent (updating current in-use buffers) - // it could generate a stall and consequently a frame drop, limiting the number of drawn bunnies - DrawTexture(texBunny, (int)bunny.Position.X, (int)bunny.Position.Y, bunny.Color); - } - - DrawRectangle(0, 0, screenWidth, 40, Color.Black); - DrawText($"bunnies: {bunniesCount}", 120, 10, 20, Color.Green); - DrawText($"batched draw calls: {1 + bunniesCount / MAX_BATCH_ELEMENTS}", 320, 10, 20, Color.Maroon); - DrawText("Left Mouse: Add Bunnies!!! :D", 10, 400, 20, Color.LightGray); - DrawText("Right Mouse: Remove Bunnies", 10, 420, 20, Color.LightGray); - - DrawFPS(10, 10); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texBunny); - CloseWindow(); //-------------------------------------------------------------------------------------- diff --git a/Examples/Textures/DrawTiled.cs b/Examples/Textures/DrawTiled.cs index e069926..1e59525 100644 --- a/Examples/Textures/DrawTiled.cs +++ b/Examples/Textures/DrawTiled.cs @@ -1,11 +1,17 @@ /******************************************************************************************* * -* raylib [textures] example - Draw part of the texture tiled +* raylib [textures] example - tiled drawing * -* This example has been created using raylib 3.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 * -* Copyright (c) 2020 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) +* Example originally created with raylib 3.0, last time updated with raylib 4.2 +* +* Example contributed by Vlad Adrian (@demizdor) 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) 2020-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,53 +20,69 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class DrawTiled +public partial class DrawTiled : IExample { - const int OptWidth = 220; - const int MarginSize = 8; - const int ColorSize = 16; + private const int OptWidth = 220; + private const int MarginSize = 8; + private const int ColorSize = 16; - public static int Main() + public string Name => "Textures / Draw Tiled"; + + public string Title => "raylib [textures] example - tiled drawing"; + + public ConfigFlags ConfigFlags => ConfigFlags.ResizableWindow; + + private int screenWidth; + private int screenHeight; + + private Texture2D texPattern; + + // Coordinates for all patterns inside the texture + private Rectangle[] recPattern; + + // Setup colors + private Color[] colors; + private Rectangle[] colorRec; + + private int activePattern; + private int activeCol; + private float scale; + private float rotation; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - int screenWidth = 800; - int screenHeight = 450; - - SetConfigFlags(ConfigFlags.ResizableWindow); - InitWindow(screenWidth, screenHeight, "raylib [textures] example - Draw part of a texture tiled"); + screenWidth = 800; + screenHeight = 450; // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - Texture2D texPattern = LoadTexture("resources/patterns.png"); - - // Makes the texture smoother when upscaled - SetTextureFilter(texPattern, TextureFilter.Trilinear); + texPattern = LoadTexture("resources/patterns.png"); + SetTextureFilter(texPattern, TextureFilter.Bilinear); // Makes the texture smoother when upscaled // Coordinates for all patterns inside the texture - Rectangle[] recPattern = new[] { - new Rectangle(3, 3, 66, 66), - new Rectangle(75, 3, 100, 100), - new Rectangle(3, 75, 66, 66), - new Rectangle(7, 156, 50, 50), - new Rectangle(85, 106, 90, 45), - new Rectangle(75, 154, 100, 60) - }; + recPattern = new[] { + new Rectangle(3, 3, 66, 66), + new Rectangle(75, 3, 100, 100), + new Rectangle(3, 75, 66, 66), + new Rectangle(7, 156, 50, 50), + new Rectangle(85, 106, 90, 45), + new Rectangle(75, 154, 100, 60) + }; // Setup colors - Color[] colors = new[] + colors = new[] { - Color.Black, - Color.Maroon, - Color.Orange, - Color.Blue, - Color.Purple, - Color.Beige, - Color.Lime, - Color.Red, - Color.DarkGray, - Color.SkyBlue - }; - Rectangle[] colorRec = new Rectangle[colors.Length]; + Color.Black, + Color.Maroon, + Color.Orange, + Color.Blue, + Color.Purple, + Color.Beige, + Color.Lime, + Color.Red, + Color.DarkGray, + Color.SkyBlue + }; + colorRec = new Rectangle[colors.Length]; // Calculate rectangle for each color for (int i = 0, x = 0, y = 0; i < colors.Length; i++) @@ -81,155 +103,176 @@ public class DrawTiled } } - int activePattern = 0, activeCol = 0; - float scale = 1.0f, rotation = 0.0f; + activePattern = 0; + activeCol = 0; + scale = 1.0f; + rotation = 0.0f; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + screenWidth = GetScreenWidth(); + screenHeight = GetScreenHeight(); + + // Handle mouse + if (IsMouseButtonPressed(MouseButton.Left)) + { + var mouse = GetMousePosition(); + + // Check which pattern was clicked and set it as the active pattern + for (var i = 0; i < recPattern.Length; i++) + { + Rectangle rec = new( + 2 + MarginSize + recPattern[i].X, + 40 + MarginSize + recPattern[i].Y, + recPattern[i].Width, + recPattern[i].Height + ); + if (CheckCollisionPointRec(mouse, rec)) + { + activePattern = i; + break; + } + } + + // Check to see which color was clicked and set it as the active color + for (var i = 0; i < colors.Length; ++i) + { + if (CheckCollisionPointRec(mouse, colorRec[i])) + { + activeCol = i; + break; + } + } + } + + // Handle keys: change scale + if (IsKeyPressed(KeyboardKey.Up)) + { + scale += 0.25f; + } + if (IsKeyPressed(KeyboardKey.Down)) + { + scale -= 0.25f; + } + if (scale > 10.0f) + { + scale = 10.0f; + } + else if (scale <= 0.0f) + { + scale = 0.25f; + } + + // Handle keys: change rotation + if (IsKeyPressed(KeyboardKey.Left)) + { + rotation -= 25.0f; + } + if (IsKeyPressed(KeyboardKey.Right)) + { + rotation += 25.0f; + } + + // Handle keys: reset + if (IsKeyPressed(KeyboardKey.Space)) + { + rotation = 0.0f; + scale = 1.0f; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + // Draw the tiled area + var source = recPattern[activePattern]; + Rectangle dest = new( + OptWidth + MarginSize, + MarginSize, + screenWidth - OptWidth - 2 * MarginSize, + screenHeight - 2 * MarginSize + ); + DrawTextureTiled(texPattern, source, dest, Vector2.Zero, rotation, scale, colors[activeCol]); + + // Draw options + var color = ColorAlpha(Color.LightGray, 0.5f); + DrawRectangle(MarginSize, MarginSize, OptWidth - MarginSize, screenHeight - 2 * MarginSize, color); + + DrawText("Select Pattern", 2 + MarginSize, 30 + MarginSize, 10, Color.Black); + DrawTexture(texPattern, 2 + MarginSize, 40 + MarginSize, Color.Black); + DrawRectangle( + 2 + MarginSize + (int)recPattern[activePattern].X, + 40 + MarginSize + (int)recPattern[activePattern].Y, + (int)recPattern[activePattern].Width, + (int)recPattern[activePattern].Height, + ColorAlpha(Color.DarkBlue, 0.3f) + ); + + DrawText("Select Color", 2 + MarginSize, 10 + 256 + MarginSize, 10, Color.Black); + for (var i = 0; i < colors.Length; i++) + { + DrawRectangleRec(colorRec[i], colors[i]); + if (activeCol == i) + { + DrawRectangleLinesEx(colorRec[i], 3, ColorAlpha(Color.White, 0.5f)); + } + } + + DrawText("Scale (UP/DOWN to change)", 2 + MarginSize, 80 + 256 + MarginSize, 10, Color.Black); + DrawText($"{scale:F2}x", 2 + MarginSize, 92 + 256 + MarginSize, 20, Color.Black); + + DrawText("Rotation (LEFT/RIGHT to change)", 2 + MarginSize, 122 + 256 + MarginSize, 10, Color.Black); + DrawText($"{rotation:F0} degrees", 2 + MarginSize, 134 + 256 + MarginSize, 20, Color.Black); + + DrawText("Press [SPACE] to reset", 2 + MarginSize, 164 + 256 + MarginSize, 10, Color.DarkBlue); + + // Draw FPS + DrawText($"{GetFPS()} FPS", 2 + MarginSize, 2 + MarginSize, 20, Color.Black); + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texPattern); // Unload texture + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + SetConfigFlags(ConfigFlags.ResizableWindow); // Make the window resizable + InitWindow(800, 450, "raylib [textures] example - tiled drawing"); SetTargetFPS(60); //--------------------------------------------------------------------------------------- + var game = new DrawTiled(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - screenWidth = GetScreenWidth(); - screenHeight = GetScreenHeight(); - - // Handle mouse - if (IsMouseButtonPressed(MouseButton.Left)) - { - Vector2 mouse = GetMousePosition(); - - // Check which pattern was clicked and set it as the active pattern - for (int i = 0; i < recPattern.Length; i++) - { - Rectangle rec = new( - 2 + MarginSize + recPattern[i].X, - 40 + MarginSize + recPattern[i].Y, - recPattern[i].Width, - recPattern[i].Height - ); - if (CheckCollisionPointRec(mouse, rec)) - { - activePattern = i; - break; - } - } - - // Check to see which color was clicked and set it as the active color - for (int i = 0; i < colors.Length; ++i) - { - if (CheckCollisionPointRec(mouse, colorRec[i])) - { - activeCol = i; - break; - } - } - } - - // Handle keys - - // Change scale - if (IsKeyPressed(KeyboardKey.Up)) - { - scale += 0.25f; - } - if (IsKeyPressed(KeyboardKey.Down)) - { - scale -= 0.25f; - } - if (scale > 10.0f) - { - scale = 10.0f; - } - else if (scale <= 0.0f) - { - scale = 0.25f; - } - - // Change rotation - if (IsKeyPressed(KeyboardKey.Left)) - { - rotation -= 25.0f; - } - if (IsKeyPressed(KeyboardKey.Right)) - { - rotation += 25.0f; - } - - // Reset - if (IsKeyPressed(KeyboardKey.Space)) - { - rotation = 0.0f; - scale = 1.0f; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // Draw the tiled area - Rectangle source = recPattern[activePattern]; - Rectangle dest = new( - OptWidth + MarginSize, - MarginSize, - screenWidth - OptWidth - 2 * MarginSize, - screenHeight - 2 * MarginSize - ); - DrawTextureTiled(texPattern, source, dest, Vector2.Zero, rotation, scale, colors[activeCol]); - - // Draw options - Color color = ColorAlpha(Color.LightGray, 0.5f); - DrawRectangle(MarginSize, MarginSize, OptWidth - MarginSize, screenHeight - 2 * MarginSize, color); - - DrawText("Select Pattern", 2 + MarginSize, 30 + MarginSize, 10, Color.Black); - DrawTexture(texPattern, 2 + MarginSize, 40 + MarginSize, Color.Black); - DrawRectangle( - 2 + MarginSize + (int)recPattern[activePattern].X, - 40 + MarginSize + (int)recPattern[activePattern].Y, - (int)recPattern[activePattern].Width, - (int)recPattern[activePattern].Height, - ColorAlpha(Color.DarkBlue, 0.3f) - ); - - DrawText("Select Color", 2 + MarginSize, 10 + 256 + MarginSize, 10, Color.Black); - for (int i = 0; i < colors.Length; i++) - { - DrawRectangleRec(colorRec[i], colors[i]); - if (activeCol == i) - { - DrawRectangleLinesEx(colorRec[i], 3, ColorAlpha(Color.White, 0.5f)); - } - } - - DrawText("Scale (UP/DOWN to change)", 2 + MarginSize, 80 + 256 + MarginSize, 10, Color.Black); - DrawText($"{scale}x", 2 + MarginSize, 92 + 256 + MarginSize, 20, Color.Black); - - DrawText("Rotation (LEFT/RIGHT to change)", 2 + MarginSize, 122 + 256 + MarginSize, 10, Color.Black); - DrawText($"{rotation} degrees", 2 + MarginSize, 134 + 256 + MarginSize, 20, Color.Black); - - DrawText("Press [SPACE] to reset", 2 + MarginSize, 164 + 256 + MarginSize, 10, Color.DarkBlue); - - // Draw FPS - DrawText($"{GetFPS()}", 2 + MarginSize, 2 + MarginSize, 20, Color.Black); - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texPattern); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; } // Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. - static void DrawTextureTiled( + private static void DrawTextureTiled( Texture2D texture, Rectangle source, Rectangle dest, @@ -268,7 +311,7 @@ public class DrawTiled else if (dest.Width <= tileWidth) { // Tiled vertically (one column) - int dy = 0; + var dy = 0; for (; dy + tileHeight < dest.Height; dy += tileHeight) { DrawTexturePro( @@ -307,7 +350,7 @@ public class DrawTiled else if (dest.Height <= tileHeight) { // Tiled horizontally (one row) - int dx = 0; + var dx = 0; for (; dx + tileWidth < dest.Width; dx += tileWidth) { DrawTexturePro( @@ -351,10 +394,10 @@ public class DrawTiled else { // Tiled both horizontally and vertically (rows and columns) - int dx = 0; + var dx = 0; for (; dx + tileWidth < dest.Width; dx += tileWidth) { - int dy = 0; + var dy = 0; for (; dy + tileHeight < dest.Height; dy += tileHeight) { DrawTexturePro( @@ -397,7 +440,7 @@ public class DrawTiled // Fit last column of tiles if (dx < dest.Width) { - int dy = 0; + var dy = 0; for (; dy + tileHeight < dest.Height; dy += tileHeight) { DrawTexturePro( @@ -446,4 +489,3 @@ public class DrawTiled } } } - diff --git a/Examples/Textures/ImageDrawing.cs b/Examples/Textures/ImageDrawing.cs index 7b048d0..25cee29 100644 --- a/Examples/Textures/ImageDrawing.cs +++ b/Examples/Textures/ImageDrawing.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [textures] example - Image loading and drawing on it +* raylib [textures] example - image drawing +* +* Example complexity rating: [★★☆☆] 2/4 * * NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) * -* This example has been created using raylib 1.4 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.4, last time updated with raylib 1.4 * -* Copyright (c) 2016 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) 2016-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -16,85 +20,99 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class ImageDrawing +public partial class ImageDrawing : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Textures / Image Drawing"; + + public string Title => "raylib [textures] example - image drawing"; + + private Texture2D texture; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [textures] example - image drawing"); - // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - Image cat = LoadImage("resources/cat.png"); - ImageCrop(ref cat, new Rectangle(100, 10, 280, 380)); - ImageFlipHorizontal(ref cat); - ImageResize(ref cat, 150, 200); + var cat = LoadImage("resources/cat.png"); // Load image in CPU memory (RAM) + ImageCrop(ref cat, new Rectangle(100, 10, 280, 380)); // Crop an image piece + ImageFlipHorizontal(ref cat); // Flip cropped image horizontally + ImageResize(ref cat, 150, 200); // Resize flipped-cropped image - Image parrots = LoadImage("resources/parrots.png"); + var parrots = LoadImage("resources/parrots.png"); // Load image in CPU memory (RAM) // Draw one image over the other with a scaling of 1.5f Rectangle src = new(0, 0, cat.Width, cat.Height); ImageDraw(ref parrots, cat, src, new Rectangle(30, 40, cat.Width * 1.5f, cat.Height * 1.5f), Color.White); - ImageCrop(ref parrots, new Rectangle(0, 50, parrots.Width, parrots.Height - 100)); + ImageCrop(ref parrots, new Rectangle(0, 50, parrots.Width, parrots.Height - 100)); // Crop resulting image // Draw on the image with a few image draw methods ImageDrawPixel(ref parrots, 10, 10, Color.RayWhite); - ImageDrawCircle(ref parrots, 10, 10, 5, Color.RayWhite); + ImageDrawCircleLines(ref parrots, 10, 10, 5, Color.RayWhite); ImageDrawRectangle(ref parrots, 5, 20, 10, 10, Color.RayWhite); - UnloadImage(cat); + UnloadImage(cat); // Unload image from RAM - // Load custom font for frawing on image - Font font = LoadFont("resources/fonts/custom_jupiter_crash.png"); + // Load custom font for drawing on image + var font = LoadFont("resources/custom_jupiter_crash.png"); // Draw over image using custom font ImageDrawTextEx(ref parrots, font, "PARROTS & CAT", new Vector2(300, 230), font.BaseSize, -2, Color.White); - // Unload custom spritefont (already drawn used on image) - UnloadFont(font); + UnloadFont(font); // Unload custom font (already drawn used on image) - Texture2D texture = LoadTextureFromImage(parrots); - UnloadImage(parrots); + texture = LoadTextureFromImage(parrots); // Image converted to texture, uploaded to GPU memory (VRAM) + UnloadImage(parrots); // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM + } + + public void Update() + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + var x = screenWidth / 2 - texture.Width / 2; + var y = screenHeight / 2 - texture.Height / 2; + DrawTexture(texture, x, y - 40, Color.White); + DrawRectangleLines(x, y - 40, texture.Width, texture.Height, Color.DarkGray); + + DrawText("We are drawing only one texture from various images composed!", 240, 350, 10, Color.DarkGray); + DrawText("Source images have been cropped, scaled, flipped and copied one over the other.", 190, 370, 10, Color.DarkGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texture); // Texture unloading + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [textures] example - image drawing"); SetTargetFPS(60); - //--------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------- + + var game = new ImageDrawing(); + game.Init(); // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update your variables here - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - int x = screenWidth / 2 - texture.Width / 2; - int y = screenHeight / 2 - texture.Height / 2; - DrawTexture(texture, x, y - 40, Color.White); - DrawRectangleLines(x, y - 40, texture.Width, texture.Height, Color.DarkGray); - - DrawText("We are drawing only one texture from various images composed!", 240, 350, 10, Color.DarkGray); - - string text = "Source images have been cropped, scaled, flipped and copied one over the other."; - DrawText(text, 90, 370, 10, Color.DarkGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texture); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/ImageGeneration.cs b/Examples/Textures/ImageGeneration.cs index dacff9a..5cd901d 100644 --- a/Examples/Textures/ImageGeneration.cs +++ b/Examples/Textures/ImageGeneration.cs @@ -1,11 +1,17 @@ /******************************************************************************************* * -* raylib [textures] example - Procedural images generation +* raylib [textures] example - image generation * -* This example has been created using raylib 1.8 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2O17 Wilhem Barbier (@nounoursheureux) +* Example originally created with raylib 1.8, last time updated with raylib 1.8 +* +* Example contributed by Wilhem Barbier (@nounoursheureux) 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) 2017-2025 Wilhem Barbier (@nounoursheureux) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -13,104 +19,147 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class ImageGeneration +public partial class ImageGeneration : IExample { - public const int NumTextures = 6; + private const int screenWidth = 800; + private const int screenHeight = 450; + + // Currently we have 8 generation algorithms but some have multiple purposes (Linear and Square Gradients) + public const int NumTextures = 9; + + public string Name => "Textures / Image Generation"; + + public string Title => "raylib [textures] example - image generation"; + + private Texture2D[] textures; + private int currentTexture; + + public void Init() + { + var verticalGradient = GenImageGradientLinear(screenWidth, screenHeight, 0, Color.Red, Color.Blue); + var horizontalGradient = GenImageGradientLinear(screenWidth, screenHeight, 90, Color.Red, Color.Blue); + var diagonalGradient = GenImageGradientLinear(screenWidth, screenHeight, 45, Color.Red, Color.Blue); + var radialGradient = GenImageGradientRadial(screenWidth, screenHeight, 0.0f, Color.White, Color.Black); + var squareGradient = GenImageGradientSquare(screenWidth, screenHeight, 0.0f, Color.White, Color.Black); + var isChecked = GenImageChecked(screenWidth, screenHeight, 32, 32, Color.Red, Color.Blue); + var whiteNoise = GenImageWhiteNoise(screenWidth, screenHeight, 0.5f); + var perlinNoise = GenImagePerlinNoise(screenWidth, screenHeight, 50, 50, 4.0f); + var cellular = GenImageCellular(screenWidth, screenHeight, 32); + + textures = new Texture2D[NumTextures]; + textures[0] = LoadTextureFromImage(verticalGradient); + textures[1] = LoadTextureFromImage(horizontalGradient); + textures[2] = LoadTextureFromImage(diagonalGradient); + textures[3] = LoadTextureFromImage(radialGradient); + textures[4] = LoadTextureFromImage(squareGradient); + textures[5] = LoadTextureFromImage(isChecked); + textures[6] = LoadTextureFromImage(whiteNoise); + textures[7] = LoadTextureFromImage(perlinNoise); + textures[8] = LoadTextureFromImage(cellular); + + // Unload image data (CPU RAM) + UnloadImage(verticalGradient); + UnloadImage(horizontalGradient); + UnloadImage(diagonalGradient); + UnloadImage(radialGradient); + UnloadImage(squareGradient); + UnloadImage(isChecked); + UnloadImage(whiteNoise); + UnloadImage(perlinNoise); + UnloadImage(cellular); + + currentTexture = 0; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsMouseButtonPressed(MouseButton.Left) || IsKeyPressed(KeyboardKey.Right)) + { + // Cycle between the textures + currentTexture = (currentTexture + 1) % NumTextures; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawTexture(textures[currentTexture], 0, 0, Color.White); + + DrawRectangle(30, 400, 325, 30, Fade(Color.SkyBlue, 0.5f)); + DrawRectangleLines(30, 400, 325, 30, Fade(Color.White, 0.5f)); + DrawText("MOUSE LEFT BUTTON to CYCLE PROCEDURAL TEXTURES", 40, 410, 10, Color.White); + + switch (currentTexture) + { + case 0: + DrawText("VERTICAL GRADIENT", 560, 10, 20, Color.RayWhite); + break; + case 1: + DrawText("HORIZONTAL GRADIENT", 540, 10, 20, Color.RayWhite); + break; + case 2: + DrawText("DIAGONAL GRADIENT", 540, 10, 20, Color.RayWhite); + break; + case 3: + DrawText("RADIAL GRADIENT", 580, 10, 20, Color.LightGray); + break; + case 4: + DrawText("SQUARE GRADIENT", 580, 10, 20, Color.LightGray); + break; + case 5: + DrawText("CHECKED", 680, 10, 20, Color.RayWhite); + break; + case 6: + DrawText("WHITE NOISE", 640, 10, 20, Color.Red); + break; + case 7: + DrawText("PERLIN NOISE", 640, 10, 20, Color.Red); + break; + case 8: + DrawText("CELLULAR", 670, 10, 20, Color.RayWhite); + break; + default: + break; + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + for (var i = 0; i < textures.Length; i++) + { + UnloadTexture(textures[i]); + } + } public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [textures] example - procedural images generation"); - - Image verticalGradient = GenImageGradientLinear(screenWidth, screenHeight, 0, Color.Red, Color.Blue); - Image horizontalGradient = GenImageGradientLinear(screenWidth, screenHeight, 90, Color.Red, Color.Blue); - Image radialGradient = GenImageGradientRadial(screenWidth, screenHeight, 0.0f, Color.White, Color.Black); - Image isChecked = GenImageChecked(screenWidth, screenHeight, 32, 32, Color.Red, Color.Blue); - Image whiteNoise = GenImageWhiteNoise(screenWidth, screenHeight, 0.5f); - Image cellular = GenImageCellular(screenWidth, screenHeight, 32); - - Texture2D[] textures = new Texture2D[NumTextures]; - textures[0] = LoadTextureFromImage(verticalGradient); - textures[1] = LoadTextureFromImage(horizontalGradient); - textures[2] = LoadTextureFromImage(radialGradient); - textures[3] = LoadTextureFromImage(isChecked); - textures[4] = LoadTextureFromImage(whiteNoise); - textures[5] = LoadTextureFromImage(cellular); - - UnloadImage(verticalGradient); - UnloadImage(horizontalGradient); - UnloadImage(radialGradient); - UnloadImage(isChecked); - UnloadImage(whiteNoise); - UnloadImage(cellular); - - int currentTexture = 0; + InitWindow(screenWidth, screenHeight, "raylib [textures] example - image generation"); SetTargetFPS(60); //--------------------------------------------------------------------------------------- + var game = new ImageGeneration(); + game.Init(); + // Main game loop while (!WindowShouldClose()) { - // Update - //---------------------------------------------------------------------------------- - if (IsMouseButtonPressed(MouseButton.Left) || IsKeyPressed(KeyboardKey.Right)) - { - // Cycle between the textures - currentTexture = (currentTexture + 1) % NumTextures; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawTexture(textures[currentTexture], 0, 0, Color.White); - - DrawRectangle(30, 400, 325, 30, ColorAlpha(Color.SkyBlue, 0.5f)); - DrawRectangleLines(30, 400, 325, 30, ColorAlpha(Color.White, 0.5f)); - DrawText("MOUSE LEFT BUTTON to CYCLE PROCEDURAL TEXTURES", 40, 410, 10, Color.White); - - switch (currentTexture) - { - case 0: - DrawText("VERTICAL GRADIENT", 560, 10, 20, Color.RayWhite); - break; - case 1: - DrawText("HORIZONTAL GRADIENT", 540, 10, 20, Color.RayWhite); - break; - case 2: - DrawText("RADIAL GRADIENT", 580, 10, 20, Color.LightGray); - break; - case 3: - DrawText("CHECKED", 680, 10, 20, Color.RayWhite); - break; - case 4: - DrawText("Color.WHITE NOISE", 640, 10, 20, Color.Red); - break; - case 5: - DrawText("CELLULAR", 670, 10, 20, Color.RayWhite); - break; - default: - break; - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - for (int i = 0; i < textures.Length; i++) - { - UnloadTexture(textures[i]); - } - CloseWindow(); //-------------------------------------------------------------------------------------- diff --git a/Examples/Textures/ImageLoading.cs b/Examples/Textures/ImageLoading.cs index 5772b06..d525900 100644 --- a/Examples/Textures/ImageLoading.cs +++ b/Examples/Textures/ImageLoading.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [textures] example - Image loading and texture creation +* raylib [textures] example - image loading +* +* Example complexity rating: [★☆☆☆] 1/4 * * NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.3, last time updated with raylib 1.3 * -* Copyright (c) 2015 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -15,56 +19,74 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class ImageLoading +public partial class ImageLoading : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Textures / Image Loading"; + + public string Title => "raylib [textures] example - image loading"; + + private Texture2D texture; + + public void Init() + { + // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) + + var image = LoadImage("resources/raylib-cs_logo.png"); // Loaded in CPU memory (RAM) + texture = LoadTextureFromImage(image); // Image converted to texture, GPU memory (VRAM) + UnloadImage(image); // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM + } + + public void Update() + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawTexture( + texture, + screenWidth / 2 - texture.Width / 2, + screenHeight / 2 - texture.Height / 2, + Color.White + ); + + DrawText("this IS a texture loaded from an image!", 300, 370, 10, Color.Gray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texture); // Texture unloading + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [textures] example - image loading"); - // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - - Image image = LoadImage("resources/raylib-cs_logo.png"); - Texture2D texture = LoadTextureFromImage(image); - - UnloadImage(image); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //--------------------------------------------------------------------------------------- + var game = new ImageLoading(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update your variables here - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawTexture( - texture, - screenWidth / 2 - texture.Width / 2, - screenHeight / 2 - texture.Height / 2, - Color.White - ); - - DrawText("this IS a texture loaded from an image!", 300, 370, 10, Color.Gray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texture); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/ImageProcessing.cs b/Examples/Textures/ImageProcessing.cs index c28a263..91b546d 100644 --- a/Examples/Textures/ImageProcessing.cs +++ b/Examples/Textures/ImageProcessing.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [textures] example - Image processing +* raylib [textures] example - image processing +* +* Example complexity rating: [★★★☆] 3/4 * * NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) * -* This example has been created using raylib 1.4 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.4, last time updated with raylib 3.5 * -* Copyright (c) 2016 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) 2016-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -15,11 +19,18 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class ImageProcessing +public partial class ImageProcessing : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + public const int NumProcesses = 9; - enum ImageProcess + public string Name => "Textures / Image Processing"; + + public string Title => "raylib [textures] example - image processing"; + + private enum ImageProcess { None = 0, ColorGrayScale, @@ -32,188 +43,210 @@ public class ImageProcessing FlipHorizontal } - static string[] processText = { - "NO PROCESSING", - "COLOR GRAYSCALE", - "COLOR TINT", - "COLOR INVERT", - "COLOR CONTRAST", - "COLOR BRIGHTNESS", - "GAUSSIAN BLUR", - "FLIP VERTICAL", - "FLIP HORIZONTAL" - }; + private string[] processText = { + "NO PROCESSING", + "COLOR GRAYSCALE", + "COLOR TINT", + "COLOR INVERT", + "COLOR CONTRAST", + "COLOR BRIGHTNESS", + "GAUSSIAN BLUR", + "FLIP VERTICAL", + "FLIP HORIZONTAL" + }; - public unsafe static int Main() + private Image imageOrigin; + private Image imageCopy; + private Texture2D texture; + private ImageProcess currentProcess; + private bool textureReload; + private Rectangle[] toggleRecs; + private int mouseHoverRec; + + public void Init() + { + // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) + + imageOrigin = LoadImage("resources/parrots.png"); // Loaded in CPU memory (RAM) + ImageFormat(ref imageOrigin, PixelFormat.UncompressedR8G8B8A8); // Format image to RGBA 32bit (required for texture update) <-- ISSUE + texture = LoadTextureFromImage(imageOrigin); // Image converted to texture, GPU memory (VRAM) + + imageCopy = ImageCopy(imageOrigin); + + currentProcess = ImageProcess.None; + textureReload = false; + + toggleRecs = new Rectangle[NumProcesses]; + mouseHoverRec = -1; + + for (var i = 0; i < NumProcesses; i++) + { + toggleRecs[i] = new Rectangle(40.0f, (float)(50 + 32 * i), 150.0f, 30.0f); + } + } + + public unsafe void Update() + { + // Update + //---------------------------------------------------------------------------------- + + // Mouse toggle group logic + for (var i = 0; i < NumProcesses; i++) + { + if (CheckCollisionPointRec(GetMousePosition(), toggleRecs[i])) + { + mouseHoverRec = i; + + if (IsMouseButtonReleased(MouseButton.Left)) + { + currentProcess = (ImageProcess)i; + textureReload = true; + } + break; + } + else + { + mouseHoverRec = -1; + } + } + + // Keyboard toggle group logic + if (IsKeyPressed(KeyboardKey.Down)) + { + currentProcess++; + if ((int)currentProcess > (NumProcesses - 1)) + { + currentProcess = 0; + } + + textureReload = true; + } + else if (IsKeyPressed(KeyboardKey.Up)) + { + currentProcess--; + if (currentProcess < 0) + { + currentProcess = ImageProcess.FlipHorizontal; + } + + textureReload = true; + } + + // Reload texture when required + if (textureReload) + { + UnloadImage(imageCopy); // Unload image-copy data + imageCopy = ImageCopy(imageOrigin); // Restore image-copy from image-origin + + // NOTE: Image processing is a costly CPU process to be done every frame, + // If image processing is required in a frame-basis, it should be done + // with a texture and by shaders + switch (currentProcess) + { + case ImageProcess.ColorGrayScale: + ImageColorGrayscale(ref imageCopy); + break; + case ImageProcess.ColorTint: + ImageColorTint(ref imageCopy, Color.Green); + break; + case ImageProcess.ColorInvert: + ImageColorInvert(ref imageCopy); + break; + case ImageProcess.ColorContrast: + ImageColorContrast(ref imageCopy, -40); + break; + case ImageProcess.ColorBrightness: + ImageColorBrightness(ref imageCopy, -80); + break; + case ImageProcess.GaussianBlur: + ImageBlurGaussian(ref imageCopy, 10); + break; + case ImageProcess.FlipVertical: + ImageFlipVertical(ref imageCopy); + break; + case ImageProcess.FlipHorizontal: + ImageFlipHorizontal(ref imageCopy); + break; + default: + break; + } + + var pixels = LoadImageColors(imageCopy); // Load pixel data from image (RGBA 32bit) + UpdateTexture(texture, pixels); // Update texture with new image data + UnloadImageColors(pixels); // Unload pixels data from RAM + + textureReload = false; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("IMAGE PROCESSING:", 40, 30, 10, Color.DarkGray); + + // Draw rectangles + for (var i = 0; i < NumProcesses; i++) + { + DrawRectangleRec(toggleRecs[i], ((i == (int)currentProcess) || (i == mouseHoverRec)) ? Color.SkyBlue : Color.LightGray); + DrawRectangleLines( + (int)toggleRecs[i].X, + (int)toggleRecs[i].Y, + (int)toggleRecs[i].Width, + (int)toggleRecs[i].Height, + ((i == (int)currentProcess) || (i == mouseHoverRec)) ? Color.Blue : Color.Gray + ); + + var labelX = (int)(toggleRecs[i].X + toggleRecs[i].Width / 2); + DrawText( + processText[i], + (int)(labelX - MeasureText(processText[i], 10) / 2), + (int)toggleRecs[i].Y + 11, + 10, + ((i == (int)currentProcess) || (i == mouseHoverRec)) ? Color.DarkBlue : Color.DarkGray + ); + } + + var x = screenWidth - texture.Width - 60; + var y = screenHeight / 2 - texture.Height / 2; + DrawTexture(texture, x, y, Color.White); + DrawRectangleLines(x, y, texture.Width, texture.Height, Color.Black); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texture); // Unload texture from VRAM + UnloadImage(imageOrigin); // Unload image-origin from RAM + UnloadImage(imageCopy); // Unload image-copy from RAM + } + + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [textures] example - image processing"); - // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - Image imageOrigin = LoadImage("resources/parrots.png"); - ImageFormat(ref imageOrigin, PixelFormat.UncompressedR8G8B8A8); - Texture2D texture = LoadTextureFromImage(imageOrigin); - - Image imageCopy = ImageCopy(imageOrigin); - - ImageProcess currentProcess = ImageProcess.None; - bool textureReload = false; - - Rectangle[] toggleRecs = new Rectangle[NumProcesses]; - int mouseHoverRec = -1; - - for (int i = 0; i < NumProcesses; i++) - { - toggleRecs[i] = new Rectangle(40, 50 + 32 * i, 150, 30); - } - SetTargetFPS(60); //--------------------------------------------------------------------------------------- + var game = new ImageProcessing(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - - // Mouse toggle group logic - for (int i = 0; i < NumProcesses; i++) - { - if (CheckCollisionPointRec(GetMousePosition(), toggleRecs[i])) - { - mouseHoverRec = i; - - if (IsMouseButtonReleased(MouseButton.Left)) - { - currentProcess = (ImageProcess)i; - textureReload = true; - } - break; - } - else - { - mouseHoverRec = -1; - } - } - - // Keyboard toggle group logic - if (IsKeyPressed(KeyboardKey.Down)) - { - currentProcess++; - if ((int)currentProcess > (NumProcesses - 1)) - { - currentProcess = 0; - } - - textureReload = true; - } - else if (IsKeyPressed(KeyboardKey.Up)) - { - currentProcess--; - if (currentProcess < 0) - { - currentProcess = ImageProcess.FlipHorizontal; - } - - textureReload = true; - } - - if (textureReload) - { - UnloadImage(imageCopy); - imageCopy = ImageCopy(imageOrigin); - - // NOTE: Image processing is a costly CPU process to be done every frame, - // If image processing is required in a frame-basis, it should be done - // with a texture and by shaders - switch (currentProcess) - { - case ImageProcess.ColorGrayScale: - ImageColorGrayscale(ref imageCopy); - break; - case ImageProcess.ColorTint: - ImageColorTint(ref imageCopy, Color.Green); - break; - case ImageProcess.ColorInvert: - ImageColorInvert(ref imageCopy); - break; - case ImageProcess.ColorContrast: - ImageColorContrast(ref imageCopy, -40); - break; - case ImageProcess.ColorBrightness: - ImageColorBrightness(ref imageCopy, -80); - break; - case ImageProcess.GaussianBlur: - ImageBlurGaussian(ref imageCopy, 10); - break; - case ImageProcess.FlipVertical: - ImageFlipVertical(ref imageCopy); - break; - case ImageProcess.FlipHorizontal: - ImageFlipHorizontal(ref imageCopy); - break; - default: - break; - } - - // Get pixel data from image (RGBA 32bit) - Color* pixels = LoadImageColors(imageCopy); - UpdateTexture(texture, pixels); - UnloadImageColors(pixels); - - textureReload = false; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("IMAGE PROCESSING:", 40, 30, 10, Color.DarkGray); - - // Draw rectangles - for (int i = 0; i < NumProcesses; i++) - { - DrawRectangleRec(toggleRecs[i], (i == (int)currentProcess) ? Color.SkyBlue : Color.LightGray); - DrawRectangleLines( - (int)toggleRecs[i].X, - (int)toggleRecs[i].Y, - (int)toggleRecs[i].Width, - (int)toggleRecs[i].Height, - (i == (int)currentProcess) ? Color.Blue : Color.Gray - ); - - int labelX = (int)(toggleRecs[i].X + toggleRecs[i].Width / 2); - DrawText( - processText[i], - (int)(labelX - MeasureText(processText[i], 10) / 2), - (int)toggleRecs[i].Y + 11, - 10, - (i == (int)currentProcess) ? Color.DarkBlue : Color.DarkGray - ); - } - - int x = screenWidth - texture.Width - 60; - int y = screenHeight / 2 - texture.Height / 2; - DrawTexture(texture, x, y, Color.White); - DrawRectangleLines(x, y, texture.Width, texture.Height, Color.Black); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texture); - UnloadImage(imageOrigin); - UnloadImage(imageCopy); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/ImageText.cs b/Examples/Textures/ImageText.cs index 71fed41..cbed26e 100644 --- a/Examples/Textures/ImageText.cs +++ b/Examples/Textures/ImageText.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [texture] example - Image text drawing using TTF generated spritefont +* raylib [textures] example - image text * -* This example has been created using raylib 1.8 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2017 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.8, last time updated with raylib 4.0 +* +* 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) 2017-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,21 +18,26 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class ImageText +public partial class ImageText : IExample { - public static int Main() - { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + private const int screenWidth = 800; + private const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [texture] example - image text drawing"); + public string Name => "Textures / Image Text"; + + public string Title => "raylib [textures] example - image text"; + + private Font font; + private Texture2D texture; + private Vector2 position; + private bool showFont; + + public void Init() + { + var parrots = LoadImage("resources/parrots.png"); // Load image in CPU memory (RAM) // TTF Font loading with custom generation parameters - Font font = LoadFontEx("resources/fonts/KAISG.ttf", 64, null, 95); - - Image parrots = LoadImage("resources/parrots.png"); + font = LoadFontEx("resources/fonts/KAISG.ttf", 64, null, 0); // Draw over image using custom font ImageDrawTextEx( @@ -38,69 +47,89 @@ public class ImageText new Vector2(20, 20), font.BaseSize, 0, - Color.White + Color.Red ); - // Image converted to texture, uploaded to GPU memory (VRAM) - Texture2D texture = LoadTextureFromImage(parrots); - UnloadImage(parrots); + texture = LoadTextureFromImage(parrots); // Image converted to texture, uploaded to GPU memory (VRAM) + UnloadImage(parrots); // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM - Vector2 position = new( + position = new( screenWidth / 2 - texture.Width / 2, screenHeight / 2 - texture.Height / 2 - 20 ); - bool showFont = false; + showFont = false; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyDown(KeyboardKey.Space)) + { + showFont = true; + } + else + { + showFont = false; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + if (!showFont) + { + // Draw texture with text already drawn inside + DrawTextureV(texture, position, Color.White); + + // Draw text directly using sprite font + Vector2 textPosition = new(position.X + 20, position.Y + 20 + 280); + DrawTextEx(font, "[Parrots font drawing]", textPosition, font.BaseSize, 0, Color.White); + } + else + { + DrawTexture(font.Texture, screenWidth / 2 - font.Texture.Width / 2, 50, Color.Black); + } + + DrawText("PRESS SPACE to SHOW FONT ATLAS USED", 290, 420, 10, Color.DarkGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texture); // Texture unloading + + UnloadFont(font); // Unload custom font + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [textures] example - image text"); SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new ImageText(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyDown(KeyboardKey.Space)) - { - showFont = true; - } - else - { - showFont = false; - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - if (!showFont) - { - // Draw texture with text already drawn inside - DrawTextureV(texture, position, Color.White); - - // Draw text directly using sprite font - Vector2 textPosition = new(position.X + 20, position.Y + 20 + 280); - DrawTextEx(font, "[Parrots font drawing]", textPosition, font.BaseSize, 0, Color.White); - } - else - { - DrawTexture(font.Texture, screenWidth / 2 - font.Texture.Width / 2, 50, Color.Black); - } - - DrawText("PRESS SPACE to SEE USED SPRITEFONT ", 290, 420, 10, Color.DarkGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texture); - UnloadFont(font); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/LogoRaylibTexture.cs b/Examples/Textures/LogoRaylibTexture.cs index 995c90e..64b5a6c 100644 --- a/Examples/Textures/LogoRaylibTexture.cs +++ b/Examples/Textures/LogoRaylibTexture.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [textures] example - Texture loading and drawing +* raylib [textures] example - logo raylib * -* This example has been created using raylib 1.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 * -* Copyright (c) 2014 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.0, last time updated with raylib 1.0 +* +* 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -13,52 +17,71 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class LogoRaylibTexture +public partial class LogoRaylibTexture : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Textures / Logo Raylib Texture"; + + public string Title => "raylib [textures] example - logo raylib"; + + private Texture2D texture; + + public void Init() + { + // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) + texture = LoadTexture("resources/raylib-cs_logo.png"); // Texture loading + } + + public void Update() + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawTexture( + texture, + screenWidth / 2 - texture.Width / 2, + screenHeight / 2 - texture.Height / 2, + Color.White + ); + + DrawText("this IS a texture!", 360, 370, 10, Color.Gray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texture); // Texture unloading + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [textures] example - logo raylib"); - InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture loading and drawing"); - - // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - Texture2D texture = LoadTexture("resources/raylib-cs_logo.png"); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //--------------------------------------------------------------------------------------- + var game = new LogoRaylibTexture(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update your variables here - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawTexture( - texture, - screenWidth / 2 - texture.Width / 2, - screenHeight / 2 - texture.Height / 2, - Color.White - ); - - DrawText("this IS a texture!", 360, 370, 10, Color.Gray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texture); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/MousePainting.cs b/Examples/Textures/MousePainting.cs index 62f4692..e828201 100644 --- a/Examples/Textures/MousePainting.cs +++ b/Examples/Textures/MousePainting.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [textures] example - Mouse painting +* raylib [textures] example - mouse painting * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 3.0, last time updated with raylib 3.0 * * Example contributed by Chris Dill (@MysteriousSpace) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2019 Chris Dill (@MysteriousSpace) and 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) 2019-2025 Chris Dill (@MysteriousSpace) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -16,48 +20,66 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class MousePainting +public partial class MousePainting : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Textures / Mouse Painting"; + + public string Title => "raylib [textures] example - mouse painting"; + + public int TargetFps => 120; + + private Color[] colors; + private Rectangle[] colorsRecs; + + private int colorSelected; + private int colorSelectedPrev; + private int colorMouseHover; + private float brushSize; + private bool mouseWasPressed; + + private Rectangle btnSaveRec; + private bool btnSaveMouseHover; + private bool showSaveMessage; + private int saveMessageCounter; + + private RenderTexture2D target; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [textures] example - mouse painting"); - - // Colours to choose from - Color[] colors = new Color[] { - Color.RayWhite, - Color.Yellow, - Color.Gold, - Color.Orange, - Color.Pink, - Color.Red, - Color.Maroon, - Color.Green, - Color.Lime, - Color.DarkGreen, - Color.SkyBlue, - Color.Blue, - Color.DarkBlue, - Color.Purple, - Color.Violet, - Color.DarkPurple, - Color.Beige, - Color.Brown, - Color.DarkBrown, - Color.LightGray, - Color.Gray, - Color.DarkGray, - Color.Black - }; + // Colors to choose from + colors = new Color[] { + Color.RayWhite, + Color.Yellow, + Color.Gold, + Color.Orange, + Color.Pink, + Color.Red, + Color.Maroon, + Color.Green, + Color.Lime, + Color.DarkGreen, + Color.SkyBlue, + Color.Blue, + Color.DarkBlue, + Color.Purple, + Color.Violet, + Color.DarkPurple, + Color.Beige, + Color.Brown, + Color.DarkBrown, + Color.LightGray, + Color.Gray, + Color.DarkGray, + Color.Black + }; // Define colorsRecs data (for every rectangle) - Rectangle[] colorsRecs = new Rectangle[colors.Length]; + colorsRecs = new Rectangle[colors.Length]; - for (int i = 0; i < colorsRecs.Length; i++) + for (var i = 0; i < colorsRecs.Length; i++) { colorsRecs[i].X = 10 + 30 * i + 2 * i; colorsRecs[i].Y = 10; @@ -65,226 +87,255 @@ public class MousePainting colorsRecs[i].Height = 30; } - int colorSelected = 0; - int colorSelectedPrev = colorSelected; - int colorMouseHover = 0; - int brushSize = 20; + colorSelected = 0; + colorSelectedPrev = colorSelected; + colorMouseHover = 0; + brushSize = 20.0f; + mouseWasPressed = false; - Rectangle btnSaveRec = new(750, 10, 40, 30); - bool btnSaveMouseHover = false; - bool showSaveMessage = false; - int saveMessageCounter = 0; + btnSaveRec = new(750, 10, 40, 30); + btnSaveMouseHover = false; + showSaveMessage = false; + saveMessageCounter = 0; // Create a RenderTexture2D to use as a canvas - RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); + target = LoadRenderTexture(screenWidth, screenHeight); // Clear render texture before entering the game loop BeginTextureMode(target); ClearBackground(colors[0]); EndTextureMode(); + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + var mousePos = GetMousePosition(); + + // Move between colors with keys + if (IsKeyPressed(KeyboardKey.Right)) + { + colorSelected++; + } + else if (IsKeyPressed(KeyboardKey.Left)) + { + colorSelected--; + } + + if (colorSelected >= colors.Length) + { + colorSelected = colors.Length - 1; + } + else if (colorSelected < 0) + { + colorSelected = 0; + } + + // Choose color with mouse + for (var i = 0; i < colors.Length; i++) + { + if (CheckCollisionPointRec(mousePos, colorsRecs[i])) + { + colorMouseHover = i; + break; + } + else + { + colorMouseHover = -1; + } + } + + if ((colorMouseHover >= 0) && IsMouseButtonPressed(MouseButton.Left)) + { + colorSelected = colorMouseHover; + colorSelectedPrev = colorSelected; + } + + // Change brush size + brushSize += GetMouseWheelMove() * 5; + if (brushSize < 2) + { + brushSize = 2; + } + + if (brushSize > 50) + { + brushSize = 50; + } + + if (IsKeyPressed(KeyboardKey.C)) + { + // Clear render texture to clear color + BeginTextureMode(target); + ClearBackground(colors[0]); + EndTextureMode(); + } + + if (IsMouseButtonDown(MouseButton.Left) || (GetGestureDetected() == Gesture.Drag)) + { + // Paint circle into render texture + // NOTE: To avoid discontinuous circles, we could store + // previous-next mouse points and just draw a line using brush size + BeginTextureMode(target); + if (mousePos.Y > 50) + { + DrawCircle((int)mousePos.X, (int)mousePos.Y, brushSize, colors[colorSelected]); + } + + EndTextureMode(); + } + + if (IsMouseButtonDown(MouseButton.Right)) + { + if (!mouseWasPressed) + { + colorSelectedPrev = colorSelected; + colorSelected = 0; + } + + mouseWasPressed = true; + + // Erase circle from render texture + BeginTextureMode(target); + if (mousePos.Y > 50) + { + DrawCircle((int)mousePos.X, (int)mousePos.Y, brushSize, colors[0]); + } + + EndTextureMode(); + } + else if (IsMouseButtonReleased(MouseButton.Right) && mouseWasPressed) + { + colorSelected = colorSelectedPrev; + mouseWasPressed = false; + } + + // Check mouse hover save button + if (CheckCollisionPointRec(mousePos, btnSaveRec)) + { + btnSaveMouseHover = true; + } + else + { + btnSaveMouseHover = false; + } + + // Image saving logic + // NOTE: Saving painted texture to a default named image + if ((btnSaveMouseHover && IsMouseButtonReleased(MouseButton.Left)) || + IsKeyPressed(KeyboardKey.S)) + { + var image = LoadImageFromTexture(target.Texture); + ImageFlipVertical(ref image); + ExportImage(image, "my_amazing_texture_painting.png"); + UnloadImage(image); + showSaveMessage = true; + } + + if (showSaveMessage) + { + // On saving, show a full screen message for 2 seconds + saveMessageCounter++; + if (saveMessageCounter > 240) + { + showSaveMessage = false; + saveMessageCounter = 0; + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + // NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom) + Rectangle source = new(0, 0, target.Texture.Width, -target.Texture.Height); + DrawTextureRec(target.Texture, source, new Vector2(0, 0), Color.White); + + // Draw drawing circle for reference + if (mousePos.Y > 50) + { + if (IsMouseButtonDown(MouseButton.Right)) + { + DrawCircleLines((int)mousePos.X, (int)mousePos.Y, brushSize, Color.Gray); + } + else + { + DrawCircle(GetMouseX(), GetMouseY(), brushSize, colors[colorSelected]); + } + } + + // Draw top panel + DrawRectangle(0, 0, GetScreenWidth(), 50, Color.RayWhite); + DrawLine(0, 50, GetScreenWidth(), 50, Color.LightGray); + + // Draw color selection rectangles + for (var i = 0; i < colors.Length; i++) + { + DrawRectangleRec(colorsRecs[i], colors[i]); + } + + DrawRectangleLines(10, 10, 30, 30, Color.LightGray); + + if (colorMouseHover >= 0) + { + DrawRectangleRec(colorsRecs[colorMouseHover], Fade(Color.White, 0.6f)); + } + + Rectangle rec = new( + colorsRecs[colorSelected].X - 2, + colorsRecs[colorSelected].Y - 2, + colorsRecs[colorSelected].Width + 4, + colorsRecs[colorSelected].Height + 4 + ); + DrawRectangleLinesEx(rec, 2, Color.Black); + + // Draw save image button + DrawRectangleLinesEx(btnSaveRec, 2, btnSaveMouseHover ? Color.Red : Color.Black); + DrawText("SAVE!", 755, 20, 10, btnSaveMouseHover ? Color.Red : Color.Black); + + // Draw save image message + if (showSaveMessage) + { + DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Fade(Color.RayWhite, 0.8f)); + DrawRectangle(0, 150, GetScreenWidth(), 80, Color.Black); + DrawText("IMAGE SAVED!", 150, 180, 20, Color.RayWhite); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadRenderTexture(target); // Unload render texture + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [textures] example - mouse painting"); SetTargetFPS(120); // Set our game to run at 120 frames-per-second //-------------------------------------------------------------------------------------- + var game = new MousePainting(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - Vector2 mousePos = GetMousePosition(); - - // Move between colors with keys - if (IsKeyPressed(KeyboardKey.Right)) - { - colorSelected++; - } - else if (IsKeyPressed(KeyboardKey.Left)) - { - colorSelected--; - } - - if (colorSelected >= colors.Length) - { - colorSelected = colors.Length - 1; - } - else if (colorSelected < 0) - { - colorSelected = 0; - } - - // Choose color with mouse - for (int i = 0; i < colors.Length; i++) - { - if (CheckCollisionPointRec(mousePos, colorsRecs[i])) - { - colorMouseHover = i; - break; - } - else - { - colorMouseHover = -1; - } - } - - if ((colorMouseHover >= 0) && IsMouseButtonPressed(MouseButton.Left)) - { - colorSelected = colorMouseHover; - colorSelectedPrev = colorSelected; - } - - // Change brush size - brushSize += (int)(GetMouseWheelMove() * 5); - if (brushSize < 2) - { - brushSize = 2; - } - - if (brushSize > 50) - { - brushSize = 50; - } - - if (IsKeyPressed(KeyboardKey.C)) - { - // Clear render texture to clear color - BeginTextureMode(target); - ClearBackground(colors[0]); - EndTextureMode(); - } - - if (IsMouseButtonDown(MouseButton.Left)) - { - // Paint circle into render texture - // NOTE: To avoid discontinuous circles, we could store - // previous-next mouse points and just draw a line using brush size - BeginTextureMode(target); - if (mousePos.Y > 50) - { - DrawCircle((int)mousePos.X, (int)mousePos.Y, brushSize, colors[colorSelected]); - } - - EndTextureMode(); - } - else if (IsMouseButtonDown(MouseButton.Right)) - { - colorSelected = 0; - - // Erase circle from render texture - BeginTextureMode(target); - if (mousePos.Y > 50) - { - DrawCircle((int)mousePos.X, (int)mousePos.Y, brushSize, colors[0]); - } - - EndTextureMode(); - } - else - { - colorSelected = colorSelectedPrev; - } - - // Check mouse hover save button - if (CheckCollisionPointRec(mousePos, btnSaveRec)) - { - btnSaveMouseHover = true; - } - else - { - btnSaveMouseHover = false; - } - - // Image saving logic - // NOTE: Saving painted texture to a default named image - if ((btnSaveMouseHover && IsMouseButtonReleased(MouseButton.Left)) || - IsKeyPressed(KeyboardKey.S)) - { - Image image = LoadImageFromTexture(target.Texture); - ImageFlipVertical(ref image); - ExportImage(image, "my_amazing_texture_painting.png"); - UnloadImage(image); - showSaveMessage = true; - } - - if (showSaveMessage) - { - // On saving, show a full screen message for 2 seconds - saveMessageCounter++; - if (saveMessageCounter > 240) - { - showSaveMessage = false; - saveMessageCounter = 0; - } - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom) - Rectangle source = new(0, 0, target.Texture.Width, -target.Texture.Height); - DrawTextureRec(target.Texture, source, new Vector2(0, 0), Color.White); - - // Draw drawing circle for reference - if (mousePos.Y > 50) - { - if (IsMouseButtonDown(MouseButton.Right)) - { - DrawCircleLines((int)mousePos.X, (int)mousePos.Y, brushSize, colors[colorSelected]); - } - else - { - DrawCircle(GetMouseX(), GetMouseY(), brushSize, colors[colorSelected]); - } - } - - // Draw top panel - DrawRectangle(0, 0, GetScreenWidth(), 50, Color.RayWhite); - DrawLine(0, 50, GetScreenWidth(), 50, Color.LightGray); - - // Draw color selection rectangles - for (int i = 0; i < colors.Length; i++) - { - DrawRectangleRec(colorsRecs[i], colors[i]); - } - - DrawRectangleLines(10, 10, 30, 30, Color.LightGray); - - if (colorMouseHover >= 0) - { - DrawRectangleRec(colorsRecs[colorMouseHover], ColorAlpha(Color.White, 0.6f)); - } - - Rectangle rec = new( - colorsRecs[colorSelected].X - 2, - colorsRecs[colorSelected].Y - 2, - colorsRecs[colorSelected].Width + 4, - colorsRecs[colorSelected].Height + 4 - ); - DrawRectangleLinesEx(rec, 2, Color.Black); - - // Draw save image button - DrawRectangleLinesEx(btnSaveRec, 2, btnSaveMouseHover ? Color.Red : Color.Black); - DrawText("SAVE!", 755, 20, 10, btnSaveMouseHover ? Color.Red : Color.Black); - - // Draw save image message - if (showSaveMessage) - { - DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), ColorAlpha(Color.RayWhite, 0.8f)); - DrawRectangle(0, 150, GetScreenWidth(), 80, Color.Black); - DrawText("IMAGE SAVED: my_amazing_texture_painting.png", 150, 180, 20, Color.RayWhite); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadRenderTexture(target); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/NpatchDrawing.cs b/Examples/Textures/NpatchDrawing.cs index 58315ca..f641535 100644 --- a/Examples/Textures/NpatchDrawing.cs +++ b/Examples/Textures/NpatchDrawing.cs @@ -1,15 +1,19 @@ /******************************************************************************************* * -* raylib [textures] example - N-patch drawing +* raylib [textures] example - npatch drawing +* +* Example complexity rating: [★★★☆] 3/4 * * NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) * -* This example has been created using raylib 2.0 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 2.0, last time updated with raylib 2.5 * * Example contributed by Jorge A. Gomes (@overdev) and reviewed by Ramon Santamaria (@raysan5) * -* Copyright (c) 2018 Jorge A. Gomes (@overdev) and 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) 2018-2025 Jorge A. Gomes (@overdev) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -19,31 +23,46 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class NpatchDrawing +public partial class NpatchDrawing : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Textures / N-patch Drawing"; + + public string Title => "raylib [textures] example - npatch drawing"; + + private Texture2D nPatchTexture; + + private Vector2 mousePosition; + private Vector2 origin; + + private Rectangle dstRec1; + private Rectangle dstRec2; + private Rectangle dstRecH; + private Rectangle dstRecV; + + private NPatchInfo ninePatchInfo1; + private NPatchInfo ninePatchInfo2; + private NPatchInfo h3PatchInfo; + private NPatchInfo v3PatchInfo; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [textures] example - N-patch drawing"); - // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - Texture2D nPatchTexture = LoadTexture("resources/ninepatch_button.png"); + nPatchTexture = LoadTexture("resources/ninepatch_button.png"); - Vector2 mousePosition = new(0.0f, 0.0f); - Vector2 origin = new(0.0f, 0.0f); + mousePosition = new(0.0f, 0.0f); + origin = new(0.0f, 0.0f); // Position and size of the n-patches - Rectangle dstRec1 = new(480.0f, 160.0f, 32.0f, 32.0f); - Rectangle dstRec2 = new(160.0f, 160.0f, 32.0f, 32.0f); - Rectangle dstRecH = new(160.0f, 93.0f, 32.0f, 32.0f); - Rectangle dstRecV = new(92.0f, 160.0f, 32.0f, 32.0f); + dstRec1 = new(480.0f, 160.0f, 32.0f, 32.0f); + dstRec2 = new(160.0f, 160.0f, 32.0f, 32.0f); + dstRecH = new(160.0f, 93.0f, 32.0f, 32.0f); + dstRecV = new(92.0f, 160.0f, 32.0f, 32.0f); - // A 9-patch (NPT_9PATCH) changes its sizes in both axis - NPatchInfo ninePatchInfo1 = new NPatchInfo + // A 9-patch (NPATCH_NINE_PATCH) changes its sizes in both axis + ninePatchInfo1 = new NPatchInfo { Source = new Rectangle(0.0f, 0.0f, 64.0f, 64.0f), Left = 12, @@ -52,7 +71,7 @@ public class NpatchDrawing Bottom = 12, Layout = NPatchLayout.NinePatch }; - NPatchInfo ninePatchInfo2 = new NPatchInfo + ninePatchInfo2 = new NPatchInfo { Source = new Rectangle(0.0f, 128.0f, 64.0f, 64.0f), Left = 16, @@ -62,8 +81,8 @@ public class NpatchDrawing Layout = NPatchLayout.NinePatch }; - // A horizontal 3-patch (NPT_3PATCH_HORIZONTAL) changes its sizes along the x axis only - NPatchInfo h3PatchInfo = new NPatchInfo + // A horizontal 3-patch (NPATCH_THREE_PATCH_HORIZONTAL) changes its sizes along the x axis only + h3PatchInfo = new NPatchInfo { Source = new Rectangle(0.0f, 64.0f, 64.0f, 64.0f), Left = 8, @@ -73,8 +92,8 @@ public class NpatchDrawing Layout = NPatchLayout.ThreePatchHorizontal }; - // A vertical 3-patch (NPT_3PATCH_VERTICAL) changes its sizes along the y axis only - NPatchInfo v3PatchInfo = new NPatchInfo + // A vertical 3-patch (NPATCH_THREE_PATCH_VERTICAL) changes its sizes along the y axis only + v3PatchInfo = new NPatchInfo { Source = new Rectangle(0.0f, 192.0f, 64.0f, 64.0f), Left = 6, @@ -83,61 +102,81 @@ public class NpatchDrawing Bottom = 6, Layout = NPatchLayout.ThreePatchVertical }; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + mousePosition = GetMousePosition(); + + // Resize the n-patches based on mouse position + dstRec1.Width = mousePosition.X - dstRec1.X; + dstRec1.Height = mousePosition.Y - dstRec1.Y; + dstRec2.Width = mousePosition.X - dstRec2.X; + dstRec2.Height = mousePosition.Y - dstRec2.Y; + dstRecH.Width = mousePosition.X - dstRecH.X; + dstRecV.Height = mousePosition.Y - dstRecV.Y; + + // Set a minimum width and/or height + dstRec1.Width = Math.Clamp(dstRec1.Width, 1.0f, 300.0f); + dstRec1.Height = MathF.Max(dstRec1.Height, 1.0f); + dstRec2.Width = Math.Clamp(dstRec2.Width, 1.0f, 300.0f); + dstRec2.Height = MathF.Max(dstRec2.Height, 1.0f); + dstRecH.Width = MathF.Max(dstRecH.Width, 1.0f); + dstRecV.Height = MathF.Max(dstRecV.Height, 1.0f); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + // Draw the n-patches + DrawTextureNPatch(nPatchTexture, ninePatchInfo2, dstRec2, origin, 0.0f, Color.White); + DrawTextureNPatch(nPatchTexture, ninePatchInfo1, dstRec1, origin, 0.0f, Color.White); + DrawTextureNPatch(nPatchTexture, h3PatchInfo, dstRecH, origin, 0.0f, Color.White); + DrawTextureNPatch(nPatchTexture, v3PatchInfo, dstRecV, origin, 0.0f, Color.White); + + // Draw the source texture + DrawRectangleLines(5, 88, 74, 266, Color.Blue); + DrawTexture(nPatchTexture, 10, 93, Color.White); + DrawText("TEXTURE", 15, 360, 10, Color.DarkGray); + + DrawText("Move the mouse to stretch or shrink the n-patches", 10, 20, 20, Color.DarkGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(nPatchTexture); // Texture unloading + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [textures] example - npatch drawing"); SetTargetFPS(60); //--------------------------------------------------------------------------------------- + var game = new NpatchDrawing(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - mousePosition = GetMousePosition(); - - // Resize the n-patches based on mouse position - dstRec1.Width = mousePosition.X - dstRec1.X; - dstRec1.Height = mousePosition.Y - dstRec1.Y; - dstRec2.Width = mousePosition.X - dstRec2.X; - dstRec2.Height = mousePosition.Y - dstRec2.Y; - dstRecH.Width = mousePosition.X - dstRecH.X; - dstRecV.Height = mousePosition.Y - dstRecV.Y; - - // Set a minimum width and/or height - dstRec1.Width = Math.Clamp(dstRec1.Width, 1.0f, 300.0f); - dstRec1.Height = MathF.Max(dstRec1.Height, 1.0f); - dstRec2.Width = Math.Clamp(dstRec2.Width, 1.0f, 300.0f); - dstRec2.Height = MathF.Max(dstRec2.Height, 1.0f); - dstRecH.Width = MathF.Max(dstRecH.Width, 1.0f); - dstRecV.Height = MathF.Max(dstRecV.Height, 1.0f); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // Draw the n-patches - DrawTextureNPatch(nPatchTexture, ninePatchInfo2, dstRec2, origin, 0.0f, Color.White); - DrawTextureNPatch(nPatchTexture, ninePatchInfo1, dstRec1, origin, 0.0f, Color.White); - DrawTextureNPatch(nPatchTexture, h3PatchInfo, dstRecH, origin, 0.0f, Color.White); - DrawTextureNPatch(nPatchTexture, v3PatchInfo, dstRecV, origin, 0.0f, Color.White); - - // Draw the source texture - DrawRectangleLines(5, 88, 74, 266, Color.Blue); - DrawTexture(nPatchTexture, 10, 93, Color.White); - DrawText("TEXTURE", 15, 360, 10, Color.DarkGray); - - DrawText("Move the mouse to stretch or shrink the n-patches", 10, 20, 20, Color.DarkGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(nPatchTexture); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/ParticlesBlending.cs b/Examples/Textures/ParticlesBlending.cs index e011f44..257eda9 100644 --- a/Examples/Textures/ParticlesBlending.cs +++ b/Examples/Textures/ParticlesBlending.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib example - particles blending +* raylib [textures] example - particles blending * -* This example has been created using raylib 1.7 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 * -* Copyright (c) 2017 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.7, last time updated with raylib 3.5 +* +* 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) 2017-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,12 +18,19 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class ParticlesBlending +public partial class ParticlesBlending : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + public const int MaxParticles = 200; + public string Name => "Textures / Particles Blending"; + + public string Title => "raylib [textures] example - particles blending"; + // Particle structure with basic data - struct Particle + private struct Particle { public Vector2 Position; public Color Color; @@ -30,20 +41,18 @@ public class ParticlesBlending public bool Active; } - public static int Main() + private Particle[] mouseTail; + private float gravity; + private Texture2D smoke; + private BlendMode blending; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [textures] example - particles blending"); - // Particles pool, reuse them! - Particle[] mouseTail = new Particle[MaxParticles]; + mouseTail = new Particle[MaxParticles]; // Initialize particles - for (int i = 0; i < mouseTail.Length; i++) + for (var i = 0; i < mouseTail.Length; i++) { mouseTail[i].Position = new Vector2(0, 0); mouseTail[i].Color = new Color( @@ -58,113 +67,133 @@ public class ParticlesBlending mouseTail[i].Active = false; } - float gravity = 3.0f; - Texture2D smoke = LoadTexture("resources/spark_flame.png"); - BlendMode blending = BlendMode.Alpha; + gravity = 3.0f; + smoke = LoadTexture("resources/spark_flame.png"); + blending = BlendMode.Alpha; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + + // Activate one particle every frame and Update active particles + // NOTE: Particles initial position should be mouse position when activated + // NOTE: Particles fall down with gravity and rotation... and disappear after 2 seconds (alpha = 0) + // NOTE: When a particle disappears, active = false and it can be reused + for (var i = 0; i < mouseTail.Length; i++) + { + if (!mouseTail[i].Active) + { + mouseTail[i].Active = true; + mouseTail[i].Alpha = 1.0f; + mouseTail[i].Position = GetMousePosition(); + i = mouseTail.Length; + } + } + + for (var i = 0; i < mouseTail.Length; i++) + { + if (mouseTail[i].Active) + { + mouseTail[i].Position.Y += gravity / 2; + mouseTail[i].Alpha -= 0.005f; + + if (mouseTail[i].Alpha <= 0.0f) + { + mouseTail[i].Active = false; + } + + mouseTail[i].Rotation += 2.0f; + } + } + + if (IsKeyPressed(KeyboardKey.Space)) + { + if (blending == BlendMode.Alpha) + { + blending = BlendMode.Additive; + } + else + { + blending = BlendMode.Alpha; + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.DarkGray); + + BeginBlendMode(blending); + + // Draw active particles + for (var i = 0; i < mouseTail.Length; i++) + { + if (mouseTail[i].Active) + { + Rectangle source = new(0, 0, smoke.Width, smoke.Height); + Rectangle dest = new( + mouseTail[i].Position.X, + mouseTail[i].Position.Y, + smoke.Width * mouseTail[i].Size, + smoke.Height * mouseTail[i].Size + ); + Vector2 position = new( + smoke.Width * mouseTail[i].Size / 2, + smoke.Height * mouseTail[i].Size / 2 + ); + var color = Fade(mouseTail[i].Color, mouseTail[i].Alpha); + DrawTexturePro(smoke, source, dest, position, mouseTail[i].Rotation, color); + } + } + + EndBlendMode(); + + DrawText("PRESS SPACE to CHANGE BLENDING MODE", 180, 20, 20, Color.Black); + + if (blending == BlendMode.Alpha) + { + DrawText("ALPHA BLENDING", 290, screenHeight - 40, 20, Color.Black); + } + else + { + DrawText("ADDITIVE BLENDING", 280, screenHeight - 40, 20, Color.RayWhite); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(smoke); + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [textures] example - particles blending"); SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new ParticlesBlending(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - - // Activate one particle every frame and Update active particles - // NOTE: Particles initial position should be mouse position when activated - // NOTE: Particles fall down with gravity and rotation... and disappear after 2 seconds (alpha = 0) - // NOTE: When a particle disappears, active = false and it can be reused. - for (int i = 0; i < mouseTail.Length; i++) - { - if (!mouseTail[i].Active) - { - mouseTail[i].Active = true; - mouseTail[i].Alpha = 1.0f; - mouseTail[i].Position = GetMousePosition(); - i = mouseTail.Length; - } - } - - for (int i = 0; i < mouseTail.Length; i++) - { - if (mouseTail[i].Active) - { - mouseTail[i].Position.Y += gravity / 2; - mouseTail[i].Alpha -= 0.005f; - - if (mouseTail[i].Alpha <= 0.0f) - { - mouseTail[i].Active = false; - } - - mouseTail[i].Rotation += 2.0f; - } - } - - if (IsKeyPressed(KeyboardKey.Space)) - { - if (blending == BlendMode.Alpha) - { - blending = BlendMode.Additive; - } - else - { - blending = BlendMode.Alpha; - } - } - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.DarkGray); - - BeginBlendMode(blending); - - // Draw active particles - for (int i = 0; i < mouseTail.Length; i++) - { - if (mouseTail[i].Active) - { - Rectangle source = new(0, 0, smoke.Width, smoke.Height); - Rectangle dest = new( - mouseTail[i].Position.X, - mouseTail[i].Position.Y, - smoke.Width * mouseTail[i].Size, - smoke.Height * mouseTail[i].Size - ); - Vector2 position = new( - smoke.Width * mouseTail[i].Size / 2, - smoke.Height * mouseTail[i].Size / 2 - ); - Color color = ColorAlpha(mouseTail[i].Color, mouseTail[i].Alpha); - DrawTexturePro(smoke, source, dest, position, mouseTail[i].Rotation, color); - } - } - - EndBlendMode(); - - DrawText("PRESS SPACE to CHANGE BLENDING MODE", 180, 20, 20, Color.Black); - - if (blending == BlendMode.Alpha) - { - DrawText("ALPHA BLENDING", 290, screenHeight - 40, 20, Color.Black); - } - else - { - DrawText("ADDITIVE BLENDING", 280, screenHeight - 40, 20, Color.RayWhite); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(smoke); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/Polygon.cs b/Examples/Textures/Polygon.cs index b9fb586..787a751 100644 --- a/Examples/Textures/Polygon.cs +++ b/Examples/Textures/Polygon.cs @@ -1,12 +1,17 @@ /******************************************************************************************* * -* raylib [shapes] example - Draw Textured Polygon +* raylib [textures] example - polygon drawing * -* This example has been created using raylib 99.98 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★☆☆☆] 1/4 * -* Copyright (c) 2014 Ramon Santamaria (@raysan5) -* Copyright (c) 2021 Chris Camacho (codifies - bedroomcoders.co.uk) +* Example originally created with raylib 3.7, last time updated with raylib 3.7 +* +* Example contributed by Chris Camacho (@chriscamacho) 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) 2021-2025 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -15,37 +20,43 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class Polygon +public partial class Polygon : IExample { - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Textures / Textured Polygon"; + + public string Title => "raylib [textures] example - polygon drawing"; + + private Vector2[] texcoords; + private Vector2[] points; + private Vector2[] positions; + private Texture2D texture; + private float angle; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + texcoords = new[] { + new Vector2(0.75f, 0), + new Vector2(0.25f, 0), + new Vector2(0, 0.5f), + new Vector2(0, 0.75f), + new Vector2(0.25f, 1), + new Vector2(0.375f, 0.875f), + new Vector2(0.625f, 0.875f), + new Vector2(0.75f, 1), + new Vector2(1, 0.75f), + new Vector2(1, 0.5f), + // Close the poly + new Vector2(0.75f, 0) + }; - InitWindow(screenWidth, screenHeight, "raylib [textures] example - Textured Polygon"); - - Vector2[] texcoords = new[] { - new Vector2(0.75f, 0), - new Vector2(0.25f, 0), - new Vector2(0, 0.5f), - new Vector2(0, 0.75f), - new Vector2(0.25f, 1), - new Vector2(0.375f, 0.875f), - new Vector2(0.625f, 0.875f), - new Vector2(0.75f, 1), - new Vector2(1, 0.75f), - new Vector2(1, 0.5f), - // Close the poly - new Vector2(0.75f, 0) - }; - - Vector2[] points = new Vector2[11]; + points = new Vector2[11]; // Define the base poly vertices from the UV's // NOTE: They can be specified in any other way - for (int i = 0; i < points.Length; i++) + for (var i = 0; i < points.Length; i++) { points[i].X = (texcoords[i].X - 0.5f) * 256.0f; points[i].Y = (texcoords[i].Y - 0.5f) * 256.0f; @@ -53,58 +64,49 @@ public class Polygon // Define the vertices drawing position // NOTE: Initially same as points but updated every frame - Vector2[] positions = new Vector2[points.Length]; - for (int i = 0; i < positions.Length; i++) + positions = new Vector2[points.Length]; + for (var i = 0; i < positions.Length; i++) { positions[i] = points[i]; } - Texture2D texture = LoadTexture("resources/cat.png"); - float angle = 0; + texture = LoadTexture("resources/cat.png"); + angle = 0; + } - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + angle += 1; + for (var i = 0; i < positions.Length; i++) { - // Update - //---------------------------------------------------------------------------------- - // Update your variables here - //---------------------------------------------------------------------------------- - angle += 1; - for (int i = 0; i < positions.Length; i++) - { - positions[i] = Raymath.Vector2Rotate(points[i], angle * Raylib.DEG2RAD); - } - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawText("Textured Polygon", 20, 20, 20, Color.DarkGray); - Vector2 center = new(screenWidth / 2, screenHeight / 2); - DrawTexturePoly(texture, center, positions, texcoords, positions.Length, Color.White); - - EndDrawing(); - //---------------------------------------------------------------------------------- + positions[i] = Raymath.Vector2Rotate(points[i], angle * Raylib.DEG2RAD); } + //---------------------------------------------------------------------------------- + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawText("textured polygon", 20, 20, 20, Color.DarkGray); + Vector2 center = new(screenWidth / 2, screenHeight / 2); + DrawTexturePoly(texture, center, positions, texcoords, positions.Length, Color.White); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { UnloadTexture(texture); - - // De-Initialization - //-------------------------------------------------------------------------------------- - CloseWindow(); - //-------------------------------------------------------------------------------------- - - return 0; } // Draw textured polygon, defined by vertex and texture coordinates // NOTE: Polygon center must have straight line path to all points // without crossing perimeter, points must be in anticlockwise order - static void DrawTexturePoly( + private static void DrawTexturePoly( Texture2D texture, Vector2 center, Vector2[] points, @@ -114,13 +116,11 @@ public class Polygon ) { Rlgl.SetTexture(texture.Id); - - // Texturing is only supported on RL_QUADS - Rlgl.Begin(DrawMode.Quads); + Rlgl.Begin(DrawMode.Triangles); Rlgl.Color4ub(tint.R, tint.G, tint.B, tint.A); - for (int i = 0; i < pointCount - 1; i++) + for (var i = 0; i < pointCount - 1; i++) { Rlgl.TexCoord2f(0.5f, 0.5f); Rlgl.Vertex2f(center.X, center.Y); @@ -130,12 +130,37 @@ public class Polygon Rlgl.TexCoord2f(texcoords[i + 1].X, texcoords[i + 1].Y); Rlgl.Vertex2f(points[i + 1].X + center.X, points[i + 1].Y + center.Y); - - Rlgl.TexCoord2f(texcoords[i + 1].X, texcoords[i + 1].Y); - Rlgl.Vertex2f(points[i + 1].X + center.X, points[i + 1].Y + center.Y); } Rlgl.End(); Rlgl.SetTexture(0); } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [textures] example - polygon drawing"); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + var game = new Polygon(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) + { + game.Update(); + } + + game.Unload(); + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); + //-------------------------------------------------------------------------------------- + + return 0; + } } diff --git a/Examples/Textures/RawData.cs b/Examples/Textures/RawData.cs index 40470c3..0af7ebe 100644 --- a/Examples/Textures/RawData.cs +++ b/Examples/Textures/RawData.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [textures] example - Load textures from raw data +* raylib [textures] example - raw data +* +* Example complexity rating: [★★★☆] 3/4 * * NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.3, last time updated with raylib 3.5 * -* Copyright (c) 2015 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -15,98 +19,125 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class RawData +public partial class RawData : IExample { - public unsafe static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Textures / Raw Data"; + + public string Title => "raylib [textures] example - raw data"; + + private Texture2D fudesumi; + private Texture2D checkedTex; + + public unsafe void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture from raw data"); - // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) // Load RAW image data (512x512, 32bit RGBA, no file header) - Image fudesumiRaw = LoadImageRaw( + var fudesumiRaw = LoadImageRaw( "resources/fudesumi.raw", 384, 512, PixelFormat.UncompressedR8G8B8A8, 0 ); - Texture2D fudesumi = LoadTextureFromImage(fudesumiRaw); - UnloadImage(fudesumiRaw); + fudesumi = LoadTextureFromImage(fudesumiRaw); // Upload CPU (RAM) image to GPU (VRAM) + UnloadImage(fudesumiRaw); // Unload CPU (RAM) image data // Generate a checked texture by code - int width = 960; - int height = 480; + var imWidth = 960; + var imHeight = 480; - // Store pixel data - Color* pixels = (Color*)Raylib.MemAlloc((uint)(width * height * sizeof(Color))); - for (int y = 0; y < height; y++) + // Dynamic memory allocation to store pixels data (Color type) + // WARNING: Using raylib provided MemAlloc() that uses default raylib + // internal memory allocator, so this data can be freed using UnloadImage() + // that also uses raylib internal memory de-allocator + var pixels = (Color*)Raylib.MemAlloc((uint)(imWidth * imHeight * sizeof(Color))); + + for (var y = 0; y < imHeight; y++) { - for (int x = 0; x < width; x++) + for (var x = 0; x < imWidth; x++) { if (((x / 32 + y / 32) / 1) % 2 == 0) { - pixels[y * width + x] = Color.Orange; + pixels[y * imWidth + x] = Color.Orange; } else { - pixels[y * width + x] = Color.Gold; + pixels[y * imWidth + x] = Color.Gold; } } } // Load pixels data into an image structure and create texture - Image checkedIm = new Image + // NOTE: We can assign pixels directly to data because Color is R8G8B8A8 + // data structure defining that pixelformat, format must be set properly + var checkedIm = new Image { Data = pixels, - Width = width, - Height = height, + Width = imWidth, + Height = imHeight, Format = PixelFormat.UncompressedR8G8B8A8, Mipmaps = 1, }; - Texture2D checkedTex = LoadTextureFromImage(checkedIm); - Raylib.MemFree(pixels); + + checkedTex = LoadTextureFromImage(checkedIm); + Raylib.MemFree(pixels); // Unload CPU (RAM) image data (pixels) + } + + public void Update() + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + var x = screenWidth / 2 - checkedTex.Width / 2; + var y = screenHeight / 2 - checkedTex.Height / 2; + DrawTexture(checkedTex, x, y, Fade(Color.White, 0.5f)); + DrawTexture(fudesumi, 430, -30, Color.White); + + DrawText("CHECKED TEXTURE ", 84, 85, 30, Color.Brown); + DrawText("GENERATED by CODE", 72, 148, 30, Color.Brown); + DrawText("and RAW IMAGE LOADING", 46, 210, 30, Color.Brown); + + DrawText("(c) Fudesumi sprite by Eiden Marsal", 310, screenHeight - 20, 10, Color.Brown); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(fudesumi); // Texture unloading + UnloadTexture(checkedTex); // Texture unloading + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [textures] example - raw data"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //--------------------------------------------------------------------------------------- + var game = new RawData(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update your variables here - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - int x = screenWidth / 2 - checkedTex.Width / 2; - int y = screenHeight / 2 - checkedTex.Height / 2; - DrawTexture(checkedTex, x, y, ColorAlpha(Color.White, 0.5f)); - DrawTexture(fudesumi, 430, -30, Color.White); - - DrawText("CHECKED TEXTURE ", 84, 85, 30, Color.Brown); - DrawText("GENERATED by CODE", 72, 148, 30, Color.Brown); - DrawText("and RAW IMAGE LOADING", 46, 210, 30, Color.Brown); - - DrawText("(c) Fudesumi sprite by Eiden Marsal", 310, screenHeight - 20, 10, Color.Brown); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(fudesumi); - UnloadTexture(checkedTex); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/SpriteAnim.cs b/Examples/Textures/SpriteAnim.cs index 4ae3759..099a0bf 100644 --- a/Examples/Textures/SpriteAnim.cs +++ b/Examples/Textures/SpriteAnim.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [textures] example - Texture loading and drawing a part defined by a rectangle +* raylib [textures] example - sprite animation * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2014 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.3, last time updated with raylib 1.3 +* +* 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) 2014-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -15,108 +19,133 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class SpriteAnim +public partial class SpriteAnim : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + public const int MaxFrameSpeed = 15; public const int MinFrameSpeed = 1; + public string Name => "Textures / Sprite Anim"; + + public string Title => "raylib [textures] example - sprite animation"; + + private Texture2D scarfy; + private Vector2 position; + private Rectangle frameRec; + private int currentFrame; + private int framesCounter; + private int framesSpeed; + + public void Init() + { + // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) + scarfy = LoadTexture("resources/scarfy.png"); // Texture loading + + position = new(350.0f, 280.0f); + frameRec = new(0.0f, 0.0f, (float)scarfy.Width / 6, (float)scarfy.Height); + currentFrame = 0; + + framesCounter = 0; + framesSpeed = 8; // Number of spritesheet frames shown by second + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + framesCounter++; + + if (framesCounter >= (60 / framesSpeed)) + { + framesCounter = 0; + currentFrame++; + + if (currentFrame > 5) + { + currentFrame = 0; + } + + frameRec.X = (float)currentFrame * (float)scarfy.Width / 6; + } + + if (IsKeyPressed(KeyboardKey.Right)) + { + framesSpeed++; + } + else if (IsKeyPressed(KeyboardKey.Left)) + { + framesSpeed--; + } + + framesSpeed = Math.Clamp(framesSpeed, MinFrameSpeed, MaxFrameSpeed); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawTexture(scarfy, 15, 40, Color.White); + DrawRectangleLines(15, 40, scarfy.Width, scarfy.Height, Color.Lime); + DrawRectangleLines( + 15 + (int)frameRec.X, + 40 + (int)frameRec.Y, + (int)frameRec.Width, + (int)frameRec.Height, + Color.Red + ); + + DrawText("FRAME SPEED: ", 165, 210, 10, Color.DarkGray); + DrawText($"{framesSpeed:D2} FPS", 575, 210, 10, Color.DarkGray); + DrawText("PRESS RIGHT/LEFT KEYS to CHANGE SPEED!", 290, 240, 10, Color.DarkGray); + + for (var i = 0; i < MaxFrameSpeed; i++) + { + if (i < framesSpeed) + { + DrawRectangle(250 + 21 * i, 205, 20, 20, Color.Red); + } + DrawRectangleLines(250 + 21 * i, 205, 20, 20, Color.Maroon); + } + + DrawTextureRec(scarfy, frameRec, position, Color.White); // Draw part of the texture + + DrawText("(c) Scarfy sprite by Eiden Marsal", screenWidth - 200, screenHeight - 20, 10, Color.Gray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(scarfy); // Texture unloading + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [textures] example - sprite animation"); - InitWindow(screenWidth, screenHeight, "raylib [texture] example - texture rectangle"); - - // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - Texture2D scarfy = LoadTexture("resources/scarfy.png"); - - Vector2 position = new(350.0f, 280.0f); - Rectangle frameRec = new(0.0f, 0.0f, (float)scarfy.Width / 6, (float)scarfy.Height); - int currentFrame = 0; - - int framesCounter = 0; - - // Number of spritesheet frames shown by second - int framesSpeed = 8; - - SetTargetFPS(60); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new SpriteAnim(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - framesCounter++; - - if (framesCounter >= (60 / framesSpeed)) - { - framesCounter = 0; - currentFrame++; - - if (currentFrame > 5) - { - currentFrame = 0; - } - - frameRec.X = (float)currentFrame * (float)scarfy.Width / 6; - } - - if (IsKeyPressed(KeyboardKey.Right)) - { - framesSpeed++; - } - else if (IsKeyPressed(KeyboardKey.Left)) - { - framesSpeed--; - } - - framesSpeed = Math.Clamp(framesSpeed, MinFrameSpeed, MaxFrameSpeed); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawTexture(scarfy, 15, 40, Color.White); - DrawRectangleLines(15, 40, scarfy.Width, scarfy.Height, Color.Lime); - DrawRectangleLines( - 15 + (int)frameRec.X, - 40 + (int)frameRec.Y, - (int)frameRec.Width, - (int)frameRec.Height, - Color.Red - ); - - DrawText("FRAME SPEED: ", 165, 210, 10, Color.DarkGray); - DrawText($"{framesSpeed:2F} FPS", 575, 210, 10, Color.DarkGray); - DrawText("PRESS RIGHT/LEFT KEYS to CHANGE SPEED!", 290, 240, 10, Color.DarkGray); - - for (int i = 0; i < MaxFrameSpeed; i++) - { - if (i < framesSpeed) - { - DrawRectangle(250 + 21 * i, 205, 20, 20, Color.Red); - } - DrawRectangleLines(250 + 21 * i, 205, 20, 20, Color.Maroon); - } - - // Draw part of the texture - DrawTextureRec(scarfy, frameRec, position, Color.White); - DrawText("(c) Scarfy sprite by Eiden Marsal", screenWidth - 200, screenHeight - 20, 10, Color.Gray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(scarfy); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/SpriteButton.cs b/Examples/Textures/SpriteButton.cs index c2de041..eb01ebf 100644 --- a/Examples/Textures/SpriteButton.cs +++ b/Examples/Textures/SpriteButton.cs @@ -2,10 +2,14 @@ * * raylib [textures] example - sprite button * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2019 Ramon Santamaria (@raysan5) +* Example originally created with raylib 2.5, last time updated with raylib 2.5 +* +* 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) 2019-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,31 +18,40 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class SpriteButton +public partial class SpriteButton : IExample { // Number of frames (rectangles) for the button sprite texture public const int NumFrames = 3; - public static int Main() + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Textures / Sprite Button"; + + public string Title => "raylib [textures] example - sprite button"; + + private Sound fxButton; + private Texture2D button; + private int frameHeight; + private Rectangle sourceRec; + private Rectangle btnBounds; + private int btnState; + private bool btnAction; + private Vector2 mousePoint; + + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitAudioDevice(); // Initialize audio device - InitWindow(screenWidth, screenHeight, "raylib [textures] example - sprite button"); - - InitAudioDevice(); - - Sound fxButton = LoadSound("resources/audio/buttonfx.wav"); - Texture2D button = LoadTexture("resources/button.png"); + fxButton = LoadSound("resources/audio/buttonfx.wav"); // Load button sound + button = LoadTexture("resources/button.png"); // Load button texture // Define frame rectangle for drawing - int frameHeight = button.Height / NumFrames; - Rectangle sourceRec = new(0, 0, button.Width, frameHeight); + frameHeight = button.Height / NumFrames; + sourceRec = new(0, 0, button.Width, frameHeight); // Define button bounds on screen - Rectangle btnBounds = new( + btnBounds = new( screenWidth / 2 - button.Width / 2, screenHeight / 2 - button.Height / NumFrames / 2, button.Width, @@ -46,75 +59,95 @@ public class SpriteButton ); // Button state: 0-NORMAL, 1-MOUSE_HOVER, 2-PRESSED - int btnState = 0; + btnState = 0; // Button action should be activated - bool btnAction = false; + btnAction = false; - Vector2 mousePoint = new(0.0f, 0.0f); + mousePoint = new(0.0f, 0.0f); + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + mousePoint = GetMousePosition(); + btnAction = false; + + // Check button state + if (CheckCollisionPointRec(mousePoint, btnBounds)) + { + if (IsMouseButtonDown(MouseButton.Left)) + { + btnState = 2; + } + else + { + btnState = 1; + } + + if (IsMouseButtonReleased(MouseButton.Left)) + { + btnAction = true; + } + } + else + { + btnState = 0; + } + + if (btnAction) + { + PlaySound(fxButton); + // TODO: Any desired action + } + + // Calculate button frame rectangle to draw depending on button state + sourceRec.Y = btnState * frameHeight; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawTextureRec(button, sourceRec, new Vector2(btnBounds.X, btnBounds.Y), Color.White); // Draw button frame + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(button); // Unload button texture + UnloadSound(fxButton); // Unload sound + + CloseAudioDevice(); // Close audio device + } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib [textures] example - sprite button"); SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new SpriteButton(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - mousePoint = GetMousePosition(); - btnAction = false; - - // Check button state - if (CheckCollisionPointRec(mousePoint, btnBounds)) - { - if (IsMouseButtonDown(MouseButton.Left)) - { - btnState = 2; - } - else - { - btnState = 1; - } - - if (IsMouseButtonReleased(MouseButton.Left)) - { - btnAction = true; - } - } - else - { - btnState = 0; - } - - if (btnAction) - { - PlaySound(fxButton); - // TODO: Any desired action - } - - // Calculate button frame rectangle to draw depending on button state - sourceRec.Y = btnState * frameHeight; - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // Draw button frame - DrawTextureRec(button, sourceRec, new Vector2(btnBounds.X, btnBounds.Y), Color.White); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(button); - UnloadSound(fxButton); - - CloseAudioDevice(); - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/SpriteExplosion.cs b/Examples/Textures/SpriteExplosion.cs index f748686..fd7523a 100644 --- a/Examples/Textures/SpriteExplosion.cs +++ b/Examples/Textures/SpriteExplosion.cs @@ -2,10 +2,14 @@ * * raylib [textures] example - sprite explosion * -* This example has been created using raylib 2.5 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★☆☆] 2/4 * -* Copyright (c) 2019 Anata and Ramon Santamaria (@raysan5) +* Example originally created with raylib 2.5, last time updated with raylib 3.5 +* +* 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) 2019-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,117 +18,144 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class SpriteExplosion +public partial class SpriteExplosion : IExample { - const int NumFramesPerLine = 5; - const int NumLines = 5; + private const int NumFramesPerLine = 5; + private const int NumLines = 5; + + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Textures / Sprite Explosion"; + + public string Title => "raylib [textures] example - sprite explosion"; + + private Sound fxBoom; + private Texture2D explosion; + private int frameWidth; + private int frameHeight; + private int currentFrame; + private int currentLine; + private Rectangle frameRec; + private Vector2 position; + private bool active; + private int framesCounter; + + public void Init() + { + InitAudioDevice(); + + // Load explosion sound + fxBoom = LoadSound("resources/audio/boom.wav"); + + // Load explosion texture + explosion = LoadTexture("resources/explosion.png"); + + // Init variables for animation + frameWidth = explosion.Width / NumFramesPerLine; // Sprite one frame rectangle width + frameHeight = explosion.Height / NumLines; // Sprite one frame rectangle height + currentFrame = 0; + currentLine = 0; + + frameRec = new(0, 0, frameWidth, frameHeight); + position = new(0.0f, 0.0f); + + active = false; + framesCounter = 0; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + + // Check for mouse button pressed and activate explosion (if not active) + if (IsMouseButtonPressed(MouseButton.Left) && !active) + { + position = GetMousePosition(); + active = true; + + position.X -= frameWidth / 2; + position.Y -= frameHeight / 2; + + PlaySound(fxBoom); + } + + // Compute explosion animation frames + if (active) + { + framesCounter++; + + if (framesCounter > 2) + { + currentFrame++; + + if (currentFrame >= NumFramesPerLine) + { + currentFrame = 0; + currentLine++; + + if (currentLine >= NumLines) + { + currentLine = 0; + active = false; + } + } + + framesCounter = 0; + } + } + + frameRec.X = frameWidth * currentFrame; + frameRec.Y = frameHeight * currentLine; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + // Draw explosion required frame rectangle + if (active) + { + DrawTextureRec(explosion, frameRec, position, Color.White); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(explosion); // Unload texture + UnloadSound(fxBoom); // Unload sound + + CloseAudioDevice(); + } public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [textures] example - sprite explosion"); - InitAudioDevice(); - // Load explosion sound - Sound fxBoom = LoadSound("resources/audio/boom.wav"); - - // Load explosion texture - Texture2D explosion = LoadTexture("resources/explosion.png"); - - // Init variables for animation - - // Sprite one frame rectangle width - int frameWidth = explosion.Width / NumFramesPerLine; - - // Sprite one frame rectangle height - int frameHeight = explosion.Height / NumLines; - - int currentFrame = 0; - int currentLine = 0; - - Rectangle frameRec = new(0, 0, frameWidth, frameHeight); - Vector2 position = new(0.0f, 0.0f); - - bool active = false; - int framesCounter = 0; - - SetTargetFPS(120); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + var game = new SpriteExplosion(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - - // Check for mouse button pressed and activate explosion (if not active) - if (IsMouseButtonPressed(MouseButton.Left) && !active) - { - position = GetMousePosition(); - active = true; - - position.X -= frameWidth / 2; - position.Y -= frameHeight / 2; - - PlaySound(fxBoom); - } - - // Compute explosion animation frames - if (active) - { - framesCounter++; - - if (framesCounter > 2) - { - currentFrame++; - - if (currentFrame >= NumFramesPerLine) - { - currentFrame = 0; - currentLine++; - - if (currentLine >= NumLines) - { - currentLine = 0; - active = false; - } - } - - framesCounter = 0; - } - } - - frameRec.X = frameWidth * currentFrame; - frameRec.Y = frameHeight * currentLine; - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // Draw explosion required frame rectangle - if (active) - { - DrawTextureRec(explosion, frameRec, position, Color.White); - } - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(explosion); - UnloadSound(fxBoom); - - CloseAudioDevice(); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/SrcRecDstRec.cs b/Examples/Textures/SrcRecDstRec.cs index dafc610..ea97511 100644 --- a/Examples/Textures/SrcRecDstRec.cs +++ b/Examples/Textures/SrcRecDstRec.cs @@ -1,11 +1,15 @@ /******************************************************************************************* * -* raylib [textures] example - Texture source and destination rectangles +* raylib [textures] example - srcrec dstrec * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example complexity rating: [★★★☆] 3/4 * -* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* Example originally created with raylib 1.3, last time updated with raylib 1.3 +* +* 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -14,72 +18,98 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class SrcRecDstRec +public partial class SrcRecDstRec : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Textures / Src and Dst Rectangles"; + + public string Title => "raylib [textures] example - srcrec dstrec"; + + private Texture2D scarfy; + private Rectangle sourceRec; + private Rectangle destRec; + private Vector2 origin; + private int rotation; + + public void Init() + { + // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) + + scarfy = LoadTexture("resources/scarfy.png"); // Texture loading + + var frameWidth = scarfy.Width / 6; + var frameHeight = scarfy.Height; + + // Source rectangle (part of the texture to use for drawing) + sourceRec = new(0, 0, frameWidth, frameHeight); + + // Destination rectangle (screen rectangle where drawing part of texture) + destRec = new(screenWidth / 2, screenHeight / 2, frameWidth * 2, frameHeight * 2); + + // Origin of the texture (rotation/scale point), it's relative to destination rectangle size + origin = new(frameWidth, frameHeight); + + rotation = 0; + } + + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + rotation++; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + // NOTE: Using DrawTexturePro() we can easily rotate and scale the part of the texture we draw + // sourceRec defines the part of the texture we use for drawing + // destRec defines the rectangle where our texture part will fit (scaling it to fit) + // origin defines the point of the texture used as reference for rotation and scaling + // rotation defines the texture rotation (using origin as rotation point) + DrawTexturePro(scarfy, sourceRec, destRec, origin, rotation, Color.White); + + DrawLine((int)destRec.X, 0, (int)destRec.X, screenHeight, Color.Gray); + DrawLine(0, (int)destRec.Y, screenWidth, (int)destRec.Y, Color.Gray); + + DrawText("(c) Scarfy sprite by Eiden Marsal", screenWidth - 200, screenHeight - 20, 10, Color.Gray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(scarfy); // Texture unloading + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - string title = "raylib [textures] examples - texture source and destination rectangles"; - InitWindow(screenWidth, screenHeight, title); - - // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - Texture2D scarfy = LoadTexture("resources/scarfy.png"); - - int frameWidth = scarfy.Width / 6; - int frameHeight = scarfy.Height; - - // NOTE: Source rectangle (part of the texture to use for drawing) - Rectangle sourceRec = new(0, 0, frameWidth, frameHeight); - - // NOTE: Destination rectangle (screen rectangle where drawing part of texture) - Rectangle destRec = new(screenWidth / 2, screenHeight / 2, frameWidth * 2, frameHeight * 2); - - // NOTE: Origin of the texture (rotation/scale point), it's relative to destination rectangle size - Vector2 origin = new(frameWidth, frameHeight); - - int rotation = 0; + InitWindow(screenWidth, screenHeight, "raylib [textures] example - srcrec dstrec"); SetTargetFPS(60); //-------------------------------------------------------------------------------------- + var game = new SrcRecDstRec(); + game.Init(); + // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - rotation++; - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - // NOTE: Using DrawTexturePro() we can easily rotate and scale the part of the texture we draw - // sourceRec defines the part of the texture we use for drawing - // destRec defines the rectangle where our texture part will fit (scaling it to fit) - // origin defines the point of the texture used as reference for rotation and scaling - // rotation defines the texture rotation (using origin as rotation point) - DrawTexturePro(scarfy, sourceRec, destRec, origin, rotation, Color.White); - - DrawLine((int)destRec.X, 0, (int)destRec.X, screenHeight, Color.Gray); - DrawLine(0, (int)destRec.Y, screenWidth, (int)destRec.Y, Color.Gray); - - DrawText("(c) Scarfy sprite by Eiden Marsal", screenWidth - 200, screenHeight - 20, 10, Color.Gray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(scarfy); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Textures/TexturedCurve.cs b/Examples/Textures/TexturedCurve.cs index 4ee8def..d2d1118 100644 --- a/Examples/Textures/TexturedCurve.cs +++ b/Examples/Textures/TexturedCurve.cs @@ -1,11 +1,37 @@ +/******************************************************************************************* +* +* raylib [textures] example - textured curve +* +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 4.5, last time updated with raylib 4.5 +* +* Example contributed by Jeffery Myers (@JeffM2501) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2022-2025 Jeffery Myers (@JeffM2501) and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + using System; using System.Numerics; using static Raylib_cs.Raylib; namespace Examples.Textures; -public unsafe class TexturedCurve +public unsafe partial class TexturedCurve : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Textures / Textured Curve"; + + public string Title => "raylib [textures] example - textured curve"; + + public ConfigFlags ConfigFlags => ConfigFlags.VSyncHint | ConfigFlags.Msaa4xHint; + public class CurvePoint { public Vector2 value; @@ -17,26 +43,18 @@ public unsafe class TexturedCurve public static implicit operator Vector2(CurvePoint v) => v.value; } - static Texture2D texRoad; - static bool showCurve = false; - static float curveWidth = 50; - static int curveSegments = 24; - static CurvePoint curveStartPosition; - static CurvePoint curveStartPositionTangent; - static CurvePoint curveEndPosition; - static CurvePoint curveEndPositionTangent; - static CurvePoint curveSelectedPoint; + private Texture2D texRoad; + private bool showCurve; + private float curveWidth; + private int curveSegments; + private CurvePoint curveStartPosition; + private CurvePoint curveStartPositionTangent; + private CurvePoint curveEndPosition; + private CurvePoint curveEndPositionTangent; + private CurvePoint curveSelectedPoint; - public static int Main() + public void Init() { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - SetConfigFlags(ConfigFlags.VSyncHint | ConfigFlags.Msaa4xHint); - InitWindow(screenWidth, screenHeight, "raylib [textures] examples - textured curve"); - // Load the road texture texRoad = LoadTexture("resources/road.png"); SetTextureFilter(texRoad, TextureFilter.Bilinear); @@ -48,45 +66,42 @@ public unsafe class TexturedCurve curveEndPosition = new Vector2(700, 350); curveEndPositionTangent = new Vector2(600, 100); - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) - { - // Update - //---------------------------------------------------------------------------------- - UpdateCurve(); - UpdateOptions(); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - DrawTexturedCurve(); - DrawCurve(); - - DrawText("Drag points to move curve, press SPACE to show/hide base curve", 10, 10, 10, Color.DarkGray); - DrawText($"Curve width: {curveWidth} (Use + and - to adjust)", 10, 30, 10, Color.DarkGray); - DrawText($"Curve segments: {curveSegments} (Use LEFT and RIGHT to adjust)", 10, 50, 10, Color.DarkGray); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - UnloadTexture(texRoad); - - CloseWindow(); - //-------------------------------------------------------------------------------------- - - return 0; + showCurve = false; + curveWidth = 50; + curveSegments = 24; + curveSelectedPoint = null; } - static void DrawCurve() + public void Update() + { + // Update + //---------------------------------------------------------------------------------- + UpdateCurve(); + UpdateOptions(); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + DrawTexturedCurve(); + DrawCurve(); + + DrawText("Drag points to move curve, press SPACE to show/hide base curve", 10, 10, 10, Color.DarkGray); + DrawText($"Curve width: {curveWidth} (Use + and - to adjust)", 10, 30, 10, Color.DarkGray); + DrawText($"Curve segments: {curveSegments} (Use LEFT and RIGHT to adjust)", 10, 50, 10, Color.DarkGray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texRoad); + } + + private void DrawCurve() { if (showCurve) { @@ -104,7 +119,7 @@ public unsafe class TexturedCurve DrawLineV(curveStartPosition, curveStartPositionTangent, Color.SkyBlue); DrawLineV(curveStartPositionTangent, curveEndPositionTangent, Fade(Color.LightGray, 0.4f)); DrawLineV(curveEndPosition, curveEndPositionTangent, Color.Purple); - Vector2 mouse = GetMousePosition(); + var mouse = GetMousePosition(); if (CheckCollisionPointCircle(mouse, curveStartPosition, 6)) { @@ -131,11 +146,12 @@ public unsafe class TexturedCurve DrawCircleV(curveEndPositionTangent, 5, Color.DarkGreen); } - static void UpdateCurve() + private void UpdateCurve() { // If the mouse is not down, we are not editing the curve so clear the selection if (!IsMouseButtonDown(MouseButton.Left)) { + curveSelectedPoint = null; return; } @@ -146,7 +162,7 @@ public unsafe class TexturedCurve } // The mouse is down, and nothing was selected, so see if anything was picked - Vector2 mouse = GetMousePosition(); + var mouse = GetMousePosition(); if (CheckCollisionPointCircle(mouse, curveStartPosition, 6)) { @@ -166,28 +182,28 @@ public unsafe class TexturedCurve } } - static void DrawTexturedCurve() + private void DrawTexturedCurve() { - float step = 1.0f / curveSegments; + var step = 1.0f / curveSegments; Vector2 previous = curveStartPosition; - Vector2 previousTangent = Vector2.Zero; + var previousTangent = Vector2.Zero; float previousV = 0; // We can't compute a tangent for the first point, so we need to reuse the tangent from the first segment - bool tangentSet = false; + var tangentSet = false; - Vector2 current = Vector2.Zero; - float t = 0.0f; + var current = Vector2.Zero; + var t = 0.0f; - for (int i = 1; i <= curveSegments; i++) + for (var i = 1; i <= curveSegments; i++) { // Segment the curve t = step * i; - float a = MathF.Pow(1 - t, 3); - float b = 3 * MathF.Pow(1 - t, 2) * t; - float c = 3 * (1 - t) * MathF.Pow(t, 2); - float d = MathF.Pow(t, 3); + var a = MathF.Pow(1 - t, 3); + var b = 3 * MathF.Pow(1 - t, 2) * t; + var c = 3 * (1 - t) * MathF.Pow(t, 2); + var d = MathF.Pow(t, 3); // Compute the endpoint for this segment current.Y = a * curveStartPosition.Y + b * curveStartPositionTangent.Y; @@ -199,10 +215,10 @@ public unsafe class TexturedCurve Vector2 delta = new(current.X - previous.X, current.Y - previous.Y); // The right hand normal to the delta vector - Vector2 normal = Vector2.Normalize(new Vector2(-delta.Y, delta.X)); + var normal = Vector2.Normalize(new Vector2(-delta.Y, delta.X)); - // The v teXture coordinate of the segment (add up the length of all the segments so far) - float v = previousV + delta.Length(); + // The v texture coordinate of the segment (add up the length of all the segments so far) + var v = previousV + delta.Length() / (texRoad.Height * 2); // Make sure the start point has a normal if (!tangentSet) @@ -211,12 +227,12 @@ public unsafe class TexturedCurve tangentSet = true; } - // EXtend out the normals from the previous and current points to get the quad for this segment - Vector2 prevPosNormal = previous + (previousTangent * curveWidth); - Vector2 prevNegNormal = previous + (previousTangent * -curveWidth); + // Extend out the normals from the previous and current points to get the quad for this segment + var prevPosNormal = previous + (previousTangent * curveWidth); + var prevNegNormal = previous + (previousTangent * -curveWidth); - Vector2 currentPosNormal = current + (normal * curveWidth); - Vector2 currentNegNormal = current + (normal * -curveWidth); + var currentPosNormal = current + (normal * curveWidth); + var currentNegNormal = current + (normal * -curveWidth); // Draw the segment as a quad Rlgl.SetTexture(texRoad.Id); @@ -239,21 +255,21 @@ public unsafe class TexturedCurve Rlgl.End(); - // The current step is the start of the neXt step + // The current step is the start of the next step previous = current; previousTangent = normal; previousV = v; } } - static void UpdateOptions() + private void UpdateOptions() { if (IsKeyPressed(KeyboardKey.Space)) { showCurve = !showCurve; } - // Update with + // Update width if (IsKeyPressed(KeyboardKey.Equal)) { curveWidth += 2; @@ -282,4 +298,33 @@ public unsafe class TexturedCurve curveSegments = 2; } } + + public static int Main() + { + // Initialization + //-------------------------------------------------------------------------------------- + SetConfigFlags(ConfigFlags.VSyncHint | ConfigFlags.Msaa4xHint); + InitWindow(screenWidth, screenHeight, "raylib [textures] example - textured curve"); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + var game = new TexturedCurve(); + game.Init(); + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + game.Update(); + } + + game.Unload(); + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; + } } diff --git a/Examples/Textures/ToImage.cs b/Examples/Textures/ToImage.cs index a0dbc32..9f71118 100644 --- a/Examples/Textures/ToImage.cs +++ b/Examples/Textures/ToImage.cs @@ -1,13 +1,17 @@ /******************************************************************************************* * -* raylib [textures] example - Retrieve image data from texture: GetTextureData() +* raylib [textures] example - to image +* +* Example complexity rating: [★☆☆☆] 1/4 * * NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) * -* This example has been created using raylib 1.3 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* Example originally created with raylib 1.3, last time updated with raylib 4.0 * -* Copyright (c) 2015 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) 2015-2025 Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -15,58 +19,77 @@ using static Raylib_cs.Raylib; namespace Examples.Textures; -public class ToImage +public partial class ToImage : IExample { + private const int screenWidth = 800; + private const int screenHeight = 450; + + public string Name => "Textures / Texture to Image"; + + public string Title => "raylib [textures] example - to image"; + + private Texture2D texture; + + public void Init() + { + // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) + + var image = LoadImage("resources/raylib-cs_logo.png"); // Load image data into CPU memory (RAM) + texture = LoadTextureFromImage(image); // Image converted to texture, GPU memory (RAM -> VRAM) + UnloadImage(image); // Unload image data from CPU memory (RAM) + + image = LoadImageFromTexture(texture); // Load image from GPU texture (VRAM -> RAM) + UnloadTexture(texture); // Unload texture from GPU memory (VRAM) + + texture = LoadTextureFromImage(image); // Recreate texture from retrieved image data (RAM -> VRAM) + UnloadImage(image); // Unload retrieved image data from CPU memory (RAM) + } + + public void Update() + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RayWhite); + + var x = screenWidth / 2 - texture.Width / 2; + var y = screenHeight / 2 - texture.Height / 2; + DrawTexture(texture, x, y, Color.White); + + DrawText("this IS a texture loaded from an image!", 300, 370, 10, Color.Gray); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + public void Unload() + { + UnloadTexture(texture); // Texture unloading + } + public static int Main() { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; + InitWindow(screenWidth, screenHeight, "raylib [textures] example - to image"); - InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture to image"); + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- - // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - - Image image = LoadImage("resources/raylib-cs_logo.png"); - Texture2D texture = LoadTextureFromImage(image); - UnloadImage(image); - - image = LoadImageFromTexture(texture); - UnloadTexture(texture); - - texture = LoadTextureFromImage(image); - UnloadImage(image); - //--------------------------------------------------------------------------------------- + var game = new ToImage(); + game.Init(); // Main game loop - while (!WindowShouldClose()) + while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update - //---------------------------------------------------------------------------------- - // TODO: Update your variables here - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - ClearBackground(Color.RayWhite); - - int x = screenWidth / 2 - texture.Width / 2; - int y = screenHeight / 2 - texture.Height / 2; - DrawTexture(texture, x, y, Color.White); - - DrawText("this IS a texture loaded from an image!", 300, 370, 10, Color.Gray); - - EndDrawing(); - //---------------------------------------------------------------------------------- + game.Update(); } + game.Unload(); + // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texture); - - CloseWindow(); + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/Examples/Web/Host.cs b/Examples/Web/Host.cs new file mode 100644 index 0000000..c5ff082 --- /dev/null +++ b/Examples/Web/Host.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices.JavaScript; + +namespace Examples.Web; + +/// +/// Browser host: owns the single raylib window and the "current" example. The frame loop is +/// driven from JavaScript (main.js, via requestAnimationFrame) since a blocking C# loop would +/// freeze the page. Methods marked [JSExport] are called from main.js. +/// +public partial class Host +{ + public const int screenWidth = 800; + public const int screenHeight = 450; + + // Examples discovered from the assembly, minus desktop-only ones (see ExampleRegistry). + private static readonly List _examples = new(ExampleRegistry.BrowserExamples); + + private static IExample _current; + + public static void Main() + { + InitWindow(screenWidth, screenHeight, "raylib-cs web examples"); + SetTargetFPS(60); + + SetExample(_examples[0].Name); + } + + /// Render one frame of the current example (called every requestAnimationFrame tick). + [JSExport] + public static void UpdateFrame() + { + try + { + _current?.Update(); + } + catch (Exception ex) + { + // Don't let one misbehaving example kill the whole page; log and stop driving it. + Console.WriteLine($"[Examples.Web] '{_current?.Name}' threw during Update: {ex}"); + _current = null; + } + } + + /// Switch the active example by name (called from the nav dropdown). + [JSExport] + public static void SetExample(string name) + { + var next = _examples.Find(e => e.Name == name); + if (next == null) + { + return; + } + + try + { + _current?.Unload(); + _current = next; + _current.Init(); + } + catch (Exception ex) + { + Console.WriteLine($"[Examples.Web] '{next.Name}' failed to initialize: {ex}"); + _current = null; + } + } + + /// Newline-separated example names, used to populate the nav dropdown. + [JSExport] + public static string GetExampleNames() + { + return string.Join("\n", _examples.ConvertAll(e => e.Name)); + } + + /// + /// Map browser CSS mouse coordinates to the fixed 800x450 framebuffer when the canvas + /// is CSS-scaled (integer/fit/native display modes in main.js). + /// + [JSExport] + public static void SetMouseScaleFromDisplay(float scaleX, float scaleY) + { + SetMouseOffset(0, 0); + SetMouseScale(scaleX, scaleY); + } +} diff --git a/Examples/Web/README.md b/Examples/Web/README.md new file mode 100644 index 0000000..b11d7eb --- /dev/null +++ b/Examples/Web/README.md @@ -0,0 +1,118 @@ +# Examples/Web — raylib-cs in the browser (WebAssembly) + +A small browser app that runs raylib examples in the browser via WebAssembly, with a dropdown to +switch between them. It exists to **prove the `Raylib-cs` NuGet package's `browser-wasm` support +works end-to-end**: the package's `buildTransitive/Raylib-cs.targets` automatically links the +shipped `runtimes/browser-wasm/native/raylib.a` (and adds `-sUSE_GLFW=3`) into the .NET wasm +runtime. The browser host lives in `Examples/Web`, while `Examples.csproj` remains the single +examples project. + +The browser-wasm configuration is enabled only when publishing `Examples` with +`RuntimeIdentifier=browser-wasm`, so normal solution builds do not require the `wasm-tools` +workload. + +## Prerequisites + +- .NET 10 SDK +- `dotnet workload install wasm-tools` +- The `Raylib-cs` package matching `$(RaylibCsVersion)` (see `Directory.Build.props`) — from + nuget.org, or built locally into the repo's `./nuget` feed (`dotnet pack Raylib-cs -c Release --output nuget`). + +## Build + +```bash +dotnet publish Examples -f net10.0 -r browser-wasm -c Release +# -> Examples/bin/Release/net10.0/browser-wasm/AppBundle/ +``` + +### Toolchain caveat + +If the link step fails with: + +``` +wasm-opt: Unknown option '--enable-bulk-memory-opt' +``` + +your installed `wasm-tools` workload (Binaryen) is out of sync with the SDK — emscripten passes a +feature flag the bundled `wasm-opt` doesn't understand (commonly caused by a stale workload band +or a conflicting system EMSDK on PATH/`$EMSDK`). Proper fix: + +```bash +dotnet workload update +``` + +`Examples.csproj` defaults browser-wasm publishes to unoptimized native linking so the standard +publish command works on affected local toolchains. A fully optimized publish can override those +MSBuild properties after updating the workload/toolchain. + +## Run + +WebAssembly must be served over HTTP (not `file://`): + +```bash +dotnet serve -d Examples/bin/Release/net10.0/browser-wasm/AppBundle # dotnet tool install -g dotnet-serve +# or: npx http-server Examples/bin/Release/net10.0/browser-wasm/AppBundle +``` + +Open the printed URL and use the **Example** dropdown to switch examples. + +## Canvas scaling modes + +The browser host keeps raylib's internal render buffer fixed to `800x450` and applies display +scaling in CSS. Use the **Scale** dropdown (or `?scale=` query param) to choose behavior: + +- `native` (default): exact `800x450` CSS pixels (1x), no resizing. +- `integer`: largest whole-number multiple that fits the viewport, centered with letterboxing. + Best for pixel-perfect presentation. +- `fit`: fills available viewport while preserving aspect ratio (can be fractional, less crisp on + some DPI/zoom combinations). + +Scaling is computed in **device pixels first** and then converted back to CSS pixels using current +`devicePixelRatio`, which decouples the modes from needing a specific browser zoom level on +125%/150% OS scale displays. + +`main.js` also shows runtime diagnostics in the status bar (`DPR`, CSS size, backing size, scale) +to help debug OS scaling / browser zoom behavior across monitors. + +### Manual validation matrix (expected behavior) + +- OS scale `100%`, browser zoom `100%`: `integer` should appear crisp and stable (`Scale 1.00` or + higher if viewport allows). +- OS scale `125%` and `150%`, browser zoom `100%`: `integer` should stay crisp (no subpixel CSS + scaling); perceived physical size changes with OS scale are expected. +- Browser zoom `80%`, `100%`, `125%`: `integer` should continue using whole-number scaling; + `fit` may show softening at non-integer effective scales. +- `native` mode should always report `CSS 800x450` and `Scale 1.00`. +- During window resize, there should be no frame-by-frame jitter because scale updates run on + resize/mode changes, not per animation frame. + +## How it works + +A browser can't run raylib's blocking `while (!WindowShouldClose())` loop (it would freeze the +page), so frames are driven from JavaScript: + +- `Host.Main()` calls `InitWindow` once and selects the default example (`main.js` runs it via + `runMain()`). +- `main.js` binds the page `` to the runtime, then calls `Host.UpdateFrame()` every + `requestAnimationFrame` tick. `UpdateFrame`, `SetExample`, and `GetExampleNames` are `[JSExport]`. +- Each base example implements `IExample` (`Init` / `Update` / `Unload`) in `#if BROWSER` partials. + The host owns the window; + examples never call `InitWindow`/`CloseWindow`. + +## Adding more examples + +For a new desktop example (`../Core/...`, `../Shaders/...`, etc.), add a browser partial +(`*.Browser.cs`) that implements `IExample` and mirrors the split of its monolithic `Main`: + +``` +Main() { ; while(!WindowShouldClose()){ } } + -> Init() { } // loop-spanning locals become fields + Update(){ } // keep BeginDrawing..EndDrawing + Unload(){ } +``` + +Then add `new YourExample()` to the `Examples` list in `Host.cs`. + +Examples that load files (`resources/...`) also need those assets in the wasm virtual filesystem. +`Examples.csproj` bundles `../resources/` into the browser app when publishing with +`RuntimeIdentifier=browser-wasm`. diff --git a/Examples/Web/index.html b/Examples/Web/index.html new file mode 100644 index 0000000..18b86fc --- /dev/null +++ b/Examples/Web/index.html @@ -0,0 +1,70 @@ + + + + + + raylib-cs — browser examples + + + + + + +

raylib-cs running in the browser (WebAssembly)

+
+ + + + +
+
+ +
+
Initializing .NET + raylib…
+ + + + diff --git a/Examples/Web/main.js b/Examples/Web/main.js new file mode 100644 index 0000000..99bf3a1 --- /dev/null +++ b/Examples/Web/main.js @@ -0,0 +1,157 @@ +import { dotnet } from './_framework/dotnet.js' +import { + CANVAS_WIDTH, + CANVAS_HEIGHT, + normalizeScaleMode, + computeDisplaySize +} from './scaleUtils.js' + +const status = document.getElementById('status'); +const select = document.getElementById('examples'); +const canvas = document.getElementById('canvas'); +const viewport = document.getElementById('viewport'); +const scaleModeSelect = document.getElementById('scaleMode'); + +function readScaleModeFromQuery() { + const params = new URLSearchParams(window.location.search); + return normalizeScaleMode(params.get('scale')); +} + +function writeScaleModeToQuery(mode) { + const params = new URLSearchParams(window.location.search); + params.set('scale', mode); + const query = params.toString(); + const nextUrl = query.length > 0 ? `${window.location.pathname}?${query}` : window.location.pathname; + window.history.replaceState(null, '', nextUrl); +} + +let scaleMode = readScaleModeFromQuery(); +let Host = null; + +function ensureBackingBufferSize() { + if (canvas.width !== CANVAS_WIDTH) { + canvas.width = CANVAS_WIDTH; + } + + if (canvas.height !== CANVAS_HEIGHT) { + canvas.height = CANVAS_HEIGHT; + } +} + +function updateScaleDiagnostics(displayScale, cssWidth, cssHeight, deviceWidth, deviceHeight, dpr) { + status.textContent = + `Running. Example: ${scaleMode} scaling | DPR ${dpr.toFixed(2)} | ` + + `CSS ${cssWidth.toFixed(2)}x${cssHeight.toFixed(2)} | ` + + `Device ${deviceWidth}x${deviceHeight} | Backing ${canvas.width}x${canvas.height} | ` + + `Scale ${displayScale.toFixed(2)}`; +} + +function syncMouseScale() { + if (!Host) { + return; + } + + const cssWidth = canvas.clientWidth; + const cssHeight = canvas.clientHeight; + if (cssWidth <= 0 || cssHeight <= 0) { + return; + } + + // Map CSS/display mouse coords to the fixed backing buffer (canvas.width/height). + const scaleX = canvas.width / cssWidth; + const scaleY = canvas.height / cssHeight; + Host.SetMouseScaleFromDisplay(scaleX, scaleY); +} + +function applyDisplayScale() { + ensureBackingBufferSize(); + + const viewportWidth = viewport.clientWidth || CANVAS_WIDTH; + const viewportHeight = viewport.clientHeight || CANVAS_HEIGHT; + const dprRaw = window.devicePixelRatio ?? 1; + const display = computeDisplaySize(scaleMode, viewportWidth, viewportHeight, dprRaw); + + canvas.style.width = `${display.cssWidth}px`; + canvas.style.height = `${display.cssHeight}px`; + + // Center canvas inside viewport, leaving letterboxing around it. + viewport.style.justifyContent = 'center'; + viewport.style.alignItems = 'center'; + + syncMouseScale(); + + updateScaleDiagnostics( + display.scale, + display.cssWidth, + display.cssHeight, + display.deviceWidth, + display.deviceHeight, + display.dpr + ); +} + +function setScaleMode(mode, updateQuery) { + const nextMode = normalizeScaleMode(mode); + if (nextMode === scaleMode && !updateQuery) { + return; + } + + scaleMode = nextMode; + if (scaleModeSelect) { + scaleModeSelect.value = scaleMode; + } + + if (updateQuery) { + writeScaleModeToQuery(scaleMode); + } + + applyDisplayScale(); +} + +if (scaleModeSelect) { + scaleModeSelect.value = scaleMode; + scaleModeSelect.addEventListener('change', () => { + setScaleMode(scaleModeSelect.value, true); + }); +} + +window.addEventListener('resize', applyDisplayScale); +if (window.visualViewport) { + window.visualViewport.addEventListener('resize', applyDisplayScale); +} + +applyDisplayScale(); + +const { getAssemblyExports, getConfig, runMain } = await dotnet + .withDiagnosticTracing(false) + .create(); + +const config = getConfig(); +const exports = await getAssemblyExports(config.mainAssemblyName); +Host = exports.Examples.Web.Host; + +// raylib's GLFW/emscripten backend renders into this canvas. +dotnet.instance.Module['canvas'] = canvas; + +// Runs Host.Main() -> InitWindow + default example Init(). Must come after the canvas is bound. +await runMain(); +applyDisplayScale(); + +// Populate the navigation dropdown from the registered examples. +select.innerHTML = ''; +for (const name of Host.GetExampleNames().split('\n').filter(n => n.length > 0)) { + const opt = document.createElement('option'); + opt.value = name; + opt.textContent = name; + select.appendChild(opt); +} +select.addEventListener('change', () => Host.SetExample(select.value)); + +setScaleMode(scaleMode, false); + +// Drive raylib one frame per animation tick (never block the browser). +function mainLoop() { + Host.UpdateFrame(); + requestAnimationFrame(mainLoop); +} +requestAnimationFrame(mainLoop); diff --git a/Examples/Web/scaleUtils.js b/Examples/Web/scaleUtils.js new file mode 100644 index 0000000..e1e4b4f --- /dev/null +++ b/Examples/Web/scaleUtils.js @@ -0,0 +1,67 @@ +export const CANVAS_WIDTH = 800; +export const CANVAS_HEIGHT = 450; +export const SCALE_MODES = new Set(['integer', 'native', 'fit']); +export const DEFAULT_SCALE_MODE = 'native'; + +export function normalizeScaleMode(mode) { + if (!mode) { + return DEFAULT_SCALE_MODE; + } + + const normalized = String(mode).toLowerCase(); + return SCALE_MODES.has(normalized) ? normalized : DEFAULT_SCALE_MODE; +} + +export function computeDisplaySize(mode, viewportWidth, viewportHeight, dpr = 1) { + const safeDpr = Number.isFinite(dpr) && dpr > 0 ? dpr : 1; + + if (viewportWidth <= 0 || viewportHeight <= 0) { + return { + cssWidth: CANVAS_WIDTH / safeDpr, + cssHeight: CANVAS_HEIGHT / safeDpr, + deviceWidth: CANVAS_WIDTH, + deviceHeight: CANVAS_HEIGHT, + scale: 1, + dpr: safeDpr + }; + } + + // Convert viewport from CSS pixels to device pixels first so scaling is DPI/zoom aware. + const viewportDeviceWidth = Math.max(1, Math.floor(viewportWidth * safeDpr)); + const viewportDeviceHeight = Math.max(1, Math.floor(viewportHeight * safeDpr)); + + let deviceWidth; + let deviceHeight; + let scale; + + if (mode === 'native') { + // 1 source pixel -> 1 physical device pixel. + scale = 1; + deviceWidth = CANVAS_WIDTH; + deviceHeight = CANVAS_HEIGHT; + } else if (mode === 'fit') { + const fitRatio = Math.min(viewportDeviceWidth / CANVAS_WIDTH, viewportDeviceHeight / CANVAS_HEIGHT); + const safeRatio = Number.isFinite(fitRatio) && fitRatio > 0 ? fitRatio : 1; + scale = safeRatio; + deviceWidth = Math.max(1, Math.floor(CANVAS_WIDTH * safeRatio)); + deviceHeight = Math.max(1, Math.floor(CANVAS_HEIGHT * safeRatio)); + } else { + // integer mode + const integerScale = Math.max(1, Math.floor(Math.min( + viewportDeviceWidth / CANVAS_WIDTH, + viewportDeviceHeight / CANVAS_HEIGHT + ))); + scale = integerScale; + deviceWidth = CANVAS_WIDTH * integerScale; + deviceHeight = CANVAS_HEIGHT * integerScale; + } + + return { + cssWidth: deviceWidth / safeDpr, + cssHeight: deviceHeight / safeDpr, + deviceWidth, + deviceHeight, + scale, + dpr: safeDpr + }; +} diff --git a/Examples/Web/scaleUtils.test.js b/Examples/Web/scaleUtils.test.js new file mode 100644 index 0000000..cecb25c --- /dev/null +++ b/Examples/Web/scaleUtils.test.js @@ -0,0 +1,92 @@ +import { describe, it } from 'node:test'; +import assert from 'node:assert/strict'; +import { + CANVAS_WIDTH, + CANVAS_HEIGHT, + normalizeScaleMode, + computeDisplaySize +} from './scaleUtils.js'; + +describe('normalizeScaleMode', () => { + it('returns native for null, undefined, and empty string', () => { + assert.equal(normalizeScaleMode(null), 'native'); + assert.equal(normalizeScaleMode(undefined), 'native'); + assert.equal(normalizeScaleMode(''), 'native'); + }); + + it('normalizes allowlisted values case-insensitively', () => { + assert.equal(normalizeScaleMode('INTEGER'), 'integer'); + assert.equal(normalizeScaleMode('Fit'), 'fit'); + assert.equal(normalizeScaleMode('native'), 'native'); + }); + + it('returns native for unknown values', () => { + assert.equal(normalizeScaleMode('stretch'), 'native'); + assert.equal(normalizeScaleMode('bogus'), 'native'); + }); +}); + +describe('computeDisplaySize', () => { + it('native mode uses 1:1 device pixels at scale 1', () => { + const result = computeDisplaySize('native', 1200, 800, 1); + + assert.equal(result.scale, 1); + assert.equal(result.deviceWidth, CANVAS_WIDTH); + assert.equal(result.deviceHeight, CANVAS_HEIGHT); + assert.equal(result.cssWidth, CANVAS_WIDTH); + assert.equal(result.cssHeight, CANVAS_HEIGHT); + assert.equal(result.dpr, 1); + }); + + it('integer mode scales by floored ratio with minimum scale 1', () => { + const doubled = computeDisplaySize('integer', 1600, 900, 1); + + assert.equal(doubled.scale, 2); + assert.equal(doubled.deviceWidth, CANVAS_WIDTH * 2); + assert.equal(doubled.deviceHeight, CANVAS_HEIGHT * 2); + + const single = computeDisplaySize('integer', 900, 500, 1); + + assert.equal(single.scale, 1); + assert.equal(single.deviceWidth, CANVAS_WIDTH); + assert.equal(single.deviceHeight, CANVAS_HEIGHT); + }); + + it('fit mode scales proportionally and keeps device dimensions at least 1', () => { + const result = computeDisplaySize('fit', 400, 225, 1); + + assert.equal(result.scale, 0.5); + assert.equal(result.deviceWidth, 400); + assert.equal(result.deviceHeight, 225); + }); + + it('fit mode falls back to scale 1 for zero or negative viewport', () => { + const zero = computeDisplaySize('fit', 0, 0, 1); + + assert.equal(zero.scale, 1); + assert.equal(zero.deviceWidth, CANVAS_WIDTH); + assert.equal(zero.deviceHeight, CANVAS_HEIGHT); + + const negative = computeDisplaySize('fit', -100, -50, 1); + + assert.equal(negative.scale, 1); + assert.equal(negative.deviceWidth, CANVAS_WIDTH); + assert.equal(negative.deviceHeight, CANVAS_HEIGHT); + }); + + it('applies dpr to viewport before scaling and converts css size back', () => { + const result = computeDisplaySize('native', 400, 225, 2); + + assert.equal(result.dpr, 2); + assert.equal(result.deviceWidth, CANVAS_WIDTH); + assert.equal(result.deviceHeight, CANVAS_HEIGHT); + assert.equal(result.cssWidth, CANVAS_WIDTH / 2); + assert.equal(result.cssHeight, CANVAS_HEIGHT / 2); + }); + + it('treats invalid dpr as 1', () => { + assert.equal(computeDisplaySize('native', 800, 450, 0).dpr, 1); + assert.equal(computeDisplaySize('native', 800, 450, NaN).dpr, 1); + assert.equal(computeDisplaySize('native', 800, 450, Infinity).dpr, 1); + }); +}); diff --git a/README.md b/README.md index 406665f..18a8839 100644 --- a/README.md +++ b/README.md @@ -65,25 +65,6 @@ otherwise the command won't work. 5. Start coding! -## Building from source - -The `Examples` and `Raylib-cs.Tests` projects consume the binding as a NuGet package, and the in-repo -version may not be published on nuget.org yet. Pack it once into the local feed before the first build: - -``` -dotnet pack Raylib-cs -c Release -o nuget -dotnet build -``` - -`NuGet.config` points restore at the local `./nuget` feed, so both projects pick up the freshly packed -package. If you skip the pack step on a fresh clone, restore fails with NU1301 (the `./nuget` source -doesn't exist) or NU1102 (Raylib-cs not found) — running the pack command fixes both. - -When iterating on the binding itself, note that NuGet caches the extracted package by version and -ignores repacks of the same version. After repacking, delete the old package from `./nuget` and clear -the cached copy (`dotnet nuget locals global-packages --clear`, or delete -`~/.nuget/packages/raylib-cs/`) before restoring again. - ## Hello, World! ```csharp