码迷,mamicode.com
首页 > 其他好文 > 详细

CSharpGL(26)在opengl中实现控件布局/渲染文字

时间:2016-06-05 15:27:11      阅读:283      评论:0      收藏:0      [点我收藏+]

标签:

CSharpGL(26)在opengl中实现控件布局/渲染文字

效果图

技术分享

如图所示,可以将文字、坐标轴固定在窗口的一角。

下载

CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL

UI控件布局关键点

ILayout

类似Winform控件那样,控件的位置、大小由其Anchor等属性决定。窗口大小改变时,控件的位置、大小会随之改变。

所以模仿Control类,直接使用Anchor作为UIRenderer的接口。

 1     /// <summary>
 2     /// 实现在OpenGL窗口中的UI布局
 3     /// </summary>
 4     public interface ILayout
 5     {
 6         /// <summary>
 7         /// parent node
 8         /// </summary>
 9         ILayout Container { get; set; }
10 
11         /// <summary>
12         /// children nodes
13         /// </summary>
14         ICollection<ILayout> Controls { get; }
15 
16         /// <summary>
17         /// the edges of the <see cref="GLCanvas"/> to which a UI’s rect is bound and determines how it is resized with its parent.
18         /// <para>something like AnchorStyles.Left | AnchorStyles.Bottom.</para>
19         /// </summary>
20         System.Windows.Forms.AnchorStyles Anchor { get; set; }
21 
22         /// <summary>
23         /// Gets or sets the space between viewport and SimpleRect.
24         /// </summary>
25         System.Windows.Forms.Padding Margin { get; set; }
26 
27         /// <summary>
28         /// 相对于<see cref="Container"/>左下角的位置(Left Down location)
29         /// </summary>
30         System.Drawing.Point Location { get; set; }
31 
32         /// <summary>
33         /// Stores width when <see cref="OpenGLUIRect.Anchor"/>.Left &amp; <see cref="OpenGLUIRect.Anchor"/>.Right is <see cref="OpenGLUIRect.Anchor"/>.None.
34         /// <para> and height when <see cref="OpenGLUIRect.Anchor"/>.Top &amp; <see cref="OpenGLUIRect.Anchor"/>.Bottom is <see cref="OpenGLUIRect.Anchor"/>.None.</para>
35         /// </summary>
36         System.Drawing.Size Size { get; set; }
37 
38         int zNear { get; set; }
39 
40         int zFar { get; set; }
41 
42     }

实现在OpenGL窗口中的UI布局

有了数据结构,就可以实现窗口中的UI布局了。当窗口大小改变时,调用下面的函数。

