aboutsummaryrefslogtreecommitdiffstatsci
path: root/src/OpenGL/GLBackend/Context.cs
diff options
context:
space:
mode:
authorjpbruyere <jp.bruyere@hotmail.com>2015-10-21 22:00:20 +0200
committerjpbruyere <jp.bruyere@hotmail.com>2015-10-21 22:00:20 +0200
commit8099f1775b6d9083e04b2f39700c2e90a0724aca (patch)
treea145c7f1ec8aeb4aade2ffae4e2c50771ef9ec86 /src/OpenGL/GLBackend/Context.cs
parentf31ffd683210c50b777dce4e76d0d9912ca925a8 (diff)
glbackend tests, copy first try from hg reposglbackend
Diffstat (limited to 'src/OpenGL/GLBackend/Context.cs')
-rw-r--r--src/OpenGL/GLBackend/Context.cs887
1 files changed, 887 insertions, 0 deletions
diff --git a/src/OpenGL/GLBackend/Context.cs b/src/OpenGL/GLBackend/Context.cs
new file mode 100644
index 00000000..51d5d86b
--- /dev/null
+++ b/src/OpenGL/GLBackend/Context.cs
@@ -0,0 +1,887 @@
+#define MPERF
+//#define GPU_TIME_MSR
+
+using System;
+using OpenTK.Graphics.OpenGL;
+using OpenTK;
+using System.Collections.Generic;
+using System.Linq;
+using System.Diagnostics;
+using System.Drawing.Imaging;
+
+
+
+namespace go.GLBackend
+{
+ public class Context : IDisposable
+ {
+ #region CTOR
+ public Context (Surface _surf)
+ {
+ surf = _surf;
+
+ saveGLConfig ();
+
+ createStencilTexture ();
+
+ fboId = createFbo (surf.texId);
+
+ projectionMatrix = Matrix4.CreateOrthographicOffCenter
+ (0, surf.width, surf.height, 0, 0, 1);
+ modelviewMatrix = Matrix4.Identity;
+
+ if (shader == null)
+ shader = new Shader ();
+ if (texturedShader == null)
+ texturedShader = new TexturedShader ();
+ if (clipShader == null)
+ clipShader = new ClipShader ();
+ if (fontShader == null)
+ fontShader = new FontShader ();
+
+
+ GL.Viewport (0, 0, surf.width, surf.height);
+ GL.PrimitiveRestartIndex (int.MaxValue);
+ GL.Enable (EnableCap.PrimitiveRestart);
+ GL.Enable (EnableCap.Blend);
+ GL.BlendFunc (BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
+
+ //GL.BindFramebuffer(FramebufferTarget.Framebuffer, fboId);
+
+ // GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ // GL.Clear(ClearBufferMask.ColorBufferBit);
+ }
+ #endregion
+
+ #if (DEBUG && MPERF)
+ Stopwatch sw_fill = new Stopwatch ();
+ Stopwatch sw_stroke = new Stopwatch ();
+ #endif
+
+ #if GPU_TIME_MSR
+ ulong gpuTime = 0;
+ ulong gpuTimeCpt = 0;
+ #endif
+
+ public float coef1 = 1.0f;
+ public float coef2 = 0.5f;
+ public float coef3 = 0.0f;
+ public float coefA = 1.0f;
+
+ static Shader shader;
+ static Shader clipShader;
+ static Shader texturedShader;
+ static FontShader fontShader;
+
+ static List<glFont> fontsCache = new List<glFont>();
+
+ #region Private Fields
+ Surface surf;
+ Surface sourceSurface;//should be a pattern object from which surf would be derrived
+
+ double lineWidth = 1.0;
+ Color color = Color.White;
+ LineCap lineCap = LineCap.Butt;
+ LineJoin lineJoint = LineJoin.Miter;
+
+ FontFace fontFace;
+ uint fontSize = 10;
+ //path building temp fields
+ Vector2 curPos = new Vector2(0f,0f);
+ List<Path> pathes = new List<Path> ();
+ Path cpath;//current path
+
+ int fboId;
+ int stencilTexId;
+ VertexArrayObject vao;
+
+ Matrix4 projectionMatrix,
+ modelviewMatrix;
+
+ bool stencilTest = false;
+
+ VertexArrayObject paint_vao; //quad used to paint source on context
+
+ //path limits used in fill
+ float minX,maxX,minY,maxY,
+ polyWidth, polyHeight;
+
+ #endregion
+
+ #region Public properties
+ public double LineWidth {
+ get { return lineWidth; }
+ set { lineWidth = value; }
+ }
+ public Color Color {
+ get { return color; }
+ set { color = value; }
+ }
+ public LineCap LineCap {
+ get { return lineCap;}
+ set {lineCap = value;}
+ }
+ public LineJoin LineJoint {
+ get {return lineJoint;}
+ set {lineJoint = value;}
+ }
+ #endregion
+
+ #region Private functions
+ int createFbo(int texID)
+ {
+ //GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
+ int fbo;
+ GL.GenFramebuffers(1,out fbo);
+ GL.BindFramebuffer(FramebufferTarget.Framebuffer, fbo);
+ GL.FramebufferTexture2D(FramebufferTarget.Framebuffer,
+ FramebufferAttachment.ColorAttachment0,
+ TextureTarget.Texture2D,texID,0);
+
+ //GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
+ FramebufferErrorCode fbErr = GL.CheckFramebufferStatus (FramebufferTarget.Framebuffer);
+
+ switch (fbErr) {
+ case FramebufferErrorCode.FramebufferUndefined:
+ break;
+ case FramebufferErrorCode.FramebufferComplete:
+ //Debug.WriteLine ("FBO complete");
+ break;
+ case FramebufferErrorCode.FramebufferIncompleteAttachment:
+ Debug.WriteLine ("FBO incomplete attachment");
+ break;
+ case FramebufferErrorCode.FramebufferIncompleteMissingAttachment:
+ Debug.WriteLine ("FBO missing attachment");
+ break;
+ default:
+ Debug.WriteLine ("FBO error");
+ break;
+ }
+ return fbo;
+ }
+ public int copyCurentSurfaceToTexture()
+ {
+ //create a new fbo to blit to with an empty texture
+ Surface tmp = surf.CreateSimilar ();
+ int dstFbo = createFbo (tmp.texId);//we are binded to new fbo (read&write)
+ //bind read to Context fbo;
+ GL.BindFramebuffer (FramebufferTarget.ReadFramebuffer, fboId);
+ GL.BindFramebuffer (FramebufferTarget.DrawFramebuffer, dstFbo);
+
+ GL.BlitFramebuffer (0, 0, surf.width, surf.height, 0, 0, surf.width, surf.height,
+ ClearBufferMask.ColorBufferBit,BlitFramebufferFilter.Nearest);
+
+ //bind write fbo to Context
+ GL.BindFramebuffer (FramebufferTarget.Framebuffer, fboId);
+ //delete temp fbo
+ GL.DeleteFramebuffer (dstFbo);
+ return tmp.texId;
+ }
+ void createStencilTexture(){
+ stencilTexId = GL.GenTexture();
+ GL.BindTexture(TextureTarget.Texture2D, stencilTexId);
+ GL.TexImage2D(TextureTarget.Texture2D,0,
+ PixelInternalFormat.R8, surf.width, surf.height,0,
+ OpenTK.Graphics.OpenGL.PixelFormat.Red, PixelType.UnsignedByte,IntPtr.Zero);
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+ GL.BindTexture(TextureTarget.Texture2D, 0);
+ }
+ void clear_path()
+ {
+ pathes.Clear ();
+ curPos = new Vector2 (0f, 0f);
+ cpath = null;
+ }
+ void addPath(Path _path){
+ if (_path == null)
+ return;
+ if (_path.Count > 1)
+ pathes.Add (_path);
+ }
+ //TODO: 360° will fail
+ double NormalizeAngle(double a)
+ {
+ double res = a % (2*Math.PI);
+ return res < 0 ? res + 2*Math.PI : res;
+ }
+ #endregion
+
+ #region Public functions
+ #region Main drawing functions
+ public void Stroke()
+ {
+ stroke_internal ();
+ clear_path ();
+ }
+ public void StrokePreserve()
+ {
+ stroke_internal ();
+ }
+ public void Fill()
+ {
+ fill_internal ();
+ clear_path ();
+ }
+ public void FillPreserve()
+ {
+ fill_internal ();
+ }
+ public void Paint()
+ {
+ if (paint_vao == null)
+ return;
+ texturedShader.Enable ();
+ //shader.LineWidth = lineWidth;
+ texturedShader.ProjectionMatrix = projectionMatrix;
+ texturedShader.ModelViewMatrix = modelviewMatrix;
+ texturedShader.Color = new Vector4(1f,1f,1f,1f);
+
+ GL.ActiveTexture (TextureUnit.Texture0);
+ GL.BindTexture (TextureTarget.Texture2D, sourceSurface.texId);
+
+ paint_vao.Render (PrimitiveType.TriangleStrip);
+
+ GL.BindTexture (TextureTarget.Texture2D, 0);
+
+ texturedShader.Disable ();
+ }
+ public void SetSourceSurface (Surface _source, int x, int y)
+ {
+ sourceSurface = _source;
+
+ if (paint_vao != null)
+ paint_vao.Dispose ();
+
+
+ paint_vao = new QuadVAO (x,y,sourceSurface.width,sourceSurface.height);
+ }
+ #region Clipping
+ public void Clip()
+ {
+ clip_internal ();
+ clear_path ();
+ }
+ public void ClipPreserve()
+ {
+ clip_internal ();
+ clear_path ();
+ }
+ public void ResetClip()
+ {
+ stencilTest = false;
+
+ GL.FramebufferTexture2D(FramebufferTarget.Framebuffer,
+ FramebufferAttachment.ColorAttachment0,
+ TextureTarget.Texture2D,stencilTexId,0);
+
+
+ GL.Clear (ClearBufferMask.ColorBufferBit);
+
+ GL.FramebufferTexture2D(FramebufferTarget.Framebuffer,
+ FramebufferAttachment.ColorAttachment0,
+ TextureTarget.Texture2D,surf.texId,0);
+ }
+ #endregion
+
+ #region Fonts & text handling
+ public void SelectFontFace(string _fontName, FontSlant _slant, FontWeight _weight){
+ Font tmp = _fontName;
+ switch (_slant) {
+ case FontSlant.Italic:
+ tmp.Style |= FontStyle.Italic;
+ break;
+ case FontSlant.Oblique:
+ tmp.Style |= FontStyle.Oblique;
+ break;
+ }
+ if (_weight == FontWeight.Bold)
+ tmp.Style |= FontStyle.Bold;
+
+ fontFace = FontFace.SearchFont (tmp);
+ }
+ public void SetFontSize(uint _fontSize){
+ fontSize = _fontSize;
+ if (!fontFace.glyphesCache.ContainsKey(fontSize))
+ fontFace.buildGlyphesTextures (fontSize, 0, 0xFF);
+ }
+ public FontExtents FontExtents {
+ get {
+ return fontFace.originalFontExtents;
+ }
+ }
+ public TextExtents TextExtents(string s)
+ {
+ TextExtents te = new go.GLBackend.TextExtents ();
+
+ int penX = 0,
+ penY = 0;
+
+ for (int i = 0; i < s.Length; i++) {
+ uint c = (uint)s [i];
+ glGlyph g = fontFace.glyphesCache [fontSize] [c];
+ penX += (int)g.advanceX;
+ penY += (int)g.advanceY;
+ }
+
+ te.XAdvance = penX;
+ te.YAdvance = penY;
+ return te;
+ }
+ public void FillText(string text, Point _pt)
+ {
+ buildTextVAO (text,_pt);
+
+ int backTex = copyCurentSurfaceToTexture ();
+
+ fontShader.Enable ();
+ //shader.LineWidth = lineWidth;
+ fontShader.ProjectionMatrix = projectionMatrix;
+ fontShader.ModelViewMatrix = modelviewMatrix;
+ fontShader.Color = new Vector4(
+ (float)color.R,
+ (float)color.G,
+ (float)color.B,
+ (float)color.A);
+ fontShader.Resolution = new Vector2 (surf.width, surf.height);
+ fontShader.coef1 = coef1;
+ fontShader.coef2 = coef2;
+ fontShader.coef3 = coef3;
+ fontShader.coefA = coefA;
+// shader.StencilTest = stencilTest;
+//
+// if (stencilTest) {
+// shader.Resolution = new Vector2 (surf.width, surf.height);
+// GL.ActiveTexture (TextureUnit.Texture0);
+// GL.BindTexture (TextureTarget.Texture2D, stencilTexId);
+// }
+
+ GL.ActiveTexture (TextureUnit.Texture0);
+ GL.BindTexture (TextureTarget.Texture2D, curFontTex);
+ GL.ActiveTexture (TextureUnit.Texture1);
+ GL.BindTexture (TextureTarget.Texture2D, backTex);
+
+ vao.Render (PrimitiveType.TriangleStrip);
+ vao.Dispose ();
+
+ // #if DEBUG
+ // ErrorCode err = GL.GetError ();
+ // if (err != ErrorCode.NoError)
+ // Debug.WriteLine ("Stroke_internal error: " + err.ToString ());
+ // #endif
+
+ fontShader.Disable ();
+
+ GL.BindTexture (TextureTarget.Texture2D, 0);
+ GL.ActiveTexture (TextureUnit.Texture0);
+ GL.BindTexture (TextureTarget.Texture2D, 0);
+
+ //destroy background texture
+ GL.DeleteTexture (backTex);
+ //GL.Enable (EnableCap.Blend);
+ }
+ #endregion
+
+ #endregion
+
+
+
+ #region Path handling functions
+ public void MoveTo(double x, double y)
+ {
+ addPath (cpath);
+ cpath = new Path ();
+ curPos = new Vector2 ((float)x, (float)y);
+ }
+ public void LineTo(double x, double y)
+ {
+ if (cpath==null)
+ cpath = new Path ();
+ if (cpath.Count == 0)
+ cpath.Add (curPos);
+ curPos = new Vector2 ((float)x, (float)y);
+ cpath.Add (curPos);
+ }
+ public void Rectangle(Rectangle r)
+ {
+ MoveTo (r.X, r.Y);
+ LineTo (r.Right, r.Y);
+ LineTo (r.Right, r.Bottom);
+ LineTo (r.X, r.Bottom);
+ ClosePath ();
+ }
+ public void Arc(double xc, double yc, double radius, double angle1, double angle2)
+ {
+ angle1 = NormalizeAngle (angle1);
+ angle2 = NormalizeAngle (angle2);
+
+ if (angle1 > angle2)
+ angle2 += Math.PI * 2.0;
+ double a = angle1,
+ //step = Math.PI/16;
+ step =Math.PI * 2/(radius+20);
+
+ Vector2 v = new Vector2 ((float)(Math.Cos (a) * radius + xc), (float)(Math.Sin (a) * radius + yc));
+ if (v!=curPos)
+ cpath.Add (v);
+ int cpt = 0;
+ while(true){
+ if (a<angle2){
+ a+=step;
+ if (a > angle2)
+ break;
+ //a = angle2;
+ }else
+ break;
+ cpt++;
+// if (a == angle2) {//add a point very close to a to get good termination angle
+// v = new Vector2 ((float)(Math.Cos (a-0.001) * radius + xc), (float)(Math.Sin (a-0.001) * radius + yc));
+// cpath.Add (v);
+// }
+
+ v = new Vector2 ((float)(Math.Cos (a) * radius + xc), (float)(Math.Sin (a) * radius + yc));
+ cpath.Add (v);
+
+ }
+ curPos = cpath [cpath.Count - 1];
+ }
+ public void ClosePath()
+ {
+ if (cpath == null)
+ return;
+ if (cpath.Count < 3)//cannot close path with less than 3 points
+ return;
+
+ cpath.IsClosed = true;
+
+ MoveTo (cpath [0].X, cpath [0].Y);
+ }
+ #endregion
+
+ #region Transformations
+ public void Translate(double tx, double ty)
+ {
+ modelviewMatrix *= Matrix4.CreateTranslation ((float)tx, (float)ty, 0f);
+ }
+ public void Scale(double sx, double sy)
+ {
+ modelviewMatrix *= Matrix4.CreateScale ((float)sx, (float)sy, 0f);
+ }
+ public void Rotate(double angle){
+ modelviewMatrix *= Matrix4.CreateRotationZ ((float)angle);
+ }
+ #endregion
+ #endregion
+
+ //temp Lists used to build arrays for vao
+ List<Vector2> vertices = new List<Vector2> ();
+ List<int> indices = new List<int> ();
+ List<Vector2> texCoords = new List<Vector2> ();
+ int idxOffset = 0; //offset in vertices array between pathes
+
+
+ void initVAOsCache()
+ {
+ if (vertices != null)
+ return;
+ vertices = new List<Vector2> ();
+ indices = new List<int> ();
+ texCoords = new List<Vector2> ();
+ idxOffset = 0;
+ }
+ void flush(){
+ vao = new VertexArrayObject (
+ vertices.ToArray (),
+ texCoords.ToArray (),
+ indices.ToArray ());
+
+ vertices = null;
+ indices = null;
+ texCoords = null;
+ }
+
+ void stroke_internal()
+ {
+ buildStrokeVAOs ();
+
+ #if GPU_TIME_MSR
+ ulong gpuTimeStart, gpuTimeStop;
+ gpuTimeStart = (ulong)GL.GetInteger64 (GetPName.Timestamp);
+ #endif
+
+ shader.Enable ();
+ //shader.LineWidth = lineWidth;
+ shader.ProjectionMatrix = projectionMatrix;
+ shader.ModelViewMatrix = modelviewMatrix;
+ shader.Color = new Vector4(
+ (float)color.R,
+ (float)color.G,
+ (float)color.B,
+ (float)color.A);
+ shader.StencilTest = stencilTest;
+
+ if (stencilTest) {
+ shader.Resolution = new Vector2 (surf.width, surf.height);
+ GL.ActiveTexture (TextureUnit.Texture0);
+ GL.BindTexture (TextureTarget.Texture2D, stencilTexId);
+ }
+
+ vao.Render (PrimitiveType.TriangleStrip);
+ vao.Dispose ();
+
+// #if DEBUG
+// ErrorCode err = GL.GetError ();
+// if (err != ErrorCode.NoError)
+// Debug.WriteLine ("Stroke_internal error: " + err.ToString ());
+// #endif
+
+ shader.Disable ();
+
+ GL.BindTexture (TextureTarget.Texture2D, 0);
+
+ #if GPU_TIME_MSR
+ gpuTimeStop = (ulong)GL.GetInteger64 (GetPName.Timestamp);
+ Debug.WriteLine(gpuTimeStop - gpuTimeStart);
+ #endif
+ }
+ void fill_internal()
+ {
+ buildFillVAOs ();
+
+ shader.Enable ();
+ //shader.LineWidth = lineWidth;
+ shader.ProjectionMatrix = projectionMatrix;
+ shader.ModelViewMatrix = modelviewMatrix;
+ shader.Color = new Vector4(
+ (float)color.R,
+ (float)color.G,
+ (float)color.B,
+ (float)color.A);
+ shader.StencilTest = stencilTest;
+
+ if (stencilTest) {
+ shader.Resolution = new Vector2 (surf.width, surf.height);
+ GL.ActiveTexture (TextureUnit.Texture0);
+ GL.BindTexture (TextureTarget.Texture2D, stencilTexId);
+ }
+
+ vao.Render (PrimitiveType.Triangles);
+ vao.Dispose ();
+
+ shader.Disable ();
+
+ GL.BindTexture (TextureTarget.Texture2D, 0);
+ }
+ void clip_internal()
+ {
+ GL.FramebufferTexture2D(FramebufferTarget.Framebuffer,
+ FramebufferAttachment.ColorAttachment0,
+ TextureTarget.Texture2D,stencilTexId,0);
+
+ if (!stencilTest)
+ GL.Clear (ClearBufferMask.ColorBufferBit);
+ stencilTest = true;
+
+ buildFillVAOs ();
+
+ clipShader.Enable ();
+ //shader.LineWidth = lineWidth;
+ clipShader.ProjectionMatrix = projectionMatrix;
+ clipShader.ModelViewMatrix = modelviewMatrix;
+
+ vao.Render (PrimitiveType.Triangles);
+ vao.Dispose ();
+
+ clipShader.Disable ();
+
+ GL.FramebufferTexture2D(FramebufferTarget.Framebuffer,
+ FramebufferAttachment.ColorAttachment0,
+ TextureTarget.Texture2D,surf.texId,0);
+ }
+
+ void buildFillVAOs()
+ {
+ #if (DEBUG && MPERF)
+ sw_fill.Start();
+ #endif
+
+ addPath (cpath);
+
+ initVAOsCache ();
+
+ float hlw = (float)lineWidth / 2.0f;//half line width
+
+ Vector2 vDir,vDir2;
+ bool genTexCoords = false;
+
+ for (int j = 0; j < pathes.Count; j++) {
+ Path path = new Path(pathes[j]);
+ //keep an array containing real vertex indice
+ List<int> vertIdx = new List<int> (Enumerable.Range(0,path.Count));
+
+ #region get polygon bounds for texture mapping
+ if (genTexCoords){
+ minX = float.MaxValue;
+ maxX = float.MinValue;
+ minY = float.MaxValue;
+ maxY = float.MinValue;
+
+ foreach (Vector2 p in path) {
+ if (p.X < minX)
+ minX = p.X;
+ if (p.Y < minY)
+ minY = p.Y;
+ if (p.X > maxX)
+ maxX = p.X;
+ if (p.Y > maxY)
+ maxY = p.Y;
+ }
+ polyWidth = maxX - minX;
+ polyHeight = maxY - minY;
+ }
+ #endregion
+
+ vertices.AddRange (path);//we only play on indices
+ int ptrS = 0, ptrS1, ptrS2; //index du sommet dans le path
+ while (path.Count > 3) {
+ if (ptrS == path.Count)
+ ptrS = 0;
+
+ if (ptrS < path.Count - 2) {
+ ptrS1 = ptrS + 1;
+ ptrS2 = ptrS + 2;
+ } else if (ptrS < path.Count - 1) {
+ ptrS1 = ptrS + 1;
+ ptrS2 = 0;
+ } else {
+ ptrS1 = 0;
+ ptrS2 = 1;
+ }
+
+ //should test only concavity points if any
+ vDir = Vector2.NormalizeFast (path [ptrS1] - path [ptrS]);
+ vDir2 = Vector2.NormalizeFast (path [ptrS2] - path [ptrS]);
+
+ float dotP = Vector2.Dot (vDir.PerpendicularLeft, vDir2);
+ //double angle = Math.Acos((double)dotP);
+//
+// Debug.WriteLine (dotP);
+// Debug.WriteLine (angle);
+
+ if (dotP <= 0) {
+ //triangle is invalid
+ ptrS++;
+ continue;
+ }
+
+ //check if no other points of path is inside tri
+// bool triangleIsValid = true;
+// int i = ptrS + 3;
+// if (i >= path.Count)
+// i -= path.Count;
+// while (i != ptrS) {
+// if (PointInTriangle (path [i],path [ptrS] , path [ptrS1], path [ptrS2])) {
+// ptrS++;
+// triangleIsValid = false;
+// break;
+// }
+// i++;
+// if (i == path.Count)
+// i = 0;
+// }
+// if (!triangleIsValid)
+// continue;
+ //triangle is valid, add tri to primitive to draw
+ //and remove middle sommet from path[];
+ addFillTriangle (j, vertIdx [ptrS], vertIdx [ptrS1], vertIdx [ptrS2]);
+
+ path.RemoveAt (ptrS1);
+ vertIdx.RemoveAt (ptrS1);
+ }
+ if (path.Count == 3)
+ addFillTriangle (j, vertIdx [0], vertIdx [1], vertIdx [2]);
+
+ if (j < pathes.Count - 1) {
+ idxOffset += pathes[j].Count;
+ }
+ }
+ flush ();
+
+ #if (DEBUG && MPERF)
+ sw_fill.Stop();
+ #endif
+ }
+
+ int curFontTex;
+ void buildTextVAO(string text, Point pt)
+ {
+ initVAOsCache ();
+
+ int penX = pt.X,
+ penY = pt.Y + (int)fontFace.originalFontExtents.Ascent;
+
+ for (int i = 0; i < text.Length; i++) {
+ uint c = (uint)text [i];
+ glGlyph g = fontFace.glyphesCache [fontSize] [c];
+ curFontTex = g.texId;
+ vertices.Add (new Vector2(penX + g.bmpLeft, penY - g.bmpTop));
+ vertices.Add (new Vector2(penX + g.bmpLeft, penY - g.bmpTop + g.dims.Height));
+ vertices.Add (new Vector2(penX + g.bmpLeft + g.dims.Width, penY - g.bmpTop));
+ vertices.Add (new Vector2(penX + g.bmpLeft + g.dims.Width, penY - g.bmpTop + g.dims.Height));
+ indices.Add (int.MaxValue);//primitive restart
+ indices.Add (i*4);
+ indices.Add (i*4 + 1);
+ indices.Add (i*4 + 2);
+ indices.Add (i*4 + 3);
+ texCoords.Add (new Vector2(g.texX,g.texY));
+ texCoords.Add (new Vector2(g.texX,g.texY + g.texHeight));
+ texCoords.Add (new Vector2(g.texX + g.texWidth,g.texY));
+ texCoords.Add (new Vector2(g.texX + g.texWidth,g.texY + g.texHeight));
+
+ penX += (int)g.advanceX;
+ penY += (int)g.advanceY;
+ }
+
+ flush ();
+ }
+ void buildStrokeVAOs()
+ {
+ addPath (cpath);
+
+ initVAOsCache ();
+
+ float hlw = (float)lineWidth / 2.0f;//half line width
+
+ Vector2 vDir,vDir2, vPerp;
+ float lPerp;
+ int i;
+ //current offset in index
+ for (int j = 0; j < pathes.Count; j++) {
+ Path path = pathes[j];
+ for (i = 0; i < path.Count; i++) {
+ lPerp = hlw;
+ vDir2 = Vector2.Zero;
+
+ if (i < path.Count - 1) {
+ vDir = Vector2.Normalize (path [i + 1] - path [i]);
+ if (i > 0) {
+ vDir2 = Vector2.Normalize (path [i] - path [i - 1]);
+ } else if (path.IsClosed)
+ vDir2 = Vector2.Normalize (path [0] - path [path.Count - 1]);
+ } else {
+ vDir = Vector2.Normalize (path [i] - path [i - 1]);
+ if (path.IsClosed)
+ vDir2 = Vector2.Normalize (path [0]-path [i]);
+ }
+
+ vPerp = Vector2.Normalize(vDir+vDir2).PerpendicularLeft;
+
+ if (vDir2 != Vector2.Zero) {
+ double dotP = Vector2.Dot (vDir.PerpendicularLeft, vPerp);
+ double angle = Math.Acos(dotP);
+ double x = Math.Tan (angle) * hlw;
+
+ if (dotP == 0)
+ lPerp = hlw;
+ else
+ lPerp = (float)Math.Sqrt (Math.Pow (x, 2) + Math.Pow (hlw, 2));
+ }
+
+ vertices.Add (path [i] + vPerp * (float)lPerp);
+ vertices.Add (path [i] - vPerp * (float)lPerp);
+ indices.Add (idxOffset + i*2);
+ indices.Add (idxOffset + i*2 + 1);
+ texCoords.Add (Vector2.Zero);
+ texCoords.Add (Vector2.One);
+ }
+ if (path.IsClosed) {
+ indices.Add (idxOffset);
+ indices.Add (idxOffset+1);
+ texCoords.Add (Vector2.Zero);
+ texCoords.Add (Vector2.One);
+ }
+ if (j < pathes.Count - 1) {
+ indices.Add (int.MaxValue); //restart primitive idex
+ idxOffset += path.Count * 2;
+ }
+ }
+ flush ();
+ }
+
+ void addFillTriangle(int pathIdx, int vx1, int vx2, int vx3){
+ addFillVertex (pathIdx, vx1);
+ addFillVertex (pathIdx, vx2);
+ addFillVertex (pathIdx, vx3);
+ }
+
+ void addFillVertex(int pathIdx, int vx){
+ Vector2 p = pathes [pathIdx] [vx];
+ indices.Add (idxOffset + vx);
+ //texCoords.Add (new Vector2(1.0f/polyWidth*(p.X-minX),1.0f/polyHeight*(p.Y-minY)));
+ }
+
+ float sign (Vector2 p1, Vector2 p2, Vector2 p3)
+ {
+ return (p1.X - p3.X) * (p2.Y - p3.Y) - (p2.X - p3.X) * (p1.Y - p3.Y);
+ }
+ bool PointInTriangle (Vector2 pt, Vector2 v1, Vector2 v2, Vector2 v3)
+ {
+ bool b1, b2, b3;
+
+ b1 = sign(pt, v1, v2) < 0.0f;
+ b2 = sign(pt, v2, v3) < 0.0f;
+ b3 = sign(pt, v3, v1) < 0.0f;
+
+ return ((b1 == b2) && (b2 == b3));
+ }
+
+ #region GL Ctx Save and restore
+ int savedFbo,
+ savedPriRstIdx,
+ savedBlendSrc,
+ savedBlendDst;
+ int[] viewport = new int[4];
+ float[] savedClsColor = new float[4];
+ bool savedBlend,savedPriRstCap;
+ void saveGLConfig(){
+ GL.GetInteger (GetPName.FramebufferBinding, out savedFbo);
+ GL.GetInteger ((GetPName)All.PrimitiveRestartIndex, out savedPriRstIdx);
+ GL.GetBoolean ((GetPName)All.PrimitiveRestart, out savedPriRstCap);
+ GL.GetInteger(GetPName.Viewport, viewport);
+ GL.GetFloat (GetPName.ColorClearValue, savedClsColor);
+ GL.GetBoolean (GetPName.Blend, out savedBlend);
+ GL.GetInteger (GetPName.BlendSrc, out savedBlendSrc);
+ GL.GetInteger (GetPName.BlendDst, out savedBlendDst);
+ }
+ void restoreGLConfig(){
+ GL.BindFramebuffer(FramebufferTarget.Framebuffer, savedFbo);
+
+ GL.Viewport (viewport[0],viewport[1],viewport[2],viewport[3]);
+ GL.PrimitiveRestartIndex (savedPriRstIdx);
+ GL.ClearColor (savedClsColor [0], savedClsColor [1], savedClsColor [2], savedClsColor [3]);
+ if (!savedBlend)
+ GL.Disable (EnableCap.Blend);
+ if (!savedPriRstCap)
+ GL.Disable (EnableCap.PrimitiveRestart);
+ GL.BlendFunc ((BlendingFactorSrc) savedBlendSrc,(BlendingFactorDest)savedBlendDst);
+ }
+ #endregion
+
+ #region IDisposable implementation
+
+ public void Dispose ()
+ {
+ restoreGLConfig ();
+
+ if (GL.IsFramebuffer (fboId))
+ GL.DeleteFramebuffer (fboId);
+ if (GL.IsTexture(stencilTexId))
+ GL.DeleteTexture(stencilTexId);
+ }
+
+ #endregion
+
+ }
+}
+