2019-12-01 22:58:20 +01:00
using System ;
2019-11-08 15:35:15 +01:00
using Microsoft.Xna.Framework ;
using Microsoft.Xna.Framework.Graphics ;
namespace MLEM.Extensions {
2020-05-21 12:53:42 +02:00
/// <summary>
/// A set of extensions for dealing with <see cref="GraphicsDevice"/> and <see cref="GraphicsDeviceManager"/>
/// </summary>
2019-11-08 15:35:15 +01:00
public static class GraphicsExtensions {
private static int lastWidth ;
private static int lastHeight ;
2020-05-20 23:59:40 +02:00
/// <summary>
/// Sets the graphics device manager to fullscreen, properly taking into account the preferred backbuffer width and height to avoid lower resolutions for higher resolution screens.
/// </summary>
/// <param name="manager">The graphics device manager</param>
/// <param name="fullscreen">True if fullscreen should be enabled, false if disabled</param>
/// <exception cref="InvalidOperationException">Thrown when changing out of fullscreen mode before changing into fullscreen mode using this method</exception>
2019-11-08 15:35:15 +01:00
public static void SetFullscreen ( this GraphicsDeviceManager manager , bool fullscreen ) {
manager . IsFullScreen = fullscreen ;
if ( fullscreen ) {
2020-01-26 01:31:40 +01:00
var view = manager . GraphicsDevice . Viewport ;
lastWidth = view . Width ;
lastHeight = view . Height ;
2020-01-26 01:20:20 +01:00
2019-11-08 15:35:15 +01:00
var curr = GraphicsAdapter . DefaultAdapter . CurrentDisplayMode ;
manager . PreferredBackBufferWidth = curr . Width ;
manager . PreferredBackBufferHeight = curr . Height ;
} else {
2020-01-26 01:20:20 +01:00
if ( lastWidth < = 0 | | lastHeight < = 0 )
throw new InvalidOperationException ( "Can't call SetFullscreen to change out of fullscreen mode without going into fullscreen mode first" ) ;
2019-11-08 15:35:15 +01:00
manager . PreferredBackBufferWidth = lastWidth ;
manager . PreferredBackBufferHeight = lastHeight ;
}
manager . ApplyChanges ( ) ;
}
2020-05-20 23:59:40 +02:00
/// <summary>
/// Save version of <see cref="GraphicsDeviceManager.ApplyChanges"/> that doesn't reset window size to defaults
/// </summary>
/// <param name="manager">The graphics device manager</param>
2020-01-26 01:31:40 +01:00
public static void ApplyChangesSafely ( this GraphicsDeviceManager manager ) {
// If we don't do this, then applying changes will cause the
// graphics device manager to reset the window size to the
// size set when starting the game :V
var view = manager . GraphicsDevice . Viewport ;
manager . PreferredBackBufferWidth = view . Width ;
manager . PreferredBackBufferHeight = view . Height ;
manager . ApplyChanges ( ) ;
}
2020-05-20 23:59:40 +02:00
/// <summary>
/// Resets preferred width and height back to the window's default bound values.
/// </summary>
/// <param name="manager">The graphics device manager</param>
/// <param name="window">The window whose bounds to use</param>
2020-04-19 03:24:43 +02:00
public static void ResetWidthAndHeight ( this GraphicsDeviceManager manager , GameWindow window ) {
var ( _ , _ , width , height ) = window . ClientBounds ;
2020-04-19 03:20:25 +02:00
manager . PreferredBackBufferWidth = Math . Max ( height , width ) ;
manager . PreferredBackBufferHeight = Math . Min ( height , width ) ;
manager . ApplyChanges ( ) ;
}
2020-05-20 23:59:40 +02:00
/// <summary>
/// Starts a new <see cref="TargetContext"/> using the specified render target.
/// The returned context automatically disposes when used in a <c>using</c> statement, which causes any previously applied render targets to be reapplied automatically.
/// </summary>
/// <param name="device">The graphics device</param>
/// <param name="target">The render target to apply</param>
/// <returns></returns>
2020-04-02 17:47:11 +02:00
public static TargetContext WithRenderTarget ( this GraphicsDevice device , RenderTarget2D target ) {
return new TargetContext ( device , target ) ;
}
2020-05-21 17:21:34 +02:00
/// <summary>
/// Represents a context in which a <see cref="RenderTarget2D"/> is applied.
/// This class should be used with <see cref="GraphicsExtensions.WithRenderTarget"/>.
/// </summary>
2020-06-18 17:24:35 +02:00
public readonly struct TargetContext : IDisposable {
2020-04-02 17:47:11 +02:00
private readonly GraphicsDevice device ;
private readonly RenderTargetBinding [ ] lastTargets ;
2020-05-21 17:21:34 +02:00
/// <summary>
/// Creates a new target context with the given settings.
/// </summary>
/// <param name="device">The graphics device to apply the target on</param>
/// <param name="target">The target to apply</param>
2020-04-02 17:47:11 +02:00
public TargetContext ( GraphicsDevice device , RenderTarget2D target ) {
this . device = device ;
this . lastTargets = device . RenderTargetCount < = 0 ? null : device . GetRenderTargets ( ) ;
device . SetRenderTarget ( target ) ;
}
2020-05-21 17:21:34 +02:00
/// <summary>
/// Disposes this target context, which causes the graphics device's previous render targets to be re-applied.
/// </summary>
2020-04-02 17:47:11 +02:00
public void Dispose ( ) {
2020-04-03 00:59:27 +02:00
this . device . SetRenderTargets ( this . lastTargets ) ;
2020-04-02 17:47:11 +02:00
}
}
2019-11-08 15:35:15 +01:00
}
}