static void Main(string[] args) { // Create the window and the graphics device VeldridInit(out var window, out var graphicsDevice); // Create a texture storage that manages textures. // Textures in OpenWheels are represented with integer values. // A platform-specific ITextureStorageImplementation handles texture creation, // destruction and modification. var texStorage = new VeldridTextureStorage(graphicsDevice); // Create a renderer that implements the OpenWheels.Rendering.IRenderer interface // this guy actually draws everything to the backbuffer var renderer = new VeldridRenderer(graphicsDevice, texStorage); // OpenWheels always requires a texture to render, so renderer implementations only need a single shader // Even for untextured primitives we need to have a texture set. So we create a white 1x1 texture for those. ReadOnlySpan <Color> blankSpan = stackalloc Color[] { Color.White }; var blank = texStorage.CreateTexture(1, 1, TextureFormat.Rgba32); texStorage.SetData(blank, blankSpan); // Our batcher lets use make calls to render lots of different primitive shapes and text. // When we're done the batcher can export draw calls so the renderer can use them do the drawing. // We won't use text rendering in this sample so we use the dummy text renderer. var batcher = new Batcher(NullBitmapFontRenderer.Instance); var first = true; // We run the game loop here and do our drawing inside of it. VeldridRunLoop(window, graphicsDevice, () => { renderer.Clear(Color.CornflowerBlue); // Start a new batch batcher.Start(); // set the texture to the blank one we registered batcher.SetTexture(blank); // Let's draw some primitives. The API is very obvious, you can use IntelliSense to find supported shapes. batcher.FillRect(new RectangleF(10, 10, 100, 100), Color.LimeGreen); // Note that subsequent line segments are connected at their corners Span <Vector2> points = stackalloc Vector2[] { new Vector2(140, 20), new Vector2(320, 20), new Vector2(320, 120), new Vector2(420, 120) }; batcher.DrawLineStrip(points, Color.Red, 20); batcher.FillTriangle(new Vector2(500, 20), new Vector2(600, 70), new Vector2(500, 120), Color.White); // The tessellation of the circle and corners for the rounded rectangle can be adjusted with the maxError parameter batcher.DrawCircle(new Vector2(700, 70), 50, Color.BlueViolet, 2); batcher.FillRoundedRect(new RectangleF(790, 10, 100, 100), 10, Color.SandyBrown); var pa = new Vector2(50, 220); var pb = new Vector2(150, 120); var pc = new Vector2(250, 220); var curve = new QuadraticBezier(pa, pb, pc); // The segmentation for curves can be adjusted with the segmentsPerLength parameter // Using that parameter and an (over)estimate of the length of the curve the number of segments // is computed batcher.DrawCurve(curve, Color.DarkGoldenrod, 2); var o = new Vector2(0, 100); var pd = new Vector2(200, 420); var curve2 = new CubicBezier(pa + o, pb + o, pd, pc + o); batcher.DrawCurve(curve2, Color.DarkOrchid, 2); // Finish the batch and let the renderer draw everything to the back buffer. batcher.Render(renderer); if (first) { Console.WriteLine("Vertices: " + batcher.VerticesSubmitted); Console.WriteLine("Indices: " + batcher.IndicesSubmitted); Console.WriteLine("Batches: " + batcher.BatchCount); first = false; } });
static void Main(string[] args) { // Create the window and the graphics device VeldridInit(out var window, out var graphicsDevice); // Create a renderer that implements the OpenWheels.Rendering.IRenderer interface // this guy actually draws everything to the backbuffer var renderer = new VeldridRenderer(graphicsDevice); // Our batcher lets use make calls to render lots of different primitive shapes and text. // When we're done the batcher sends the draw calls to the renderer which will actually do the drawing. // Alternatively to Batcher you can use StringIdBatcher so you can register and set the active texture // and font with a string identifier. var batcher = new Batcher(renderer); var checkerBoardTextureId = batcher.LoadTexture("checkerboard.png"); // OpenWheels defines a sprite as an image that's part of a texture // To create a sprite, we pass a texture and a region of that texture (in pixels) that contains the actual image // let's add a sprite that draws 3/4th of the checkerboard // So if our original texture looks like this: // |## | // |## | // | ##| // | ##| // We'll create a sprite that looks like this: // |## | // |## | // | #| var cbSize = renderer.GetTextureSize(checkerBoardTextureId); var subSpriteRect = new Rectangle(0, 0, (cbSize.Width * 3) / 4, (cbSize.Height * 3) / 4); var checkerBoardSubSprite = new Sprite(checkerBoardTextureId, subSpriteRect); var frame = 0; // We run the game loop here and do our drawing inside of it. VeldridRunLoop(window, graphicsDevice, () => { renderer.Clear(Color.CornflowerBlue); // Start a new batch batcher.Start(); // we set the texture using the texture id we got back when registering the texture // OpenWheels internally only works with sprites // If you set a texture on a batcher it will convert it to a sprite with the region being the // entire texture bounds batcher.SetTexture(checkerBoardTextureId); // The Batcher API is stateful. Anything we render now will use the checkerboard texture. // By default the UV coordinates 0, 0, 1, 1 are use, so our texture is stretched batcher.FillRect(new RectangleF(50, 20, 100, 100), Color.White); batcher.FillRect(new RectangleF(200, 20, 100, 200), Color.White); // Let's draw our subsprite batcher.Sprite = checkerBoardSubSprite; batcher.FillRect(new RectangleF(350, 20, 100, 100), Color.White); // We can only draw 1 texture in a single draw call, but since our subsprite actually uses the same // texture as our full checkerboard the batcher can still combine the calls into a single batch. batcher.SetTexture(checkerBoardTextureId); // Most of the primitives support UV coordinates one way or another. batcher.FillCircle(new Vector2(550, 70), 50, Color.White, .25f); batcher.FillRoundedRect(new RectangleF(650, 20, 100, 100), 15, Color.White); var v1 = new Vector2(50, 280); var v2 = new Vector2(150, 380); batcher.DrawLine(v1, v2, Color.White, 6f); // Note that the texture rotates with the line // This is different from the circle(segment) primitives where we draw a cutout of the active texture // There are a lot of ways to UV-map shapes, but OpenWheels currently picks just one for each shape // we can set a matrix to transform UV coordinates // let's make our texture loop in length while keeping it's aspect ratio and UV across its width. // The sampler should wrap to be able to loop the texture (the default sampler state is LinearClamp) // This state sticks across frames, so we could set it before the render loop as well batcher.SamplerState = SamplerState.LinearWrap; var v3 = new Vector2(200, 280); var v4 = new Vector2(300, 380); const float lineWidth = 10f; // we want our UV aspect ratio to be 1:1, but it's lineWidth:length and we want to use // the coordinate system of the width, so we normalize height to get the right aspect ratio // (note that height is defined as the forward direction of the line) var uvHeight = Vector2.Distance(v3, v4) / lineWidth; batcher.UvTransform = Matrix3x2.CreateScale(1f, uvHeight); batcher.DrawLine(v3, v4, Color.White, lineWidth); // Reset the uv transform batcher.UvTransform = Matrix3x2.Identity; // The color value we can pass to these methods is multiplied with our texture color at each pixel. batcher.FillRect(new RectangleF(350, 280, 100, 100), Color.Red); // Finish the batch and let the renderer draw everything to the back buffer. batcher.Finish(); if (frame < 2) { // Note that the first frame renders in two batches because we change the sampler state // halfway through. // Every subsequent frame render in a single batch because the sampler state stays at LinearClamp Console.WriteLine("Frame " + frame); Console.WriteLine("Vertices: " + batcher.VerticesSubmitted); Console.WriteLine("Indices: " + batcher.IndicesSubmitted); Console.WriteLine("Batches: " + batcher.BatchCount); Console.WriteLine(); frame++; } }); renderer.Dispose(); graphicsDevice.Dispose(); }
static void Main(string[] args) { // Create the window and the graphics device VeldridInit(out var window, out var graphicsDevice); // Create a texture storage that manages textures. // Textures in OpenWheels are represented with integer values. // A platform-specific ITextureStorageImplementation handles texture creation, // destruction and modification. var texStorage = new VeldridTextureStorage(graphicsDevice); // Create a renderer that implements the OpenWheels.Rendering.IRenderer interface. // This guy actually draws everything to the backbuffer. var renderer = new VeldridRenderer(graphicsDevice, texStorage); // To render text Batcher needs an IBitmapFontRenderer implementation. // Our batcher lets use make calls to render lots of different primitive shapes and text. // When we're done the batcher sends the draw calls to the renderer which will actually do the drawing. // textures using a string identifier. Internally in OpenWheels textures are identified by an integer. var batcher = new Batcher(); // OpenWheels.Rendering.ImageSharp contains several extension methods to easily load // images and fonts into an ITextureStorage implementation. // Using this library is the easiest way to handle font and texture loading, but it's a separate lib so you can // use another solution if you prefer. // The following call creates a font atlas and the corresponding image for the glyphs. // By default it includes only the basic Latin characters in the atlas. // The created image is registered with the renderer and the font can be // set using the Batcher by calling `SetFont(fontId)`. // m6x11 font by Daniel Linssen: https://managore.itch.io // Note that this is a pixel perfect font and so it will look good even though OpenWheels has pretty bad text rendering. var font = texStorage.LoadFont("Resources/m6x11.ttf", 32, (int)'?'); // Google's Roboto font //var font = texStorage.LoadFont("Resources/Roboto-Medium.ttf", 32, (int) '?'); var first = true; // We run the game loop here and do our drawing inside of it. VeldridRunLoop(window, graphicsDevice, () => { renderer.Clear(Color.CornflowerBlue); // Start a new batch batcher.Start(); // Note that drawing text changes the active texture to the font atlas texture. // So if you're rendering other stuff, make sure you set a texture before drawing anything else batcher.DrawText(font, "Hello, World!", new Vector2(100f, 80f), Color.Black); batcher.DrawText(font, "This is rendered from a font atlas!", new Vector2(100f, 130f), .5f, Color.DarkRed); // Reset the transformation matrix batcher.PositionTransform = Matrix3x2.Identity; // Let's also render the font atlas so we can see how this works batcher.SetTexture(font.Texture); var atlasSize = texStorage.GetTextureSize(font.Texture); batcher.FillRect(new RectangleF(100, 170, atlasSize.Width, atlasSize.Height), Color.Black); // Finish the frame and let the renderer draw everything to the back buffer. batcher.Render(renderer); if (first) { Console.WriteLine("Vertices: " + batcher.VerticesSubmitted); Console.WriteLine("Indices: " + batcher.IndicesSubmitted); Console.WriteLine("Batches: " + batcher.BatchCount); first = false; } }); renderer.Dispose(); graphicsDevice.Dispose(); }
static void Main(string[] args) { // Create the window and the graphics device VeldridInit(out var window, out var graphicsDevice); // Create a renderer that implements the OpenWheels.Rendering.IRenderer interface // this guy actually draws everything to the backbuffer var renderer = new VeldridRenderer(graphicsDevice); // Our batcher lets use make calls to render lots of different primitive shapes and text. // When we're done the batcher sends the draw calls to the renderer which will actually do the drawing. // StringIdBatcher is an extension of batcher that supports registering and setting fonts and // textures using a string identifier. Internally in OpenWheels textures are identified by an integer. var batcher = new StringIdBatcher(renderer); const string fontId = "font"; // This creates a font atlas and the corresponding image for the letters. // By default it includes only the basic Latin characters in the atlas. // The created image is registered with the renderer and the font can be // set using the StringIdBatcher by calling `SetFont(fontId)`. // LoadSystemFont and other extension methods to load textures and fonts into a batcher in a single method call // are defined in the OpenWheels.Rendering.ImageSharp library. // Using this library is the easiest way to handle font and texture loading, but it's a separate lib so you can // use another solution if you prefer. // Note: System fonts only work on Windows, you can use a font file and call instead `LoadFont` on other platforms. batcher.LoadSystemFont("Consolas", 24, '?', fontId); // set the font batcher.SetFont(fontId); var first = true; // We run the game loop here and do our drawing inside of it. VeldridRunLoop(window, graphicsDevice, () => { renderer.Clear(Color.CornflowerBlue); // Start a new batch batcher.Start(); // Note that drawing text changes the active texture to the font atlas texture. // So if you're rendering other stuff, make sure you set a texture before drawing anything else batcher.DrawText("Hello World!", new Vector2(100f), Color.Black); // We rotate and translate this one a little bit for style 😎 batcher.PositionTransform = Matrix3x2.CreateTranslation(52, -154) * Matrix3x2.CreateRotation((float)Math.PI / 2f); batcher.DrawText("Hell World!", Vector2.Zero, Color.Black, va: VerticalAlignment.Bottom); // Reset the transformation matrix batcher.PositionTransform = Matrix3x2.Identity; // Finish the batch and let the renderer draw everything to the back buffer. batcher.Finish(); if (first) { Console.WriteLine("Vertices: " + batcher.VerticesSubmitted); Console.WriteLine("Indices: " + batcher.IndicesSubmitted); Console.WriteLine("Batches: " + batcher.BatchCount); first = false; } }); renderer.Dispose(); graphicsDevice.Dispose(); }