//-----------------------------------------------------------------------------
// 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);
}
}
}