using System; using System.Drawing; using System.Drawing.Imaging; using OpenTK; namespace ClassicalSharp.GraphicsAPI { /// Abstracts a 3D graphics rendering API. public abstract class IGraphicsApi { /// Maximum supported length of a dimension (width and height) of a 2D texture. public abstract int MaxTextureDimensions { get; } /// Sets whether texturing is applied when rasterizing primitives. public abstract bool Texturing { set; } internal float MinZNear = 0.1f; /// Creates a new native texture with the specified dimensions and using the /// image data encapsulated by the Bitmap instance. /// Note that should make every effort you can to ensure that the dimensions of the bitmap /// are powers of two, because otherwise they will not display properly on certain graphics cards.
/// This method returns -1 if the input image is not a 32bpp format.
public int CreateTexture( Bitmap bmp ) { if( !FastBitmap.CheckFormat( bmp.PixelFormat ) ) { string line2 = String.Format( "input bitmap was not 32bpp, it was {0}", bmp.PixelFormat ); ErrorHandler.LogError( "IGraphicsApi.CreateTexture()", Environment.NewLine + line2 + Environment.NewLine + Environment.StackTrace ); return -1; } else { Rectangle rec = new Rectangle( 0, 0, bmp.Width, bmp.Height ); BitmapData data = bmp.LockBits( rec, ImageLockMode.ReadOnly, bmp.PixelFormat ); int texId = CreateTexture( data.Width, data.Height, data.Scan0 ); bmp.UnlockBits( data ); return texId; } } /// Creates a new native texture with the specified dimensions and FastBitmap instance /// that encapsulates the pointer to the 32bpp image data. /// Note that should make every effort you can to ensure that the dimensions are powers of two, /// because otherwise they will not display properly on certain graphics cards. public int CreateTexture( FastBitmap bmp ) { if( !bmp.IsLocked ) bmp.LockBits(); int texId = CreateTexture( bmp.Width, bmp.Height, bmp.Scan0 ); bmp.UnlockBits(); return texId; } /// Creates a new native texture with the specified dimensions and pointer to the 32bpp image data. /// Note that should make every effort you can to ensure that the dimensions are powers of two, /// because otherwise they will not display properly on certain graphics cards. public abstract int CreateTexture( int width, int height, IntPtr scan0 ); /// Updates the sub-rectangle (texX, texY) -> (texX + part.Width, texY + part.Height) /// of the native texture associated with the given ID, with the pixels encapsulated in the 'part' instance. public abstract void UpdateTexturePart( int texId, int texX, int texY, FastBitmap part ); /// Binds the given texture id so that it can be used for rasterization. public abstract void BindTexture( int texId ); /// Frees all native resources held for the given texture id. public abstract void DeleteTexture( ref int texId ); /// Frees all native resources held for the given texture id. public void DeleteTexture( ref Texture texture ) { DeleteTexture( ref texture.ID ); } /// Sets whether fog is currently enabled. public abstract bool Fog { set; } /// Sets the fog colour that is blended with final primitive colours. public abstract void SetFogColour( FastColour col ); /// Sets the density of exp and exp^2 fog public abstract void SetFogDensity( float value ); /// Sets the start radius of fog for linear fog. public abstract void SetFogStart( float value ); /// Sets the end radius of fog for for linear fog. public abstract void SetFogEnd( float value ); /// Sets the current fog mode. (linear, exp, or exp^2) public abstract void SetFogMode( Fog fogMode ); /// Whether back facing primitives should be culled by the 3D graphics api. public abstract bool FaceCulling { set; } /// Whether alpha testing is currently enabled. public abstract bool AlphaTest { set; } /// Sets the alpha test compare function that is used when alpha testing is enabled. public abstract void AlphaTestFunc( CompareFunc func, float refValue ); /// Whether alpha blending is currently enabled. public abstract bool AlphaBlending { set; } /// Sets the alpha blend function that is used when alpha blending is enabled. public abstract void AlphaBlendFunc( BlendFunc srcFunc, BlendFunc dstFunc ); /// Clears the underlying back and/or front buffer. public abstract void Clear(); /// Sets the colour the screen is cleared to when Clear() is called. public abstract void ClearColour( FastColour col ); /// Whether depth testing is currently enabled. public abstract bool DepthTest { set; } /// Sets the depth test compare function that is used when depth testing is enabled. public abstract void DepthTestFunc( CompareFunc func ); /// Sets whether writing to the colour buffer is enabled. public abstract bool ColourWrite { set; } /// Sets whether writing to the depth buffer is enabled. public abstract bool DepthWrite { set; } /// Creates a vertex buffer that can have its data dynamically updated. public abstract int CreateDynamicVb( VertexFormat format, int maxVertices ); /// Creates a static vertex buffer that has its data set at creation, /// but the vertex buffer's data cannot be updated after creation. public abstract int CreateVb( T[] vertices, VertexFormat format, int count ) where T : struct; /// Creates a static vertex buffer that has its data set at creation, /// but the vertex buffer's data cannot be updated after creation. public abstract int CreateVb( IntPtr vertices, VertexFormat format, int count ); /// Creates a static index buffer that has its data set at creation, /// but the index buffer's data cannot be updated after creation. public abstract int CreateIb( ushort[] indices, int indicesCount ); /// Creates a static vertex buffer that has its data set at creation, /// but the index buffer's data cannot be updated after creation. public abstract int CreateIb( IntPtr indices, int indicesCount ); /// Sets the currently active vertex buffer to the given id. public abstract void BindVb( int vb ); /// Sets the currently active index buffer to the given id. public abstract void BindIb( int ib ); /// Frees all native resources held for the dynamic vertex buffer associated with the given id. public abstract void DeleteDynamicVb( int id ); /// Frees all native resources held for the vertex buffer associated with the given id. public abstract void DeleteVb( int vb ); /// Frees all native resources held for the index buffer associated with the given id. public abstract void DeleteIb( int ib ); /// Informs the graphics API that the format of the vertex data used in subsequent /// draw calls will be in the given format. public abstract void SetBatchFormat( VertexFormat format ); /// Draws the specified subset of the vertices in the current dynamic vertex buffer
/// This method also replaces the dynamic vertex buffer's data first with the given vertices before drawing.
public abstract void DrawDynamicVb( DrawMode mode, int vb, T[] vertices, int count ) where T : struct; /// Draws the specified subset of the vertices in the current dynamic vertex buffer
/// This method also replaces the dynamic vertex buffer's data first with the given vertices before drawing.
public abstract void DrawDynamicIndexedVb( DrawMode mode, int vb, T[] vertices, int vCount, int indicesCount ) where T : struct; /// Draws the specified subset of the vertices in the current vertex buffer. public abstract void DrawVb( DrawMode mode, int startVertex, int verticesCount ); /// Draws the specified subset of the vertices in the current vertex buffer. public abstract void DrawIndexedVb( DrawMode mode, int indicesCount, int startIndex ); /// Optimised version of DrawIndexedVb for VertexFormat.Pos3fTex2fCol4b internal abstract void DrawIndexedVb_TrisT2fC4b( int indicesCount, int offsetVertex, int startIndex ); internal abstract void DrawIndexedVb_TrisT2fC4b( int indicesCount, int startIndex ); protected static int[] strideSizes = { 16, 24 }; /// Sets the matrix type that load/push/pop operations should be applied to. public abstract void SetMatrixMode( MatrixType mode ); /// Sets the current matrix to the given matrix. public abstract void LoadMatrix( ref Matrix4 matrix ); /// Sets the current matrix to the identity matrix. public abstract void LoadIdentityMatrix(); /// Multplies the current matrix by the given matrix, then /// sets the current matrix to the result of the multiplication. public abstract void MultiplyMatrix( ref Matrix4 matrix ); /// Gets the top matrix the current matrix stack and pushes it to the stack. public abstract void PushMatrix(); /// Removes the top matrix from the current matrix stack, then /// sets the current matrix to the new top matrix of the stack. public abstract void PopMatrix(); /// Outputs a .png screenshot of the backbuffer to the specified file. public abstract void TakeScreenshot( string output, Size size ); protected abstract void MakeApiInfo(); public string[] ApiInfo; public void MakeGraphicsInfo() { MakeApiInfo(); ErrorHandler.AdditionalInfo = ApiInfo; } /// Adds a warning to chat if this graphics API has problems with the current user's GPU. public virtual void WarnIfNecessary( ChatLog chat ) { } /// Informs the graphic api to update its state in preparation for a new frame. public abstract void BeginFrame( GameWindow game ); /// Informs the graphic api to update its state in preparation for the end of a frame, /// and to prepare that frame for display on the monitor. public abstract void EndFrame( GameWindow game ); /// Sets whether the graphics api should tie frame rendering to the refresh rate of the monitor. public abstract void SetVSync( GameWindow game, bool value ); /// Raised when the dimensions of the game's window have changed. public abstract void OnWindowResize( GameWindow game ); /// Delegate that is invoked when the current context is lost, /// and is repeatedly invoked until the context can be retrieved. public Action LostContextFunction; protected void InitDynamicBuffers() { quadVb = CreateDynamicVb( VertexFormat.Pos3fCol4b, 4 ); texVb = CreateDynamicVb( VertexFormat.Pos3fTex2fCol4b, 4 ); } public virtual void Dispose() { DeleteDynamicVb( quadVb ); DeleteDynamicVb( texVb ); } VertexPos3fCol4b[] quadVerts = new VertexPos3fCol4b[4]; int quadVb; public virtual void Draw2DQuad( float x, float y, float width, float height, FastColour col ) { quadVerts[0] = new VertexPos3fCol4b( x, y, 0, col ); quadVerts[1] = new VertexPos3fCol4b( x + width, y, 0, col ); quadVerts[2] = new VertexPos3fCol4b( x + width, y + height, 0, col ); quadVerts[3] = new VertexPos3fCol4b( x, y + height, 0, col ); SetBatchFormat( VertexFormat.Pos3fCol4b ); DrawDynamicIndexedVb( DrawMode.Triangles, quadVb, quadVerts, 4, 6 ); } internal VertexPos3fTex2fCol4b[] texVerts = new VertexPos3fTex2fCol4b[4]; internal int texVb; public virtual void Draw2DTexture( ref Texture tex, FastColour col ) { float x1 = tex.X1, y1 = tex.Y1, x2 = tex.X2, y2 = tex.Y2; #if USE_DX // NOTE: see "https://msdn.microsoft.com/en-us/library/windows/desktop/bb219690(v=vs.85).aspx", // i.e. the msdn article called "Directly Mapping Texels to Pixels (Direct3D 9)" for why we have to do this. x1 -= 0.5f; x2 -= 0.5f; y1 -= 0.5f; y2 -= 0.5f; #endif texVerts[0] = new VertexPos3fTex2fCol4b( x1, y1, 0, tex.U1, tex.V1, col ); texVerts[1] = new VertexPos3fTex2fCol4b( x2, y1, 0, tex.U2, tex.V1, col ); texVerts[2] = new VertexPos3fTex2fCol4b( x2, y2, 0, tex.U2, tex.V2, col ); texVerts[3] = new VertexPos3fTex2fCol4b( x1, y2, 0, tex.U1, tex.V2, col ); SetBatchFormat( VertexFormat.Pos3fTex2fCol4b ); DrawDynamicIndexedVb( DrawMode.Triangles, texVb, texVerts, 4, 6 ); } public static void Make2DQuad( TextureRec xy, TextureRec uv, VertexPos3fTex2fCol4b[] vertices, ref int index ) { float x1 = xy.U1, y1 = xy.V1, x2 = xy.U2, y2 = xy.V2; #if USE_DX x1 -= 0.5f; x2 -= 0.5f; y1 -= 0.5f; y2 -= 0.5f; #endif vertices[index++] = new VertexPos3fTex2fCol4b( x1, y1, 0, uv.U1, uv.V1, FastColour.White ); vertices[index++] = new VertexPos3fTex2fCol4b( x2, y1, 0, uv.U2, uv.V1, FastColour.White ); vertices[index++] = new VertexPos3fTex2fCol4b( x2, y2, 0, uv.U2, uv.V2, FastColour.White ); vertices[index++] = new VertexPos3fTex2fCol4b( x1, y2, 0, uv.U1, uv.V2, FastColour.White ); } public void Draw2DTexture( ref Texture tex ) { Draw2DTexture( ref tex, FastColour.White ); } /// Updates the various matrix stacks and properties so that the graphics API state /// is suitable for rendering 2D quads and other 2D graphics to. public void Mode2D( float width, float height, bool setFog ) { SetMatrixMode( MatrixType.Projection ); PushMatrix(); LoadOrthoMatrix( width, height ); SetMatrixMode( MatrixType.Modelview ); PushMatrix(); LoadIdentityMatrix(); DepthTest = false; AlphaBlending = true; if( setFog ) Fog = false; } protected virtual void LoadOrthoMatrix( float width, float height ) { Matrix4 matrix = Matrix4.CreateOrthographicOffCenter( 0, width, height, 0, -10000, 10000 ); LoadMatrix( ref matrix ); } /// Updates the various matrix stacks and properties so that the graphics API state /// is suitable for rendering 3D vertices. public void Mode3D( bool setFog ) { SetMatrixMode( MatrixType.Projection ); PopMatrix(); // Get rid of orthographic 2D matrix. SetMatrixMode( MatrixType.Modelview ); PopMatrix(); DepthTest = true; AlphaBlending = false; if( setFog ) Fog = true; } internal unsafe int MakeDefaultIb() { const int maxIndices = 65536 / 4 * 6; int element = 0; ushort* indices = stackalloc ushort[maxIndices]; IntPtr ptr = (IntPtr)indices; for( int i = 0; i < maxIndices; i += 6 ) { *indices++ = (ushort)(element + 0); *indices++ = (ushort)(element + 1); *indices++ = (ushort)(element + 2); *indices++ = (ushort)(element + 2); *indices++ = (ushort)(element + 3); *indices++ = (ushort)(element + 0); element += 4; } return CreateIb( ptr, maxIndices ); } } public enum VertexFormat { Pos3fCol4b = 0, Pos3fTex2fCol4b = 1, } public enum DrawMode { Triangles = 0, Lines = 1, } public enum CompareFunc { Always = 0, NotEqual = 1, Never = 2, Less = 3, LessEqual = 4, Equal = 5, GreaterEqual = 6, Greater = 7, } public enum BlendFunc { Zero = 0, One = 1, SourceAlpha = 2, InvSourceAlpha = 3, DestAlpha = 4, InvDestAlpha = 5, } public enum Fog { Linear = 0, Exp = 1, Exp2 = 2, } public enum MatrixType { Projection = 0, Modelview = 1, Texture = 2, } }