Files
Vocaluxe/Vocaluxe/Lib/Draw/COpenGL.cs
T
Flamefire 25653ee55f Revert "Remove bounds from DrawTextureReflection because it was never used"
This reverts commit f5f1a91481.

Conflicts:
	Vocaluxe/Base/CMain.cs
	Vocaluxe/Screens/CScreenSing.cs
	VocaluxeLib/IBasic.cs
	VocaluxeLib/Menu/CStatic.cs
	VocaluxeLib/Menu/SongMenu/CSongMenuTileBoard.cs
2014-09-26 15:04:08 +02:00

731 lines
24 KiB
C#

#region license
// This file is part of Vocaluxe.
//
// Vocaluxe is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Vocaluxe is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
#endregion
using System.Drawing.Imaging;
using OpenTK;
using OpenTK.Graphics;
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Vocaluxe.Base;
using VocaluxeLib;
using VocaluxeLib.Draw;
using BeginMode = OpenTK.Graphics.OpenGL.BeginMode;
using BlendingFactorDest = OpenTK.Graphics.OpenGL.BlendingFactorDest;
using BlendingFactorSrc = OpenTK.Graphics.OpenGL.BlendingFactorSrc;
using ClearBufferMask = OpenTK.Graphics.OpenGL.ClearBufferMask;
using DepthFunction = OpenTK.Graphics.OpenGL.DepthFunction;
using EnableCap = OpenTK.Graphics.OpenGL.EnableCap;
using GL = OpenTK.Graphics.OpenGL.GL;
using GenerateMipmapTarget = OpenTK.Graphics.OpenGL.GenerateMipmapTarget;
using MatrixMode = OpenTK.Graphics.OpenGL.MatrixMode;
using PixelFormat = OpenTK.Graphics.OpenGL.PixelFormat;
using PixelInternalFormat = OpenTK.Graphics.OpenGL.PixelInternalFormat;
using PixelStoreParameter = OpenTK.Graphics.OpenGL.PixelStoreParameter;
using PixelType = OpenTK.Graphics.OpenGL.PixelType;
using TextureMagFilter = OpenTK.Graphics.OpenGL.TextureMagFilter;
using TextureMinFilter = OpenTK.Graphics.OpenGL.TextureMinFilter;
using TextureParameterName = OpenTK.Graphics.OpenGL.TextureParameterName;
using TextureTarget = OpenTK.Graphics.OpenGL.TextureTarget;
namespace Vocaluxe.Lib.Draw
{
class CFormHook : Form, IFormHook
{
public MessageEventHandler OnMessage { private get; set; }
protected override void WndProc(ref Message m)
{
if (OnMessage == null || OnMessage(ref m))
base.WndProc(ref m);
}
}
class COGLTexture : CTextureBase
{
//The texture "name" according to the specs
public readonly int Name;
public COGLTexture(int name, Size dataSize, int texWidth = 0, int texHeight = 0) : base(dataSize, texWidth, texHeight)
{
Name = name;
if (name == 0)
return;
GL.BindTexture(TextureTarget.Texture2D, Name);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, W2, H2, 0, PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
GL.BindTexture(TextureTarget.Texture2D, 0);
}
public override bool IsLoaded
{
get { return Name != 0; }
}
public override void Dispose()
{
base.Dispose();
if (Name != 0)
GL.DeleteTexture(Name);
}
}
class COpenGL : CDrawBaseWindows<COGLTexture>, IDraw
{
#region private vars
private readonly GLControl _Control;
#endregion private vars
public COpenGL()
{
_Form = new CFormHook();
//Check AA Mode
CConfig.AAMode = (EAntiAliasingModes)_CheckAntiAliasingMode((int)CConfig.AAMode);
bool ok = false;
try
{
var gm = new GraphicsMode(32, 24, 0, (int)CConfig.AAMode);
_Control = new GLControl(gm, 2, 1, GraphicsContextFlags.Default);
if (_Control.GraphicsMode != null)
ok = true;
}
catch (Exception)
{
ok = false;
}
if (!ok)
_Control = new GLControl();
_Control.MakeCurrent();
_Control.VSync = CConfig.VSync == EOffOn.TR_CONFIG_ON;
_Form.Controls.Add(_Control);
_Control.KeyDown += _OnKeyDown;
_Control.PreviewKeyDown += _OnPreviewKeyDown;
_Control.KeyPress += _OnKeyPress;
_Control.KeyUp += _OnKeyUp;
_Control.MouseMove += _OnMouseMove;
_Control.MouseWheel += _OnMouseWheel;
_Control.MouseDown += _OnMouseDown;
_Control.MouseUp += _OnMouseUp;
_Control.MouseLeave += _OnMouseLeave;
_Control.MouseEnter += _OnMouseEnter;
_NonPowerOf2TextureSupported = false;
}
#region Helpers
private static int _CheckAntiAliasingMode(int setValue)
{
int samples = 0;
if (setValue > 32)
setValue = 32;
while (samples <= setValue)
{
GraphicsMode mode;
try
{
mode = new GraphicsMode(16, 0, 0, samples);
}
catch (Exception)
{
break;
}
if (mode.Samples != samples)
break;
if (samples == 0)
samples = 2;
else
samples *= 2;
}
if (samples == 2)
return 0;
return samples / 2;
}
/*
private int _CheckColorDepth(int setValue)
{
int result = 16;
if (setValue > 32)
setValue = 32;
while (result <= setValue)
{
GraphicsMode mode;
try
{
mode = new GraphicsMode(result, 0, 0, 0);
}
catch (Exception)
{
break;
}
if (mode.ColorFormat != result)
break;
result += 8;
}
return result - 8;
}
*/
#endregion Helpers
#region form events
protected override void _OnResize(object sender, EventArgs e)
{
_Control.ClientSize = _Form.ClientSize;
base._OnResize(sender, e);
}
#endregion form events
protected override void _DoResize()
{
_H = _Control.Height;
_W = _Control.Width;
_CurrentAlignment = CConfig.ScreenAlignment;
_AdjustAspect(true);
_AdjustNewBorders();
GL.Viewport(_X, _Y, _W, _H);
}
#region implementation
#region main stuff
public override bool Init()
{
if (!base.Init())
return false;
//OpenGL needs that here but D3D needs it in the constructor, so do NOT unify!
_Form.ClientSize = new Size(CConfig.ScreenW, CConfig.ScreenH);
// Init Texturing
GL.Enable(EnableCap.Texture2D);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);
GL.DepthRange(CSettings.ZFar, CSettings.ZNear);
GL.DepthFunc(DepthFunction.Lequal);
GL.Enable(EnableCap.DepthTest);
GL.ClearColor(Color.Black);
return true;
}
protected override void _OnBeforeDraw()
{
//Nothing to do
}
protected override void _OnAfterDraw()
{
_Control.SwapBuffers();
Application.DoEvents();
}
public int GetScreenWidth()
{
return _Control.Width;
}
public int GetScreenHeight()
{
return _Control.Height;
}
protected override void _ClearScreen()
{
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
}
protected override void _AdjustNewBorders()
{
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(-CConfig.BorderLeft, CConfig.BorderRight + CSettings.RenderW, CConfig.BorderBottom + CSettings.RenderH, -CConfig.BorderTop, CSettings.ZNear, CSettings.ZFar);
}
public void MakeScreenShot()
{
string file = CHelper.GetUniqueFileName(Path.Combine(CSettings.DataFolder, CSettings.FolderNameScreenshots), "Screenshot.bmp");
int width = GetScreenWidth();
int height = GetScreenHeight();
using (var screen = new Bitmap(width, height))
{
BitmapData bmpData = screen.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
GL.ReadPixels(0, 0, width, height, PixelFormat.Bgra, PixelType.UnsignedByte, bmpData.Scan0);
screen.UnlockBits(bmpData);
screen.RotateFlip(RotateFlipType.RotateNoneFlipY);
screen.Save(file, ImageFormat.Bmp);
}
}
#endregion main stuff
#region Basic Draw Methods
public CTextureRef CopyScreen()
{
//TODO: Check if _W,_H needs to be used or not
Size size = new Size(GetScreenWidth(), GetScreenHeight());
COGLTexture texture = _CreateTexture(size);
GL.BindTexture(TextureTarget.Texture2D, texture.Name);
GL.CopyTexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, 0, 0, size.Width, size.Height); //TODO: Use _X,_Y and _W,_H?
GL.BindTexture(TextureTarget.Texture2D, 0);
return _GetTextureReference(size, texture);
}
public void CopyScreen(ref CTextureRef textureRef)
{
COGLTexture texture;
//Check for actual texture sizes as it may be downsized compared to OrigSize
if (!_GetTexture(textureRef, out texture) || texture.DataSize.Width != GetScreenWidth() || texture.DataSize.Height != GetScreenHeight())
{
RemoveTexture(ref textureRef);
textureRef = CopyScreen();
}
else
{
GL.BindTexture(TextureTarget.Texture2D, texture.Name);
GL.CopyTexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, 0, 0, GetScreenWidth(), GetScreenHeight());
GL.BindTexture(TextureTarget.Texture2D, 0);
}
}
public void DrawRect(SColorF color, SRectF rect)
{
GL.Enable(EnableCap.Blend);
GL.Color4(color.R, color.G, color.B, color.A * CGraphics.GlobalAlpha);
GL.Begin(BeginMode.Quads);
GL.MatrixMode(MatrixMode.Color);
GL.PushMatrix();
if (Math.Abs(rect.Rotation) > 0.001)
{
GL.Translate(0.5f, 0.5f, 0);
GL.Rotate(-rect.Rotation, 0f, 0f, 1f);
GL.Translate(-0.5f, -0.5f, 0);
}
GL.Vertex3(rect.X, rect.Y, rect.Z + CGraphics.ZOffset);
GL.Vertex3(rect.X, rect.Y + rect.H, rect.Z + CGraphics.ZOffset);
GL.Vertex3(rect.X + rect.W, rect.Y + rect.H, rect.Z + CGraphics.ZOffset);
GL.Vertex3(rect.X + rect.W, rect.Y, rect.Z + CGraphics.ZOffset);
GL.End();
GL.PopMatrix();
GL.Disable(EnableCap.Blend);
}
public void DrawRectReflection(SColorF color, SRectF rect, float space, float height)
{
if (rect.H < height)
height = rect.H;
float rx1 = rect.X;
float rx2 = rect.X + rect.W;
float ry1 = rect.Y + rect.H + space;
float ry2 = rect.Y + rect.H + space + height;
if (rx1 < rect.X)
rx1 = rect.X;
if (rx2 > rect.X + rect.W)
rx2 = rect.X + rect.W;
if (ry1 < rect.Y + space)
ry1 = rect.Y + space;
if (ry2 > rect.Y + rect.H + space + height)
ry2 = rect.Y + rect.H + space + height;
GL.Enable(EnableCap.Blend);
GL.MatrixMode(MatrixMode.Texture);
GL.PushMatrix();
if (Math.Abs(rect.Rotation) > 0.001)
{
GL.Translate(0.5f, 0.5f, 0);
GL.Rotate(-rect.Rotation, 0f, 0f, 1f);
GL.Translate(-0.5f, -0.5f, 0);
}
GL.Begin(BeginMode.Quads);
GL.Color4(color.R, color.G, color.B, color.A * CGraphics.GlobalAlpha);
GL.Vertex3(rx2, ry1, rect.Z + CGraphics.ZOffset);
GL.Color4(color.R, color.G, color.B, 0f);
GL.Vertex3(rx2, ry2, rect.Z + CGraphics.ZOffset);
GL.Color4(color.R, color.G, color.B, 0f);
GL.Vertex3(rx1, ry2, rect.Z + CGraphics.ZOffset);
GL.Color4(color.R, color.G, color.B, color.A * CGraphics.GlobalAlpha);
GL.Vertex3(rx1, ry1, rect.Z + CGraphics.ZOffset);
GL.End();
GL.PopMatrix();
GL.Disable(EnableCap.Blend);
}
#endregion Basic Draw Methods
#region Textures
protected override COGLTexture _CreateTexture(Size dataSize)
{
if (dataSize.Width < 0)
return new COGLTexture(0, dataSize);
COGLTexture texture = new COGLTexture(GL.GenTexture(), dataSize, _CheckForNextPowerOf2(dataSize.Width), _CheckForNextPowerOf2(dataSize.Height));
return texture;
}
protected override void _WriteDataToTexture(COGLTexture texture, byte[] data)
{
GL.BindTexture(TextureTarget.Texture2D, texture.Name);
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, texture.DataSize.Width, texture.DataSize.Height, PixelFormat.Bgra, PixelType.UnsignedByte, data);
GL.Ext.GenerateMipmap(GenerateMipmapTarget.Texture2D);
GL.BindTexture(TextureTarget.Texture2D, 0);
}
protected override void _WriteDataToTexture(COGLTexture texture, IntPtr data)
{
GL.BindTexture(TextureTarget.Texture2D, texture.Name);
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, texture.DataSize.Width, texture.DataSize.Height, PixelFormat.Bgra, PixelType.UnsignedByte, data);
GL.Ext.GenerateMipmap(GenerateMipmapTarget.Texture2D);
GL.BindTexture(TextureTarget.Texture2D, 0);
}
#region drawing
public void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, bool mirrored = false)
{
COGLTexture texture;
if (!_GetTexture(textureRef, out texture))
return;
if (Math.Abs(rect.W) < 1 || Math.Abs(rect.H) < 1 || Math.Abs(color.A) < 0.01)
return;
GL.BindTexture(TextureTarget.Texture2D, texture.Name);
const float x1 = 0;
float x2 = texture.WidthRatio;
float y1 = 0;
float y2 = texture.HeightRatio;
float rx1 = rect.X;
float rx2 = rect.X + rect.W;
float ry1 = rect.Y;
float ry2 = rect.Y + rect.H;
GL.Enable(EnableCap.Blend);
GL.Color4(color.R, color.G, color.B, color.A * CGraphics.GlobalAlpha);
GL.MatrixMode(MatrixMode.Texture);
GL.PushMatrix();
if (Math.Abs(rect.Rotation) > float.Epsilon)
{
GL.Translate(0.5f, 0.5f, 0);
GL.Rotate(-rect.Rotation, 0f, 0f, 1f);
GL.Translate(-0.5f, -0.5f, 0);
}
if (mirrored)
{
float tmp = y2;
y2 = y1;
y1 = tmp;
}
GL.Begin(BeginMode.Quads);
GL.TexCoord2(x1, y1);
GL.Vertex3(rx1, ry1, rect.Z + CGraphics.ZOffset);
GL.TexCoord2(x1, y2);
GL.Vertex3(rx1, ry2, rect.Z + CGraphics.ZOffset);
GL.TexCoord2(x2, y2);
GL.Vertex3(rx2, ry2, rect.Z + CGraphics.ZOffset);
GL.TexCoord2(x2, y1);
GL.Vertex3(rx2, ry1, rect.Z + CGraphics.ZOffset);
GL.End();
GL.PopMatrix();
GL.Disable(EnableCap.Blend);
GL.BindTexture(TextureTarget.Texture2D, 0);
}
public void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false)
{
COGLTexture texture;
if (!_GetTexture(textureRef, out texture))
return;
if (Math.Abs(rect.W) < 1 || Math.Abs(rect.H) < 1 || Math.Abs(bounds.H) < 1 || Math.Abs(bounds.W) < 1 ||
Math.Abs(color.A) < 0.01)
return;
if (bounds.X > rect.X + rect.W || bounds.X + bounds.W < rect.X)
return;
if (bounds.Y > rect.Y + rect.H || bounds.Y + bounds.H < rect.Y)
return;
GL.BindTexture(TextureTarget.Texture2D, texture.Name);
float x1 = (bounds.X - rect.X) / rect.W * texture.WidthRatio;
float x2 = (bounds.X + bounds.W - rect.X) / rect.W * texture.WidthRatio;
float y1 = (bounds.Y - rect.Y) / rect.H * texture.HeightRatio;
float y2 = (bounds.Y + bounds.H - rect.Y) / rect.H * texture.HeightRatio;
if (x1 < 0)
x1 = 0f;
if (x2 > texture.WidthRatio)
x2 = texture.WidthRatio;
if (y1 < 0)
y1 = 0f;
if (y2 > texture.HeightRatio)
y2 = texture.HeightRatio;
float rx1 = rect.X;
float rx2 = rect.X + rect.W;
float ry1 = rect.Y;
float ry2 = rect.Y + rect.H;
if (rx1 < bounds.X)
rx1 = bounds.X;
if (rx2 > bounds.X + bounds.W)
rx2 = bounds.X + bounds.W;
if (ry1 < bounds.Y)
ry1 = bounds.Y;
if (ry2 > bounds.Y + bounds.H)
ry2 = bounds.Y + bounds.H;
GL.Enable(EnableCap.Blend);
GL.Color4(color.R, color.G, color.B, color.A * CGraphics.GlobalAlpha);
GL.MatrixMode(MatrixMode.Texture);
GL.PushMatrix();
if (Math.Abs(rect.Rotation) > float.Epsilon)
{
GL.Translate(0.5f, 0.5f, 0);
GL.Rotate(-rect.Rotation, 0f, 0f, 1f);
GL.Translate(-0.5f, -0.5f, 0);
}
if (mirrored)
{
float tmp = y2;
y2 = y1;
y1 = tmp;
}
GL.Begin(BeginMode.Quads);
GL.TexCoord2(x1, y1);
GL.Vertex3(rx1, ry1, rect.Z + CGraphics.ZOffset);
GL.TexCoord2(x1, y2);
GL.Vertex3(rx1, ry2, rect.Z + CGraphics.ZOffset);
GL.TexCoord2(x2, y2);
GL.Vertex3(rx2, ry2, rect.Z + CGraphics.ZOffset);
GL.TexCoord2(x2, y1);
GL.Vertex3(rx2, ry1, rect.Z + CGraphics.ZOffset);
GL.End();
GL.PopMatrix();
GL.Disable(EnableCap.Blend);
GL.BindTexture(TextureTarget.Texture2D, 0);
}
public void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, float begin, float end)
{
COGLTexture texture;
if (!_GetTexture(textureRef, out texture))
return;
GL.BindTexture(TextureTarget.Texture2D, texture.Name);
GL.Enable(EnableCap.Blend);
GL.Color4(color.R, color.G, color.B, color.A * CGraphics.GlobalAlpha);
GL.Begin(BeginMode.Quads);
GL.TexCoord2(0f + begin * texture.WidthRatio, 0f);
GL.Vertex3(rect.X + begin * rect.W, rect.Y, rect.Z + CGraphics.ZOffset);
GL.TexCoord2(0f + begin * texture.WidthRatio, texture.HeightRatio);
GL.Vertex3(rect.X + begin * rect.W, rect.Y + rect.H, rect.Z + CGraphics.ZOffset);
GL.TexCoord2(texture.WidthRatio * end, texture.HeightRatio);
GL.Vertex3(rect.X + end * rect.W, rect.Y + rect.H, rect.Z + CGraphics.ZOffset);
GL.TexCoord2(texture.WidthRatio * end, 0f);
GL.Vertex3(rect.X + end * rect.W, rect.Y, rect.Z + CGraphics.ZOffset);
GL.End();
GL.Disable(EnableCap.Blend);
GL.BindTexture(TextureTarget.Texture2D, 0);
}
public void DrawTextureReflection(CTextureRef textureRef, SRectF rect, SColorF color, SRectF bounds, float space, float height)
{
COGLTexture texture;
if (!_GetTexture(textureRef, out texture))
return;
if (Math.Abs(rect.W) < float.Epsilon || Math.Abs(rect.H) < float.Epsilon || Math.Abs(bounds.H) < float.Epsilon || Math.Abs(bounds.W) < float.Epsilon ||
Math.Abs(color.A) < float.Epsilon || height <= float.Epsilon)
return;
if (bounds.X > rect.X + rect.W || bounds.X + bounds.W < rect.X)
return;
if (bounds.Y > rect.Y + rect.H || bounds.Y + bounds.H < rect.Y)
return;
if (height > bounds.H)
height = bounds.H;
GL.BindTexture(TextureTarget.Texture2D, texture.Name);
float x1 = (bounds.X - rect.X) / rect.W * texture.WidthRatio;
float x2 = (bounds.X + bounds.W - rect.X) / rect.W * texture.WidthRatio;
float y1 = (bounds.Y - rect.Y + rect.H - height) / rect.H * texture.HeightRatio;
float y2 = (bounds.Y + bounds.H - rect.Y) / rect.H * texture.HeightRatio;
if (x1 < 0)
x1 = 0f;
if (x2 > texture.WidthRatio)
x2 = texture.WidthRatio;
if (y1 < 0)
y1 = 0f;
if (y2 > texture.HeightRatio)
y2 = texture.HeightRatio;
float rx1 = rect.X;
float rx2 = rect.X + rect.W;
float ry1 = rect.Y + rect.H + space;
float ry2 = rect.Y + rect.H + space + height;
if (rx1 < bounds.X)
rx1 = bounds.X;
if (rx2 > bounds.X + bounds.W)
rx2 = bounds.X + bounds.W;
if (ry1 < bounds.Y + space)
ry1 = bounds.Y + space;
if (ry2 > bounds.Y + bounds.H + space + height)
ry2 = bounds.Y + bounds.H + space + height;
GL.Enable(EnableCap.Blend);
GL.MatrixMode(MatrixMode.Texture);
GL.PushMatrix();
if (Math.Abs(rect.Rotation) > float.Epsilon)
{
GL.Translate(0.5f, 0.5f, 0);
GL.Rotate(-rect.Rotation, 0f, 0f, 1f);
GL.Translate(-0.5f, -0.5f, 0);
}
GL.Begin(BeginMode.Quads);
GL.Color4(color.R, color.G, color.B, color.A * CGraphics.GlobalAlpha);
GL.TexCoord2(x2, y2);
GL.Vertex3(rx2, ry1, rect.Z + CGraphics.ZOffset);
GL.Color4(color.R, color.G, color.B, 0f);
GL.TexCoord2(x2, y1);
GL.Vertex3(rx2, ry2, rect.Z + CGraphics.ZOffset);
GL.Color4(color.R, color.G, color.B, 0f);
GL.TexCoord2(x1, y1);
GL.Vertex3(rx1, ry2, rect.Z + CGraphics.ZOffset);
GL.Color4(color.R, color.G, color.B, color.A * CGraphics.GlobalAlpha);
GL.TexCoord2(x1, y2);
GL.Vertex3(rx1, ry1, rect.Z + CGraphics.ZOffset);
GL.End();
GL.PopMatrix();
GL.Disable(EnableCap.Blend);
GL.BindTexture(TextureTarget.Texture2D, 0);
}
#endregion drawing
#endregion Textures
#endregion implementation
}
}