2014-01-27 00:02:21 +00:00
//http://stackoverflow.com/questions/6893302/decode-rgb-value-to-single-float-without-bit-shift-in-glsl
using System ;
using sd = System . Drawing ;
2014-01-27 00:38:10 +00:00
using OpenTK ;
2014-01-27 09:45:16 +00:00
using OpenTK.Graphics.OpenGL ;
2014-01-27 00:38:10 +00:00
2014-01-27 00:02:21 +00:00
namespace BizHawk.Bizware.BizwareGL
{
/// <summary>
/// A simple renderer useful for rendering GUI stuff.
/// When doing GUI rendering, run everything through here (if you need a GL feature not done through here, run it through here first)
/// Call Begin, then draw, then End, and dont use other Renderers or GL calls in the meantime, unless you know what youre doing.
/// This can perform batching (well.. maybe not yet), which is occasionally necessary for drawing large quantities of things.
/// </summary>
public class GuiRenderer : IDisposable
{
public GuiRenderer ( IGL owner )
{
Owner = owner ;
VertexLayout = owner . CreateVertexLayout ( ) ;
2014-01-27 09:45:16 +00:00
VertexLayout . DefineVertexAttribute ( 0 , 2 , VertexAttribPointerType . Float , false , 16 , 0 ) ;
VertexLayout . DefineVertexAttribute ( 1 , 2 , VertexAttribPointerType . Float , false , 16 , 8 ) ;
2014-01-27 00:02:21 +00:00
VertexLayout . Close ( ) ;
_Projection = new MatrixStack ( ) ;
_Modelview = new MatrixStack ( ) ;
var vs = Owner . CreateVertexShader ( DefaultVertexShader ) ;
var ps = Owner . CreateFragmentShader ( DefaultPixelShader ) ;
CurrPipeline = DefaultPipeline = Owner . CreatePipeline ( vs , ps ) ;
}
public void Dispose ( )
{
VertexLayout . Dispose ( ) ;
VertexLayout = null ;
DefaultPipeline . Dispose ( ) ;
DefaultPipeline = null ;
}
/// <summary>
/// Sets the pipeline for this GuiRenderer to use. We won't keep possession of it.
/// This pipeline must work in certain ways, which can be discerned by inspecting the built-in one
/// </summary>
public void SetPipeline ( Pipeline pipeline )
{
if ( IsActive )
throw new InvalidOperationException ( "Can't change pipeline while renderer is running!" ) ;
Flush ( ) ;
CurrPipeline = pipeline ;
}
/// <summary>
/// Restores the pipeline to the default
/// </summary>
public void SetDefaultPipeline ( )
{
SetPipeline ( DefaultPipeline ) ;
}
public void SetModulateColorWhite ( )
{
SetModulateColor ( sd . Color . White ) ;
}
public void SetModulateColor ( sd . Color color )
{
Flush ( ) ;
CurrPipeline [ "uModulateColor" ] . Set ( new Vector4 ( color . R / 255.0f , color . G / 255.0f , color . B / 255.0f , color . A / 255.0f ) ) ;
}
public void SetBlendState ( IBlendState rsBlend )
{
Flush ( ) ;
Owner . SetBlendState ( rsBlend ) ;
}
MatrixStack _Projection , _Modelview ;
public MatrixStack Projection
{
get { return _Projection ; }
set
{
_Projection = value ;
_Projection . IsDirty = true ;
}
}
public MatrixStack Modelview
{
get { return _Modelview ; }
set
{
_Modelview = value ;
_Modelview . IsDirty = true ;
}
}
2014-01-27 09:36:18 +00:00
2014-01-27 00:02:21 +00:00
/// <summary>
/// begin rendering, initializing viewport and projections to the given dimensions
/// </summary>
2014-01-27 09:36:18 +00:00
/// <param name="yflipped">Whether the matrices should be Y-flipped, for use with render targets</param>
public void Begin ( int width , int height , bool yflipped = false )
2014-01-27 00:02:21 +00:00
{
Begin ( ) ;
Projection = Owner . CreateGuiProjectionMatrix ( width , height ) ;
Modelview = Owner . CreateGuiViewMatrix ( width , height ) ;
2014-01-27 09:36:18 +00:00
if ( yflipped )
{
//not sure this is the best way to do it. could be done in the view matrix creation
Modelview . Scale ( 1 , - 1 ) ;
Modelview . Translate ( 0 , - height ) ;
}
Owner . SetViewport ( width , height ) ;
2014-01-27 00:02:21 +00:00
}
/// <summary>
/// Begins rendering
/// </summary>
public void Begin ( )
{
//uhhmmm I want to throw an exception if its already active, but its annoying.
if ( CurrPipeline = = null )
throw new InvalidOperationException ( "Pipeline hasn't been set!" ) ;
IsActive = true ;
Owner . BindVertexLayout ( VertexLayout ) ;
Owner . BindPipeline ( CurrPipeline ) ;
//clear state cache
sTexture = null ;
Modelview . Clear ( ) ;
Projection . Clear ( ) ;
SetModulateColorWhite ( ) ;
}
/// <summary>
/// Use this, if you must do something sneaky to openGL without this GuiRenderer knowing.
/// It might be faster than End and Beginning again, and certainly prettier
/// </summary>
public void Flush ( )
{
//no batching, nothing to do here yet
}
/// <summary>
/// Ends rendering
/// </summary>
public void End ( )
{
if ( ! IsActive )
throw new InvalidOperationException ( "GuiRenderer is not active!" ) ;
IsActive = false ;
}
/// <summary>
/// Draws a subrectangle from the provided texture. For advanced users only
/// </summary>
public void DrawSubrect ( Texture2d tex , float x , float y , float w , float h , float u0 , float v0 , float u1 , float v1 )
{
DrawSubrectInternal ( tex , x , y , w , h , u0 , v0 , u1 , v1 ) ;
}
/// <summary>
/// draws the specified Art resource
/// </summary>
public void Draw ( Art art ) { DrawInternal ( art , 0 , 0 , art . Width , art . Height , false , false ) ; }
/// <summary>
/// draws the specified Art resource with the specified offset. This could be tricky if youve applied other rotate or scale transforms first.
/// </summary>
public void Draw ( Art art , float x , float y ) { DrawInternal ( art , x , y , art . Width , art . Height , false , false ) ; }
/// <summary>
/// draws the specified Art resource with the specified offset, with the specified size. This could be tricky if youve applied other rotate or scale transforms first.
/// </summary>
public void Draw ( Art art , float x , float y , float width , float height ) { DrawInternal ( art , x , y , width , height , false , false ) ; }
/// <summary>
/// draws the specified Art resource with the specified offset. This could be tricky if youve applied other rotate or scale transforms first.
/// </summary>
public void Draw ( Art art , Vector2 pos ) { DrawInternal ( art , pos . X , pos . Y , art . Width , art . Height , false , false ) ; }
/// <summary>
/// draws the specified texture2d resource.
/// </summary>
public void Draw ( Texture2d tex ) { DrawInternal ( tex , 0 , 0 , tex . Width , tex . Height ) ; }
2014-01-27 09:36:18 +00:00
/// <summary>
/// draws the specified texture2d resource.
/// </summary>
public void Draw ( Texture2d tex , float x , float y ) { DrawInternal ( tex , x , y , tex . Width , tex . Height ) ; }
2014-01-27 00:02:21 +00:00
/// <summary>
/// draws the specified Art resource with the given flip flags
/// </summary>
public void DrawFlipped ( Art art , bool xflip , bool yflip ) { DrawInternal ( art , 0 , 0 , art . Width , art . Height , xflip , yflip ) ; }
unsafe void DrawInternal ( Texture2d tex , float x , float y , float w , float h )
{
Art art = new Art ( null ) ;
art . Width = w ;
art . Height = h ;
art . u0 = art . v0 = 0 ;
art . u1 = art . v1 = 1 ;
art . BaseTexture = tex ;
DrawInternal ( art , x , y , w , h , false , false ) ;
}
unsafe void DrawInternal ( Art art , float x , float y , float w , float h , bool fx , bool fy )
{
float u0 , v0 , u1 , v1 ;
if ( fx ) { u0 = art . u1 ; u1 = art . u0 ; }
else { u0 = art . u0 ; u1 = art . u1 ; }
if ( fy ) { v0 = art . v1 ; v1 = art . v0 ; }
else { v0 = art . v0 ; v1 = art . v1 ; }
float [ ] data = new float [ 16 ] {
x , y , u0 , v0 ,
x + art . Width , y , u1 , v0 ,
x , y + art . Height , u0 , v1 ,
x + art . Width , y + art . Height , u1 , v1
} ;
Texture2d tex = art . BaseTexture ;
if ( sTexture ! = tex )
CurrPipeline [ "uSampler0" ] . Set ( sTexture = tex ) ;
if ( _Projection . IsDirty )
{
CurrPipeline [ "um44Projection" ] . Set ( ref _Projection . Top ) ;
_Projection . IsDirty = false ;
}
if ( _Modelview . IsDirty )
{
CurrPipeline [ "um44Modelview" ] . Set ( ref _Modelview . Top ) ;
_Modelview . IsDirty = false ;
}
fixed ( float * pData = & data [ 0 ] )
{
Owner . BindArrayData ( pData ) ;
Owner . DrawArrays ( PrimitiveType . TriangleStrip , 0 , 4 ) ;
}
}
unsafe void DrawSubrectInternal ( Texture2d tex , float x , float y , float w , float h , float u0 , float v0 , float u1 , float v1 )
{
float [ ] data = new float [ 16 ] {
x , y , u0 , v0 ,
x + w , y , u1 , v0 ,
x , y + h , u0 , v1 ,
x + w , y + h , u1 , v1
} ;
if ( sTexture ! = tex )
CurrPipeline [ "uSampler0" ] . Set ( sTexture = tex ) ;
if ( _Projection . IsDirty )
{
CurrPipeline [ "um44Projection" ] . Set ( ref _Projection . Top ) ;
_Projection . IsDirty = false ;
}
if ( _Modelview . IsDirty )
{
CurrPipeline [ "um44Modelview" ] . Set ( ref _Modelview . Top ) ;
_Modelview . IsDirty = false ;
}
fixed ( float * pData = & data [ 0 ] )
{
Owner . BindArrayData ( pData ) ;
Owner . DrawArrays ( PrimitiveType . TriangleStrip , 0 , 4 ) ;
}
}
public bool IsActive { get ; private set ; }
public IGL Owner { get ; private set ; }
VertexLayout VertexLayout ;
Pipeline CurrPipeline , DefaultPipeline ;
//state cache
Texture2d sTexture ;
public readonly string DefaultVertexShader = @ "
uniform mat4 um44Modelview , um44Projection ;
attribute vec2 aPosition ;
attribute vec2 aTexcoord ;
varying vec2 vTexcoord0 ;
void main ( )
{
vec4 temp = vec4 ( aPosition , 0 , 1 ) ;
gl_Position = um44Projection * ( um44Modelview * temp ) ;
vTexcoord0 = aTexcoord ;
} ";
public readonly string DefaultPixelShader = @ "
uniform sampler2D uSampler0 ;
uniform vec4 uModulateColor ;
varying vec2 vTexcoord0 ;
void main ( )
{
vec4 temp = texture2D ( uSampler0 , vTexcoord0 ) ;
temp * = uModulateColor ;
gl_FragColor = temp ;
} ";
}
}