Devlair 👾

Viewport letterboxing in Raylib

2 mins post gamedev


About week ago I started little 2D old school like project in latest .NET 7 just to test some new things I allways wanted to try. For graphic representation I’ve used small library called Raylib. For those who don’t know Raylib is high level OpenGL “wrapper” written in pure C and inspired by XNA - so it works more or less like Monogame or Love2D.

I wanted to develop whole project in one native resolution which is small (in my case 480x320) for that desired old school look and feel. Technically it’s pretty simple, we just create render texture (aka offscreen target, render target or canvas) and basically draw everything into it instead of default framework canvas. Then we take our render texture and draw it how we want. That means we can use some trivial math to calculate positon and size when user resize window.

Godot community described viewport resolution handling very well with some awesome examples.

First step is load our render target, that’s pretty easy in Raylib

1_renderTexture = Raylib.LoadRenderTexture(width, height);

Then we calculate ScreenPosition of our renderTexture. This is part of my code which is called everytime when window is resized

 1	public void WindowResized()
 2	{
 3		var windowWidth = Game.WindowWidth;
 4		var windowHeight = Game.WindowHeight;
 5
 6		// ViewPortType.Expand just stretch render texture to window
 7		// ViewPortType.Aspect stretch texture to window in way to retain aspect ratio
 8		if (_viewPortType == ViewPortType.Expand)
 9		{
10			ScreenPosition = new Rectangle(0, 0, windowWidth, windowHeight);
11			LetterBoxSpacing = Vector2.Zero;
12		}
13		else if (_viewPortType == ViewPortType.Aspect)
14		{
15			// NativeSize is Vector2 with original render texture size
16			// Due to how OpenGL coordinates internally works on render textures, we must negate NativeSize.height
17			float ratio = Math.Min(windowWidth / NativeSize.width, windowHeight / -NativeSize.height);
18
19			int width = (int)(480 * ratio);
20			int height = (int)(320 * ratio);
21
22			var centerX = windowWidth / 2 - width / 2;
23			var centerY = windowHeight / 2 - height / 2;
24
25			// Calculation of letter box spacing
26			LetterBoxSpacing = new Vector2((windowWidth - width) / 2, (windowHeight - height) / 2);
27
28			//final position and size
29			ScreenPosition = new Rectangle(centerX, centerY, width, height);
30
31		}
32		
33		ScreenCenter = new Vector2(ScreenPosition.x + ScreenPosition.width / 2, ScreenPosition.y + ScreenPosition.height / 2);
34	}

So we can now begin drawing into render texture

1		Raylib.BeginTextureMode(_renderTexture);
2		Raylib.ClearBackground(ClearColor);
3		//Draw something here...
4		Raylib.EndTextureMode();

Then we draw render texture

1		Raylib.DrawTexturePro(Viewport.GetRenderTexture(), Viewport.NativeSize, Viewport.ScreenPosition, Vector2.Zero, 0, Color.WHITE);

And… that’s all! Seems pretty simple doesn’t it?