using System.Numerics; using System.Runtime.InteropServices; using System.Text; using Raylib_cs; namespace DaggerFramework.Rendering { public class RaylibRenderer : Renderer { public override bool ShouldRun => !WindowShouldClose(); public override Vector2 WindowSize => new Vector2(Raylib.GetScreenWidth(), Raylib.GetScreenHeight()); public override Vector2 MonitorSize => new Vector2(GetMonitorWidth(GetCurrentMonitor()), GetMonitorHeight(GetCurrentMonitor())); public override string WindowTitle { get => _windowTitle; set { SetWindowTitle(value); } } public override int TargetFps { get => _targetFps; set => SetTargetFps(value); } public override bool VSync { get => _vsync; set => SetWindowVSync(value); } public override void CreateWindow(WindowSettings windowSettings) { Raylib.SetTraceLogLevel(TraceLogLevel.LOG_NONE); _windowSize = windowSettings.Size; ConfigFlags windowFlags = 0; windowFlags |= windowSettings.Resizable ? ConfigFlags.FLAG_WINDOW_RESIZABLE : 0; _windowTitle = windowSettings.Title; if (_fullscreen) { var monitorSize = MonitorSize; Raylib.InitWindow((int)monitorSize.X, (int)monitorSize.Y, windowSettings.Title); } else { Raylib.InitWindow((int)_windowSize.X, (int)_windowSize.Y, windowSettings.Title); } Raylib.SetWindowState(windowFlags); } protected override void SetWindowTitle(string title) { Raylib.SetWindowTitle(title); } protected override void SetWindowVSync(bool value) { _vsync = value; // TODO: implement VSync toggle for Raylib. } protected override void SetTargetFps(int fps) { _targetFps = fps; Raylib.SetTargetFPS(fps); } protected override bool WindowShouldClose() { return Raylib.WindowShouldClose(); } public override void Shutdown() { Raylib.CloseWindow(); } public override void BeginFrame() { Raylib.BeginDrawing(); } public override void EndFrame() { Raylib.EndDrawing(); } public override void BeginCamera2d(Vector2 offset, Vector2 target, float rotation, float zoom) { var camera = new Camera2D(offset, target, rotation, zoom); Raylib.BeginMode2D(camera); } public override void EndCamera2d() { Raylib.EndMode2D(); } public override void ClearBackground(Color color) { Raylib.ClearBackground(DaggerColorToRaylibColor(color)); } protected override double GetFrameTime() { return (double)Raylib.GetFrameTime(); } public override void DrawCircle(float radius, Color color) { Raylib.DrawCircle((int)_position.X, (int)_position.Y, radius, DaggerColorToRaylibColor(color)); } public override void SetTransform(Vector2 position, Vector2 offset, float rotation = 0) { _position = position; _offset = offset; _rotation = rotation; } public override void DrawTexture(Texture2d texture, Color tint) { if (texture.Handle == -1) { LoadTexture(texture); } Raylib.DrawTextureV(_texturePool[texture.Handle], _position, DaggerColorToRaylibColor(tint)); } public override void DrawRectangle(Vector2 size, Color color) { // Raylib.DrawRectangleV(_position, size, SystemColorToRaylibColor(color)); Raylib.DrawRectanglePro(new Rectangle() { x = _position.X, y = _position.Y, width = size.X, height = size.Y }, _offset, _rotation, DaggerColorToRaylibColor(color)); } public override void DrawDebugText(string text, int fontSize, Color color) { Raylib.DrawText(text, (int)_position.X, (int)_position.Y, fontSize, DaggerColorToRaylibColor(color)); } public override void DrawSdfText(string text, int fontSize, Color color) { throw new NotImplementedException(); } public override void Initialize(RendererSettings settings) { _targetFps = settings.TargetFps; ConfigFlags flags = 0; // MSAA flags |= settings.Msaa == Msaa.Msaa4x ? ConfigFlags.FLAG_MSAA_4X_HINT : 0; // VSync flags |= settings.UseVSync ? ConfigFlags.FLAG_VSYNC_HINT : 0; // Fullscreen flags |= settings.Fullscreen ? ConfigFlags.FLAG_WINDOW_MAXIMIZED : 0; flags |= settings.Fullscreen ? ConfigFlags.FLAG_WINDOW_UNDECORATED : 0; _fullscreen = settings.Fullscreen; Raylib.SetConfigFlags(flags); } // TODO public override void SetTransform(Matrix4x4 transform) { } public override void CreateAndInitialize(WindowSettings windowSettings, RendererSettings renderSettings) { Initialize(renderSettings); CreateWindow(windowSettings); } private Raylib_cs.Color DaggerColorToRaylibColor(Color color) { return new Raylib_cs.Color { r = (byte)Math.Round(color.R * 255f), g = (byte)Math.Round(color.G * 255f), b = (byte)Math.Round(color.B * 255f), a = (byte)Math.Round(color.A * 255f) }; } public override void DrawText(Font font, string text, Color color) { if (font.Handle == -1) { LoadFont(font); } var rayFont = _fontPool[font.Handle]; Raylib.DrawTextPro(rayFont, text, _position, Vector2.Zero, _rotation, font.Size, 0.0f, DaggerColorToRaylibColor(color)); } protected override int GetMonitorWidth(int monitorId) { return Raylib.GetMonitorWidth(monitorId); } protected override int GetMonitorHeight(int monitorId) { return Raylib.GetMonitorHeight(monitorId); } protected override int GetCurrentMonitor() { return Raylib.GetCurrentMonitor(); } private unsafe void LoadFont(Font font) { Raylib_cs.Font fontRay; ReadOnlySpan ext = ".ttf"; // TODO: don't use a hardcoded extension. Span extBytes = new byte[Encoding.Default.GetByteCount(ext) + 1]; Encoding.Default.GetBytes(ext, extBytes); int fontChars = 2048; // TODO: control this dynamically to not load the entire font. unsafe { fixed (byte* extP = extBytes) { fixed (byte* bufferP = font.Buffer) { fontRay = Raylib.LoadFontFromMemory((sbyte*)extP, bufferP, (int)font.BufferSize, font.Size, null, fontChars); } } } Raylib.GenTextureMipmaps(ref fontRay.texture); Raylib.SetTextureFilter(fontRay.texture, TextureFilter.TEXTURE_FILTER_BILINEAR); _fontPool.Add(fontRay); font.Handle = _fontPool.Count - 1; } private void LoadTexture(Texture2d texture) { Image image = new(); unsafe { fixed (void* dataPtr = texture.Buffer) { image.data = dataPtr; } } image.width = texture.Width; image.height = texture.Height; image.mipmaps = texture.Mipmaps; image.format = (PixelFormat)texture.Format; Texture2D rayTexture; rayTexture = Raylib.LoadTextureFromImage(image); _texturePool.Add(rayTexture); texture.Handle = _texturePool.Count - 1; } private List _texturePool = new(); private List _fontPool = new(); private Vector2 _position, _offset; private float _rotation; private Vector2 _windowSize; private bool _vsync; private string _windowTitle = string.Empty; private int _targetFps; private bool _fullscreen; } }