CSharpGL(26)在opengl中实现控件布局/渲染文字
Posted 天下事有难易乎
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CSharpGL(26)在opengl中实现控件布局/渲染文字相关的知识,希望对你有一定的参考价值。
CSharpGL(26)在opengl中实现控件布局/渲染文字
效果图
如图所示,可以将文字、坐标轴固定在窗口的一角。
下载
CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL)
UI控件布局关键点
ILayout
类似Winform控件那样,控件的位置、大小由其Anchor等属性决定。窗口大小改变时,控件的位置、大小会随之改变。
所以模仿Control类,直接使用Anchor作为UIRenderer的接口。
1 /// <summary> 2 /// Supports layout UI element in OpenGL canvas. 3 /// 实现在OpenGL窗口中的UI布局 4 /// </summary> 5 public interface ILayout : ITreeNode<UIRenderer> 6 { 7 //event EventHandler afterLayout; 8 9 /// <summary> 10 /// the edges of the <see cref="GLCanvas"/> to which a UI’s rect is bound and determines how it is resized with its parent. 11 /// <para>something like AnchorStyles.Left | AnchorStyles.Bottom.</para> 12 /// </summary> 13 System.Windows.Forms.AnchorStyles Anchor { get; set; } 14 15 /// <summary> 16 /// Gets or sets the space between viewport and SimpleRect. 17 /// </summary> 18 System.Windows.Forms.Padding Margin { get; set; } 19 20 /// <summary> 21 /// 相对于Parent左下角的位置(Left Down location) 22 /// </summary> 23 System.Drawing.Point Location { get; set; } 24 25 /// <summary> 26 /// Stores width when <see cref="Anchor"/>.Left & <see cref="Anchor"/>.Right is <see cref="Anchor"/>.None. 27 /// <para> and height when <see cref="Anchor"/>.Top & <see cref="Anchor"/>.Bottom is <see cref="Anchor"/>.None.</para> 28 /// </summary> 29 System.Drawing.Size Size { get; set; } 30 31 /// <summary> 32 /// 33 /// </summary> 34 System.Drawing.Size ParentLastSize { get; set; } 35 36 /// <summary> 37 /// 38 /// </summary> 39 int zNear { get; set; } 40 41 /// <summary> 42 /// 43 /// </summary> 44 int zFar { get; set; } 45 46 }
实现在OpenGL窗口中的UI布局
有了数据结构,就可以实现窗口中的UI布局了。当窗口大小改变时,调用下面的函数。
1 /// <summary> 2 /// layout controls in OpenGL canvas. 3 /// <para>This coordinate system is as below.</para> 4 /// <para> /\\ y</para> 5 /// <para> |</para> 6 /// <para> |</para> 7 /// <para> |</para> 8 /// <para> |</para> 9 /// <para> |</para> 10 /// <para> |----------------->x</para> 11 /// <para>(0, 0)</para> 12 /// </summary> 13 /// <param name="uiRenderer"></param> 14 internal static void Layout(this ILayout uiRenderer) 15 { 16 ILayout parent = uiRenderer.Parent; 17 if (parent != null) 18 { 19 uiRenderer.Self.DoBeforeLayout(); 20 NonRootNodeLayout(uiRenderer, parent); 21 uiRenderer.Self.DoAfterLayout(); 22 } 23 24 foreach (var item in uiRenderer.Children) 25 { 26 item.Layout(); 27 } 28 29 if (parent != null) 30 { 31 uiRenderer.ParentLastSize = parent.Size; 32 } 33 } 34 35 /// <summary> 36 /// leftRightAnchor = (AnchorStyles.Left | AnchorStyles.Right); 37 /// </summary> 38 private const AnchorStyles leftRightAnchor = (AnchorStyles.Left | AnchorStyles.Right); 39 40 /// <summary> 41 /// topBottomAnchor = (AnchorStyles.Top | AnchorStyles.Bottom); 42 /// </summary> 43 private const AnchorStyles topBottomAnchor = (AnchorStyles.Top | AnchorStyles.Bottom); 44 45 /// <summary> 46 /// Gets <paramref name="currentNode"/>\'s location and size according to its state and parent\'s information. 47 /// </summary> 48 /// <param name="currentNode"></param> 49 /// <param name="parent"></param> 50 private static void NonRootNodeLayout(ILayout currentNode, ILayout parent) 51 { 52 int x, y, width, height; 53 if ((currentNode.Anchor & leftRightAnchor) == leftRightAnchor) 54 { 55 width = parent.Size.Width - currentNode.Margin.Left - currentNode.Margin.Right; 56 //width = currentNode.Size.Width + (parent.Size.Width - currentNode.ParentLastSize.Width); 57 if (width < 0) { width = 0; } 58 } 59 else 60 { 61 width = currentNode.Size.Width; 62 } 63 64 if ((currentNode.Anchor & topBottomAnchor) == topBottomAnchor) 65 { 66 height = parent.Size.Height - currentNode.Margin.Top - currentNode.Margin.Bottom; 67 //height = currentNode.Size.Height + (parent.Size.Height - currentNode.ParentLastSize.Height); 68 if (height < 0) { height = 0; } 69 } 70 else 71 { 72 height = currentNode.Size.Height; 73 } 74 75 if ((currentNode.Anchor & leftRightAnchor) == AnchorStyles.None) 76 { 77 x = (int)( 78 (parent.Size.Width - width) 79 * ((double)currentNode.Margin.Left / (double)(currentNode.Margin.Left + currentNode.Margin.Right))); 80 } 81 else if ((currentNode.Anchor & leftRightAnchor) == AnchorStyles.Left) 82 { 83 x = parent.Location.X + currentNode.Margin.Left; 84 } 85 else if ((currentNode.Anchor & leftRightAnchor) == AnchorStyles.Right) 86 { 87 x = parent.Location.X + parent.Size.Width - currentNode.Margin.Right - width; 88 } 89 else if ((currentNode.Anchor & leftRightAnchor) == leftRightAnchor) 90 { 91 x = parent.Location.X + currentNode.Margin.Left; 92 } 93 else 94 { throw new Exception("uiRenderer should not happen!"); } 95 96 if ((currentNode.Anchor & topBottomAnchor) == AnchorStyles.None) 97 { 98 y = (int)( 99 (parent.Size.Height - height) 100 * ((double)currentNode.Margin.Bottom / (double)(currentNode.Margin.Bottom + currentNode.Margin.Top))); 101 } 102 else if ((currentNode.Anchor & topBottomAnchor) == AnchorStyles.Bottom) 103 { 104 //y = currentNode.Margin.Bottom; 105 y = parent.Location.Y + currentNode.Margin.Bottom; 106 } 107 else if ((currentNode.Anchor & topBottomAnchor) == AnchorStyles.Top) 108 { 109 //y = parent.Size.Height - height - currentNode.Margin.Top; 110 y = parent.Location.Y + parent.Size.Height - currentNode.Margin.Top - height; 111 } 112 else if ((currentNode.Anchor & topBottomAnchor) == topBottomAnchor) 113 { 114 //y = currentNode.Margin.Top + parent.Location.Y; 115 y = parent.Location.Y + currentNode.Margin.Bottom; 116 } 117 else 118 { throw new Exception("This should not happen!"); } 119 120 currentNode.Location = new System.Drawing.Point(x, y); 121 currentNode.Size = new Size(width, height); 122 }
glViewport/glScissor
这是避免复杂的矩阵操作,实现稳定的UI布局显示的关键。glViewport指定了GLRenderer在窗口的渲染位置,glScissor将GLRenderer范围之外的部分保护起来。
在渲染之前,根据UIRenderer的位置和大小更新viewport和scissor即可。不再需要为UI固定在窗口某处而煞费苦心地设计projection,view,model矩阵了。
1 /// <summary> 2 /// Renderer that supports UI layout. 3 /// 支持2D UI布局的渲染器 4 /// </summary> 5 public class UIRenderer : RendererBase, ILayout 6 { 7 private ViewportSwitch viewportSwitch; 8 private ScissorTestSwitch scissorTestSwitch; 9 private GLSwitchList switchList = new GLSwitchList(); 10 11 /// <summary> 12 /// 13 /// </summary> 14 public GLSwitchList SwitchList 15 { 16 get { return switchList; } 17 } 18 19 /// <summary> 20 /// triggered before layout in <see cref="ILayout"/>.Layout(). 21 /// </summary> 22 public event EventHandler BeforeLayout; 23 /// <summary> 24 /// triggered after layout in <see cref="ILayout"/>.Layout(). 25 /// </summary> 26 public event EventHandler AfterLayout; 27 28 internal void DoBeforeLayout() 29 { 30 EventHandler BeforeLayout = this.BeforeLayout; 31 if (BeforeLayout != null) 32 { 33 BeforeLayout(this, null); 34 } 35 } 36 37 internal void DoAfterLayout() 38 { 39 EventHandler AfterLayout = this.AfterLayout; 40 if (AfterLayout != null) 41 { 42 AfterLayout(this, null); 43 } 44 } 45 46 /// <summary> 47 /// 48 /// </summary> 49 public RendererBase Renderer { get; protected set; } 50 /// <summary> 51 /// 52 /// </summary> 53 /// <param name="anchor"></param> 54 /// <param name="margin"></param> 55 /// <param name="size"></param> 56 /// <param name="zNear"></param> 57 /// <param name="zFar"></param> 58 public UIRenderer( 59 System.Windows.Forms.AnchorStyles anchor, System.Windows.Forms.Padding margin, 60 System.Drawing.Size size, int zNear, int zFar) 61 { 62 this.Children = new ChildList<UIRenderer>(this);// new ILayoutList(this); 63 64 this.Anchor = anchor; this.Margin = margin; 65 this.Size = size; this.zNear = zNear; this.zFar = zFar; 66 } 67 68 /// <summary> 69 /// 70 /// </summary> 71 public System.Windows.Forms.AnchorStyles Anchor { get; set; } 72 73 /// <summary> 74 /// 75 /// </summary> 76 public System.Windows.Forms.Padding Margin { get; set; } 77 78 /// <summary> 79 /// 80 /// </summary> 81 public System.Drawing.Point Location { get; set; } 82 83 /// <summary> 84 /// 85 /// </summary> 86 public System.Drawing.Size Size { get; set; } 87 /// <summary> 88 /// 89 /// </summary> 90 public System.Drawing.Size ParentLastSize { get; set; } 91 92 /// <summary> 93 /// 94 /// </summary> 95 public int zNear { get; set; } 96 97 /// <summary> 98 /// 99 /// </summary> 100 public int zFar { get; set; } 101 102 /// <summary> 103 /// 104 /// </summary> 105 protected override void DoInitialize() 106 { 107 this.viewportSwitch = new ViewportSwitch(); 108 this.scissorTestSwitch = new ScissorTestSwitch(); 109 110 RendererBase renderer = this.Renderer; 111 if (renderer != null) 112 { 113 renderer.Initialize(); 114 } 115 } 116 117 /// <summary> 118 /// 119 /// </summary> 120 /// <param name="arg"></param> 121 protected override void DoRender(RenderEventArg arg) 122 { 123 this.viewportSwitch.X = this.Location.X; 124 this.viewportSwitch.Y = this.Location.Y; 125 this.viewportSwitch.Width = this.Size.Width; 126 this.viewportSwitch.Height = this.Size.Height; 127 this.scissorTestSwitch.X = this.Location.X; 128 this.scissorTestSwitch.Y = this.Location.Y; 129 this.scissorTestSwitch.Width = this.Size.Width; 130 this.scissorTestSwitch.Height = this.Size.Height; 131 132 this.viewportSwitch.On(); 133 this.scissorTestSwitch.On(); 134 int count = this.switchList.Count; 135 for (int i = 0; i < count; i++) { this.switchList[i].On(); } 136 137 // 把所有在此之前渲染的内容都推到最远。 138 // Push all rendered stuff to farest position. 139 OpenGL.Clear(OpenGL.GL_DEPTH_BUFFER_BIT); 140 141 RendererBase renderer = this.Renderer; 142 if (renderer != null) 143 { 144 renderer.Render(arg); 145 } 146 147 for (int i = count - 1; i >= 0; i--) { this.switchList[i].Off(); } 148 this.scissorTestSwitch.Off(); 149 this.viewportSwitch.Off(); 150 } 151 152 /// <summary> 153 /// 154 /// </summary> 155 protected override void DisposeUnmanagedResources() 156 { 157 RendererBase renderer = this.Renderer; 158 if (renderer != null) 159 { 160 renderer.Dispose(); 161 } 162 } 163 164 /// <summary> 165 /// 166 /// </summary> 167 public UIRenderer Self { get { return this; } } 168 169 /// <summary> 170 /// 171 /// </summary> 172 public UIRenderer Parent { get; set; } 173 174 //ChildList<UIRenderer> children; 175 176 /// <summary> 177 /// 178 /// </summary> 179 [Editor(typeof(IListEditor<UIRenderer>), typeof(UITypeEditor))] 180 public ChildList<UIRenderer> Children { get; private set; } 181 }
以上是关于CSharpGL(26)在opengl中实现控件布局/渲染文字的主要内容,如果未能解决你的问题,请参考以下文章