2014-01-27 00:02:21 +00:00
//http://stackoverflow.com/questions/6893302/decode-rgb-value-to-single-float-without-bit-shift-in-glsl
2014-12-07 02:26:52 +00:00
//why this stupid assert on the blendstate. just set one by default, geeze.
2014-01-27 00:02:21 +00:00
using System ;
2014-04-15 21:46:18 +00:00
using System.Diagnostics ;
2014-02-06 09:45:55 +00:00
using System.Collections ;
using System.Collections.Generic ;
2015-08-20 23:35:53 +00:00
using sd = System . Drawing ;
2014-01-27 00:02:21 +00:00
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>
2014-12-07 02:26:52 +00:00
public class GuiRenderer : IDisposable , IGuiRenderer
2014-01-27 00:02:21 +00:00
{
public GuiRenderer ( IGL owner )
{
Owner = owner ;
VertexLayout = owner . CreateVertexLayout ( ) ;
2014-12-08 02:15:42 +00:00
VertexLayout . DefineVertexAttribute ( "aPosition" , 0 , 2 , VertexAttribPointerType . Float , AttributeUsage . Position , false , 32 , 0 ) ;
VertexLayout . DefineVertexAttribute ( "aTexcoord" , 1 , 2 , VertexAttribPointerType . Float , AttributeUsage . Texcoord0 , false , 32 , 8 ) ;
VertexLayout . DefineVertexAttribute ( "aColor" , 2 , 4 , VertexAttribPointerType . Float , AttributeUsage . Texcoord1 , false , 32 , 16 ) ;
2014-01-27 00:02:21 +00:00
VertexLayout . Close ( ) ;
_Projection = new MatrixStack ( ) ;
_Modelview = new MatrixStack ( ) ;
2014-12-08 02:15:42 +00:00
string psProgram , vsProgram ;
if ( owner . API = = "D3D9" )
{
vsProgram = DefaultShader_d3d9 ;
psProgram = DefaultShader_d3d9 ;
}
else
{
vsProgram = DefaultVertexShader_gl ;
psProgram = DefaultPixelShader_gl ;
}
2015-08-20 23:35:53 +00:00
var vs = Owner . CreateVertexShader ( false , vsProgram , "vsmain" , true ) ;
var ps = Owner . CreateFragmentShader ( false , psProgram , "psmain" , true ) ;
CurrPipeline = DefaultPipeline = Owner . CreatePipeline ( VertexLayout , vs , ps , true , "xgui" ) ;
2014-01-27 00:02:21 +00:00
}
2014-02-06 09:45:55 +00:00
OpenTK . Graphics . Color4 [ ] CornerColors = new OpenTK . Graphics . Color4 [ 4 ] {
new OpenTK . Graphics . Color4 ( 1.0f , 1.0f , 1.0f , 1.0f ) , new OpenTK . Graphics . Color4 ( 1.0f , 1.0f , 1.0f , 1.0f ) , new OpenTK . Graphics . Color4 ( 1.0f , 1.0f , 1.0f , 1.0f ) , new OpenTK . Graphics . Color4 ( 1.0f , 1.0f , 1.0f , 1.0f )
} ;
2014-12-07 02:26:52 +00:00
2014-02-06 09:45:55 +00:00
public void SetCornerColor ( int which , OpenTK . Graphics . Color4 color )
{
Flush ( ) ; //dont really need to flush with current implementation. we might as well roll modulate color into it too.
CornerColors [ which ] = color ;
}
2015-08-20 23:35:53 +00:00
2014-02-06 09:45:55 +00:00
public void SetCornerColors ( OpenTK . Graphics . Color4 [ ] colors )
{
Flush ( ) ; //dont really need to flush with current implementation. we might as well roll modulate color into it too.
if ( colors . Length ! = 4 ) throw new ArgumentException ( "array must be size 4" , "colors" ) ;
for ( int i = 0 ; i < 4 ; i + + )
CornerColors [ i ] = colors [ i ] ;
}
2014-01-27 00:02:21 +00:00
public void Dispose ( )
{
DefaultPipeline . Dispose ( ) ;
}
public void SetPipeline ( Pipeline pipeline )
{
if ( IsActive )
throw new InvalidOperationException ( "Can't change pipeline while renderer is running!" ) ;
Flush ( ) ;
CurrPipeline = pipeline ;
2014-02-06 09:45:55 +00:00
//clobber state cache
sTexture = null ;
//save the modulate color? user beware, I guess, for now.
2014-01-27 00:02:21 +00:00
}
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 )
{
2015-08-20 23:35:53 +00:00
#if DEBUG
2014-04-15 21:46:18 +00:00
BlendStateSet = true ;
2015-08-20 23:35:53 +00:00
#endif
2014-01-27 00:02:21 +00:00
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-04-15 21:46:18 +00:00
public void Begin ( sd . Size size ) { Begin ( size . Width , size . Height ) ; }
2014-01-27 09:36:18 +00:00
2015-08-30 14:19:49 +00:00
public void Begin ( int width , int height )
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
Owner . SetViewport ( width , height ) ;
2014-01-27 00:02:21 +00:00
}
2014-12-07 02:26:52 +00:00
2014-01-27 00:02:21 +00:00
public void Begin ( )
{
//uhhmmm I want to throw an exception if its already active, but its annoying.
2015-08-20 23:35:53 +00:00
if ( CurrPipeline = = null )
2014-01-27 00:02:21 +00:00
throw new InvalidOperationException ( "Pipeline hasn't been set!" ) ;
2015-08-20 23:35:53 +00:00
2014-01-27 00:02:21 +00:00
IsActive = true ;
Owner . BindPipeline ( CurrPipeline ) ;
//clear state cache
sTexture = null ;
2014-02-06 09:45:55 +00:00
CurrPipeline [ "uSamplerEnable" ] . Set ( false ) ;
2014-01-27 00:02:21 +00:00
Modelview . Clear ( ) ;
Projection . Clear ( ) ;
SetModulateColorWhite ( ) ;
2014-04-15 21:46:18 +00:00
2015-08-20 23:35:53 +00:00
#if DEBUG
BlendStateSet = false ;
#endif
2014-01-27 00:02:21 +00:00
}
2014-12-07 02:26:52 +00:00
2014-01-27 00:02:21 +00:00
public void Flush ( )
{
//no batching, nothing to do here yet
}
2014-12-07 02:26:52 +00:00
2014-01-27 00:02:21 +00:00
public void End ( )
{
if ( ! IsActive )
throw new InvalidOperationException ( "GuiRenderer is not active!" ) ;
IsActive = false ;
}
2014-02-06 09:45:55 +00:00
public void RectFill ( float x , float y , float w , float h )
{
PrepDrawSubrectInternal ( null ) ;
EmitRectangleInternal ( x , y , w , h , 0 , 0 , 0 , 0 ) ;
}
2014-12-07 02:26:52 +00:00
2014-01-27 00:02:21 +00:00
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 ) ;
}
2014-12-07 02:26:52 +00:00
2014-01-27 00:02:21 +00:00
public void Draw ( Art art ) { DrawInternal ( art , 0 , 0 , art . Width , art . Height , false , false ) ; }
2014-12-07 02:26:52 +00:00
2014-01-27 00:02:21 +00:00
public void Draw ( Art art , float x , float y ) { DrawInternal ( art , x , y , art . Width , art . Height , false , false ) ; }
2014-12-07 02:26:52 +00:00
2014-01-27 00:02:21 +00:00
public void Draw ( Art art , float x , float y , float width , float height ) { DrawInternal ( art , x , y , width , height , false , false ) ; }
2014-12-07 02:26:52 +00:00
2014-01-27 00:02:21 +00:00
public void Draw ( Art art , Vector2 pos ) { DrawInternal ( art , pos . X , pos . Y , art . Width , art . Height , false , false ) ; }
2014-12-07 02:26:52 +00:00
2014-01-27 00:02:21 +00:00
public void Draw ( Texture2d tex ) { DrawInternal ( tex , 0 , 0 , tex . Width , tex . Height ) ; }
2014-12-07 02:26:52 +00:00
2014-01-27 09:36:18 +00:00
public void Draw ( Texture2d tex , float x , float y ) { DrawInternal ( tex , x , y , tex . Width , tex . Height ) ; }
2014-12-07 02:26:52 +00:00
2014-01-27 00:02:21 +00:00
public void DrawFlipped ( Art art , bool xflip , bool yflip ) { DrawInternal ( art , 0 , 0 , art . Width , art . Height , xflip , yflip ) ; }
2014-05-02 04:27:08 +00:00
public void Draw ( Texture2d art , float x , float y , float width , float height )
{
DrawInternal ( art , x , y , width , height ) ;
}
2014-01-27 00:02:21 +00:00
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 ;
2015-08-20 23:35:53 +00:00
DrawInternal ( art , x , y , w , h , false , tex . IsUpsideDown ) ;
2014-01-27 00:02:21 +00:00
}
unsafe void DrawInternal ( Art art , float x , float y , float w , float h , bool fx , bool fy )
{
2015-08-20 23:35:53 +00:00
//TEST: d3d shouldnt ever use this, it was a gl hack. maybe we can handle it some other way in gl (fix the projection? take a render-to-texture arg to the gui view transforms?)
fy = false ;
float u0 , v0 , u1 , v1 ;
if ( fx ) { u0 = art . u1 ; u1 = art . u0 ; }
2014-01-27 00:02:21 +00:00
else { u0 = art . u0 ; u1 = art . u1 ; }
2015-08-20 23:35:53 +00:00
if ( fy ) { v0 = art . v1 ; v1 = art . v0 ; }
2014-01-27 00:02:21 +00:00
else { v0 = art . v0 ; v1 = art . v1 ; }
2014-02-06 09:45:55 +00:00
float [ ] data = new float [ 32 ] {
x , y , u0 , v0 , CornerColors [ 0 ] . R , CornerColors [ 0 ] . G , CornerColors [ 0 ] . B , CornerColors [ 0 ] . A ,
x + art . Width , y , u1 , v0 , CornerColors [ 1 ] . R , CornerColors [ 1 ] . G , CornerColors [ 1 ] . B , CornerColors [ 1 ] . A ,
x , y + art . Height , u0 , v1 , CornerColors [ 2 ] . R , CornerColors [ 2 ] . G , CornerColors [ 2 ] . B , CornerColors [ 2 ] . A ,
x + art . Width , y + art . Height , u1 , v1 , CornerColors [ 3 ] . R , CornerColors [ 3 ] . G , CornerColors [ 3 ] . B , CornerColors [ 3 ] . A ,
2014-01-27 00:02:21 +00:00
} ;
Texture2d tex = art . BaseTexture ;
2015-08-20 23:35:53 +00:00
2014-02-06 09:45:55 +00:00
PrepDrawSubrectInternal ( tex ) ;
2014-01-27 00:02:21 +00:00
fixed ( float * pData = & data [ 0 ] )
{
Owner . BindArrayData ( pData ) ;
Owner . DrawArrays ( PrimitiveType . TriangleStrip , 0 , 4 ) ;
}
}
2014-02-06 09:45:55 +00:00
unsafe void PrepDrawSubrectInternal ( Texture2d tex )
2014-01-27 00:02:21 +00:00
{
if ( sTexture ! = tex )
2014-02-06 09:45:55 +00:00
{
sTexture = tex ;
CurrPipeline [ "uSampler0" ] . Set ( tex ) ;
if ( sTexture = = null )
{
CurrPipeline [ "uSamplerEnable" ] . Set ( false ) ;
}
else
{
CurrPipeline [ "uSamplerEnable" ] . Set ( true ) ;
}
}
2014-01-27 00:02:21 +00:00
if ( _Projection . IsDirty )
{
2015-08-20 23:35:53 +00:00
CurrPipeline [ "um44Projection" ] . Set ( ref _Projection . Top , false ) ;
2014-01-27 00:02:21 +00:00
_Projection . IsDirty = false ;
}
if ( _Modelview . IsDirty )
{
2015-08-20 23:35:53 +00:00
CurrPipeline [ "um44Modelview" ] . Set ( ref _Modelview . Top , false ) ;
2014-01-27 00:02:21 +00:00
_Modelview . IsDirty = false ;
}
2014-02-06 09:45:55 +00:00
}
unsafe void EmitRectangleInternal ( float x , float y , float w , float h , float u0 , float v0 , float u1 , float v1 )
{
float * pData = stackalloc float [ 32 ] ;
pData [ 0 ] = x ;
pData [ 1 ] = y ;
pData [ 2 ] = u0 ;
pData [ 3 ] = v0 ;
pData [ 4 ] = CornerColors [ 0 ] . R ;
pData [ 5 ] = CornerColors [ 0 ] . G ;
pData [ 6 ] = CornerColors [ 0 ] . B ;
pData [ 7 ] = CornerColors [ 0 ] . A ;
pData [ 8 ] = x + w ;
pData [ 9 ] = y ;
pData [ 10 ] = u1 ;
pData [ 11 ] = v0 ;
pData [ 12 ] = CornerColors [ 1 ] . R ;
pData [ 13 ] = CornerColors [ 1 ] . G ;
pData [ 14 ] = CornerColors [ 1 ] . B ;
pData [ 15 ] = CornerColors [ 1 ] . A ;
pData [ 16 ] = x ;
pData [ 17 ] = y + h ;
pData [ 18 ] = u0 ;
pData [ 19 ] = v1 ;
pData [ 20 ] = CornerColors [ 2 ] . R ;
pData [ 21 ] = CornerColors [ 2 ] . G ;
pData [ 22 ] = CornerColors [ 2 ] . B ;
pData [ 23 ] = CornerColors [ 2 ] . A ;
pData [ 24 ] = x + w ;
pData [ 25 ] = y + h ;
pData [ 26 ] = u1 ;
pData [ 27 ] = v1 ;
pData [ 28 ] = CornerColors [ 3 ] . R ;
pData [ 29 ] = CornerColors [ 3 ] . G ;
pData [ 30 ] = CornerColors [ 3 ] . B ;
pData [ 31 ] = CornerColors [ 3 ] . A ;
2014-01-27 00:02:21 +00:00
2014-01-29 00:53:04 +00:00
Owner . BindArrayData ( pData ) ;
Owner . DrawArrays ( PrimitiveType . TriangleStrip , 0 , 4 ) ;
2014-04-15 21:46:18 +00:00
2015-08-20 23:35:53 +00:00
#if DEBUG
2014-04-15 21:46:18 +00:00
Debug . Assert ( BlendStateSet ) ;
2015-08-20 23:35:53 +00:00
#endif
2014-01-27 00:02:21 +00:00
}
2014-02-06 09:45:55 +00:00
unsafe void DrawSubrectInternal ( Texture2d tex , float x , float y , float w , float h , float u0 , float v0 , float u1 , float v1 )
{
PrepDrawSubrectInternal ( tex ) ;
EmitRectangleInternal ( x , y , w , h , u0 , v0 , u1 , v1 ) ;
}
2015-08-20 23:35:53 +00:00
2014-01-27 00:02:21 +00:00
public bool IsActive { get ; private set ; }
public IGL Owner { get ; private set ; }
VertexLayout VertexLayout ;
Pipeline CurrPipeline , DefaultPipeline ;
//state cache
Texture2d sTexture ;
2015-08-20 23:35:53 +00:00
#if DEBUG
bool BlendStateSet ;
#endif
2014-01-27 00:02:21 +00:00
2015-08-20 23:35:53 +00:00
//shaders are hand-coded for each platform to make sure they stay as fast as possible
2014-12-08 02:15:42 +00:00
2015-08-20 23:35:53 +00:00
public readonly string DefaultShader_d3d9 = @ "
//vertex shader uniforms
2014-12-08 02:15:42 +00:00
float4x4 um44Modelview , um44Projection ;
float4 uModulateColor ;
2015-08-20 23:35:53 +00:00
//pixel shader uniforms
2014-12-08 02:15:42 +00:00
bool uSamplerEnable ;
2015-08-20 23:35:53 +00:00
texture2D texture0 , texture1 ;
2014-12-08 02:15:42 +00:00
sampler uSampler0 = sampler_state { Texture = ( texture0 ) ; } ;
struct VS_INPUT
{
float2 aPosition : POSITION ;
float2 aTexcoord : TEXCOORD0 ;
float4 aColor : TEXCOORD1 ;
} ;
struct VS_OUTPUT
{
float4 vPosition : POSITION ;
float2 vTexcoord0 : TEXCOORD0 ;
2015-10-24 06:32:44 +00:00
float4 vCornerColor : COLOR0 ;
2014-12-08 02:15:42 +00:00
} ;
struct PS_INPUT
{
float2 vTexcoord0 : TEXCOORD0 ;
2015-10-24 06:32:44 +00:00
float4 vCornerColor : COLOR0 ;
2014-12-08 02:15:42 +00:00
} ;
VS_OUTPUT vsmain ( VS_INPUT src )
{
VS_OUTPUT dst ;
float4 temp = float4 ( src . aPosition , 0 , 1 ) ;
2015-08-20 23:35:53 +00:00
dst . vPosition = mul ( um44Projection , mul ( um44Modelview , temp ) ) ;
2014-12-08 02:15:42 +00:00
dst . vTexcoord0 = src . aTexcoord ;
dst . vCornerColor = src . aColor * uModulateColor ;
return dst ;
}
float4 psmain ( PS_INPUT src ) : COLOR
{
float4 temp = src . vCornerColor ;
if ( uSamplerEnable ) temp * = tex2D ( uSampler0 , src . vTexcoord0 ) ;
return temp ;
}
";
public readonly string DefaultVertexShader_gl = @ "
2014-01-27 19:24:05 +00:00
# version 110 //opengl 2.0 ~ 2004
2014-01-27 00:02:21 +00:00
uniform mat4 um44Modelview , um44Projection ;
2014-02-06 09:45:55 +00:00
uniform vec4 uModulateColor ;
2014-01-27 00:02:21 +00:00
2015-08-20 23:35:53 +00:00
//attribute vec2 aPosition : gl_Vertex;
//attribute vec2 aTexcoord : gl_MultiTexCoord0;
//attribute vec4 aColor : gl_Color;
#define aPosition vec2 ( gl_Vertex . xy )
#define aTexcoord vec2 ( gl_MultiTexCoord0 . xy )
#define aColor gl_Color
2014-01-27 00:02:21 +00:00
varying vec2 vTexcoord0 ;
2014-02-06 09:45:55 +00:00
varying vec4 vCornerColor ;
2014-01-27 00:02:21 +00:00
void main ( )
{
2014-02-06 09:45:55 +00:00
vec4 temp = vec4 ( aPosition , 0 , 1 ) ;
gl_Position = um44Projection * ( um44Modelview * temp ) ;
vTexcoord0 = aTexcoord ;
vCornerColor = aColor * uModulateColor ;
2014-01-27 00:02:21 +00:00
} ";
2014-12-08 02:15:42 +00:00
public readonly string DefaultPixelShader_gl = @ "
2014-01-27 19:24:05 +00:00
# version 110 //opengl 2.0 ~ 2004
2014-02-06 09:45:55 +00:00
uniform bool uSamplerEnable ;
2014-01-27 00:02:21 +00:00
uniform sampler2D uSampler0 ;
varying vec2 vTexcoord0 ;
2014-02-06 09:45:55 +00:00
varying vec4 vCornerColor ;
2014-01-27 00:02:21 +00:00
void main ( )
{
2014-02-06 09:45:55 +00:00
vec4 temp = vCornerColor ;
if ( uSamplerEnable ) temp * = texture2D ( uSampler0 , vTexcoord0 ) ;
2014-01-27 00:02:21 +00:00
gl_FragColor = temp ;
} ";
}
}