技术分享
 1         /// <summary>
 2         /// 实现在OpenGL窗口中的UI布局
 3         /// </summary>
 4         /// <param name="uiRenderer"></param>
 5         public static void Layout(this ILayout uiRenderer)
 6         {
 7             if (uiRenderer.Container != null)
 8             {
 9                 NonRootNodeLayout(uiRenderer, uiRenderer.Container);
10             }
11 
12             foreach (var item in uiRenderer.Controls)
13             {
14                 item.Layout();
15             }
16         }
17 
18         /// <summary>
19         /// leftRightAnchor = (AnchorStyles.Left | AnchorStyles.Right); 
20         /// </summary>
21         private const AnchorStyles leftRightAnchor = (AnchorStyles.Left | AnchorStyles.Right);
22 
23         /// <summary>
24         /// topBottomAnchor = (AnchorStyles.Top | AnchorStyles.Bottom);
25         /// </summary>
26         private const AnchorStyles topBottomAnchor = (AnchorStyles.Top | AnchorStyles.Bottom);
27 
28         private static void NonRootNodeLayout(ILayout currentNode, ILayout parent)
29         {
30             int x, y, width, height;
31             if ((currentNode.Anchor & leftRightAnchor) == leftRightAnchor)
32             {
33                 width = parent.Size.Width - currentNode.Margin.Left - currentNode.Margin.Right;
34                 if (width < 0) { width = 0; }
35             }
36             else
37             {
38                 width = currentNode.Size.Width;
39             }
40 
41             if ((currentNode.Anchor & topBottomAnchor) == topBottomAnchor)
42             {
43                 height = parent.Size.Height - currentNode.Margin.Top - currentNode.Margin.Bottom;
44                 if (height < 0) { height = 0; }
45             }
46             else
47             {
48                 height = currentNode.Size.Height;
49             }
50 
51             if ((currentNode.Anchor & leftRightAnchor) == AnchorStyles.None)
52             {
53                 x = (int)(
54                     (parent.Size.Width - width)
55                     * ((double)currentNode.Margin.Left / (double)(currentNode.Margin.Left + currentNode.Margin.Right)));
56             }
57             else if ((currentNode.Anchor & leftRightAnchor) == AnchorStyles.Left)
58             {
59                 x = currentNode.Margin.Left;
60             }
61             else if ((currentNode.Anchor & leftRightAnchor) == AnchorStyles.Right)
62             {
63                 x = parent.Size.Width - width - currentNode.Margin.Right;
64             }
65             else if ((currentNode.Anchor & leftRightAnchor) == leftRightAnchor)
66             {
67                 x = currentNode.Margin.Left;
68             }
69             else
70             { throw new Exception("uiRenderer should not happen!"); }
71 
72             if ((currentNode.Anchor & topBottomAnchor) == AnchorStyles.None)
73             {
74                 y = (int)(
75                     (parent.Size.Height - height)
76                     * ((double)currentNode.Margin.Bottom / (double)(currentNode.Margin.Bottom + currentNode.Margin.Top)));
77             }
78             else if ((currentNode.Anchor & topBottomAnchor) == AnchorStyles.Bottom)
79             {
80                 y = currentNode.Margin.Bottom;
81             }
82             else if ((currentNode.Anchor & topBottomAnchor) == AnchorStyles.Top)
83             {
84                 y = parent.Size.Height - height - currentNode.Margin.Top;
85             }
86             else if ((currentNode.Anchor & topBottomAnchor) == topBottomAnchor)
87             {
88                 y = currentNode.Margin.Bottom;
89             }
90             else
91             { throw new Exception("This should not happen!"); }
92 
93             currentNode.Location = new System.Drawing.Point(x, y);
94             currentNode.Size = new Size(width, height);
95         }
public static void Layout(this ILayout uiRenderer)

glViewport/glScissor

这是避免复杂的矩阵操作,实现稳定的UI布局显示的关键。glViewport指定了GLRenderer在窗口的渲染位置,glScissor将GLRenderer范围之外的部分保护起来。

在渲染之前,根据UIRenderer的位置和大小更新viewport和scissor即可。不再需要为UI固定在窗口某处而煞费苦心地设计projection,view,model矩阵了。

