//----------------------------------------------------------------------------- // RenderToTexture.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- using Microsoft.Xna.Framework.Graphics; using System; using System.Collections; using System.Text; using RacingGame.Graphics; using RacingGame.Helpers; using Texture = RacingGame.Graphics.Texture; using Model = RacingGame.Graphics.Model; using XnaTexture = Microsoft.Xna.Framework.Graphics.Texture2D; using Microsoft.Xna.Framework; using RacingGame.GameLogic; using Microsoft.Xna.Framework.Input; namespace RacingGame.Shaders { /// /// Render to texture helper class based on the Texture class. /// This class allows to render stuff onto textures, if thats not /// supported, it will just not work and report an engine log message. /// This class is required for most PostScreenShaders. /// public class RenderToTexture : Texture { /// /// Our render target we are going to render to. Much easier than in MDX /// where you have to use Surfaces, etc. Also supports the Xbox360 model /// of resolving the render target texture before we can use it, otherwise /// the RenderToTexture class would not work on the Xbox360. /// RenderTarget2D renderTarget = null; /* /// /// Z buffer surface for shadow mapping render targets that do not /// fit in our resolution. Usually unused! /// DepthStencilBuffer zBufferSurface = null; /// /// ZBuffer surface /// /// Surface public DepthStencilBuffer ZBufferSurface { get { return zBufferSurface; } } */ /// /// Posible size types for creating a RenderToTexture object. /// public enum SizeType { /// /// Uses the full screen size for this texture /// FullScreen, /// /// Uses half the full screen size, e.g. 800x600 becomes 400x300 /// HalfScreen, /// /// Uses a quarter of the full screen size, e.g. 800x600 becomes 200x150 /// QuarterScreen, /// /// Shadow map texture, usually 1024x1024, but can also be better /// like 2048x2048 or 4096x4096. /// ShadowMap, } /// /// Size type /// private SizeType sizeType; /// /// Calc size /// private void CalcSize() { switch (sizeType) { case SizeType.FullScreen: texWidth = BaseGame.Width; texHeight = BaseGame.Height; break; case SizeType.HalfScreen: texWidth = BaseGame.Width / 2; texHeight = BaseGame.Height / 2; break; case SizeType.QuarterScreen: texWidth = BaseGame.Width / 4; texHeight = BaseGame.Height / 4; break; case SizeType.ShadowMap: // Use a larger texture for high detail if (BaseGame.HighDetail) { texWidth = 2048; texHeight = 2048; } else { texWidth = 1024; texHeight = 1024; } break; } CalcHalfPixelSize(); } /// /// Does this texture use some high percision format? Better than 8 bit color? /// private bool usesHighPercisionFormat = false; /// /// Render target /// /// Render target 2D public RenderTarget2D RenderTarget { get { return renderTarget; } } /// /// Override how to get XnaTexture, we have to resolve the render target /// for supporting the Xbox, which requires calling Resolve first! /// After that you can call this property to get the current texture. /// /// XnaTexture public override XnaTexture XnaTexture { get { if (alreadyResolved) internalXnaTexture = renderTarget; return internalXnaTexture; } } /// /// Does this texture use some high percision format? Better than 8 bit color? /// public bool UsesHighPercisionFormat { get { return usesHighPercisionFormat; } } /// /// Id for each created RenderToTexture for the generated filename. /// private static int RenderToTextureGlobalInstanceId = 0; /// /// Creates an offscreen texture with the specified size which /// can be used for render to texture. /// public RenderToTexture(SizeType setSizeType) { sizeType = setSizeType; CalcSize(); texFilename = "RenderToTexture instance " + RenderToTextureGlobalInstanceId++; Create(); BaseGame.AddRemRenderToTexture(this); } /// /// Handle the DeviceReset event, we have to re-create all our render targets. /// public void HandleDeviceReset() { // Respond to resolution changes CalcSize(); // Clear resolved texture alreadyResolved = false; internalXnaTexture = null; // Re-create Create(); } /// /// Create /// private void Create() { SurfaceFormat outSF; DepthFormat outDF; int outMSC; int MultisampleCount = 2; if (BaseGame.Device.PresentationParameters.BackBufferHeight == 720) { MultisampleCount = 4; } // UWP COMMENT OUT //if (sizeType == SizeType.ShadowMap || // BaseGame.CurrentPlatform == PlatformID.Win32NT) //{ // MultisampleCount = 0; //} // Rgba64 is a HiDef-only format and is not available on Android or iOS. // Android also does not reliably support MSAA on render targets. outSF = OperatingSystem.IsAndroid() || OperatingSystem.IsIOS() ? SurfaceFormat.Color : SurfaceFormat.Rgba64; outDF = BaseGame.BackBufferDepthFormat; outMSC = OperatingSystem.IsAndroid() || OperatingSystem.IsIOS() ? 0 : MultisampleCount; if (sizeType == SizeType.ShadowMap) outMSC = 0; // Create render target of specified size. renderTarget = new RenderTarget2D ( BaseGame.Device, texWidth, texHeight, false, outSF, outDF, outMSC, RenderTargetUsage.DiscardContents); if (outSF != SurfaceFormat.Color) usesHighPercisionFormat = true; loaded = true; } /// /// Clear render target (call SetRenderTarget first) /// public void Clear(Color clearColor) { if (loaded == false || renderTarget == null) return; BaseGame.Device.Clear( ClearOptions.Target | ClearOptions.DepthBuffer, clearColor, 1.0f, 0); } /// /// Set render target to this texture to render stuff on it. /// public bool SetRenderTarget() { if (loaded == false || renderTarget == null) return false; BaseGame.SetRenderTarget(renderTarget, false); return true; } /// /// Make sure we don't call XnaTexture before resolving for the first time! /// bool alreadyResolved = false; /// /// Resolve render target. For windows developers this method may seem /// strange, why not just use the rendertarget's texture? Well, this is /// just for the Xbox360 support. The Xbox requires that you call Resolve /// first before using the rendertarget texture. The reason for that is /// copying the data over from the EPRAM to the video memory, for more /// details read the XNA docs. /// Note: This method will only work if the render target was set before /// with SetRenderTarget, else an exception will be thrown to ensure /// correct calling order. /// public void Resolve() { // Make sure this render target is currently set! if (BaseGame.CurrentRenderTarget != renderTarget) throw new InvalidOperationException( "You can't call Resolve without first setting the render target!"); alreadyResolved = true; // fix //BaseGame.Device.ResolveRenderTarget(0); BaseGame.Device.SetRenderTarget(null); } } }