技术分享
 1     /// <summary>
 2     /// 支持UI布局的渲染器
 3     /// </summary>
 4     public class UIRenderer : RendererBase, ILayout
 5     {
 6         private ViewportSwitch viewportSwitch;
 7         private ScissorTestSwitch scissorTestSwitch;
 8 
 9         public Renderer Renderer { get; protected set; }
10 
11         public UIRenderer(Renderer renderer,
12             System.Windows.Forms.AnchorStyles anchor, System.Windows.Forms.Padding margin,
13             System.Drawing.Size size, int zNear, int zFar)
14         {
15             this.Controls = new ILayoutCollection(this);
16 
17             this.Renderer = renderer;
18             this.Anchor = anchor; this.Margin = margin;
19             this.Size = size; this.zNear = zNear; this.zFar = zFar;
20         }
21 
22         public ILayout Container { get; set; }
23 
24         public ICollection<ILayout> Controls { get; internal set; }
25 
26         public System.Windows.Forms.AnchorStyles Anchor { get; set; }
27 
28         public System.Windows.Forms.Padding Margin { get; set; }
29 
30         public System.Drawing.Point Location { get; set; }
31 
32         public System.Drawing.Size Size { get; set; }
33 
34         public int zNear { get; set; }
35 
36         public int zFar { get; set; }
37 
38         protected override void DoInitialize()
39         {
40             this.viewportSwitch = new ViewportSwitch();
41             this.scissorTestSwitch = new ScissorTestSwitch();
42 
43             Renderer renderer = this.Renderer;
44             if (renderer != null)
45             {
46                 renderer.Initialize();
47             }
48         }
49 
50         protected override void DoRender(RenderEventArgs arg)
51         {
52             Renderer renderer = this.Renderer;
53             if (renderer != null)
54             {
55                 this.viewportSwitch.X = this.Location.X;
56                 this.viewportSwitch.Y = this.Location.Y;
57                 this.viewportSwitch.Width = this.Size.Width;
58                 this.viewportSwitch.Height = this.Size.Height;
59                 this.scissorTestSwitch.X = this.Location.X;
60                 this.scissorTestSwitch.Y = this.Location.Y;
61                 this.scissorTestSwitch.Width = this.Size.Width;
62                 this.scissorTestSwitch.Height = this.Size.Height;
63 
64                 this.scissorTestSwitch.On();
65                 this.viewportSwitch.On();
66 
67                 // 把所有在此之前渲染的内容都推到最远。
68                 OpenGL.Clear(OpenGL.GL_DEPTH_BUFFER_BIT);
69 
70                 renderer.Render(arg);
71 
72                 this.viewportSwitch.Off();
73                 this.scissorTestSwitch.Off();
74             }
75         }
76 
77         protected override void DisposeUnmanagedResources()
78         {
79             Renderer renderer = this.Renderer;
80             if (renderer != null)
81             {
82                 renderer.Dispose();
83             }
84         }
85     }
UIRenderer

叠加/覆盖

注意在UIRenderer.DoRender(RenderEventArgs arg)中,使用

1                 // 把所有在此之前渲染的内容都推到最远。
2                 OpenGL.Clear(OpenGL.GL_DEPTH_BUFFER_BIT);

把所有在此之前渲染的内容都推到最远。

从ILayout的定义中可以看到,控件与控件组成了一个树结构。其根结点是覆盖整个窗口的控件,在渲染UI时处于第一个渲染的位置,然后渲染它的各个子结点代表的控件。这就实现了子控件能够完全覆盖在父控件之上。

我突然想到了WPF。

渲染文字

从TTF文件获取字形

https://github.com/MikePopoloski/SharpFont)是一个纯C#的解析TTF文件的库,能够代替C++的FreeType。我将其稍作修改,实现了从TTF文件获取任意uncode字形,进而获取字形纹理,实现渲染文字的功能。

例如下面这几个字形纹理。

技术分享

技术分享

技术分享

技术分享

技术分享

技术分享

使用FontResource

FontResource类型封装了使用字形贴图的功能。

使用方式也非常简单。首先创建一个字体资源对象。

1 FontResource fontResouce = FontResource.Load(ttfFilename,  , (char)126);

然后交给GLText。

1 var glText = new GLText(AnchorStyles.Left | AnchorStyles.Top,
2     new Padding(3, 3, 3, 3), new Size(850, 50), -100, 100, fontResouce);
3 glText.Initialize();
4 glText.SetText("The quick brown fox jumps over the lazy dog!");

GLText在初始化时指定此字体对象包含的二维纹理。

1         protected override void DoInitialize()
2         {
3             base.DoInitialize();
4 
5             this.Renderer.SetUniform("fontTexture", this.fontResource.GetSamplerValue());
6         }

总结

CSharpGL支持控件布局,支持渲染文字了。

欢迎对OpenGL有兴趣的同学关注(https://github.com/bitzhuwei/CSharpGL

CSharpGL(26)在opengl中实现控件布局/渲染文字

标签:

原文地址:http://www.cnblogs.com/bitzhuwei/p/CSharpGL-26-UI-layout-and-text-rendering.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!