CSharpGL(21)用鼠标拾取、拖拽VBO图元内的点、线或本身
以最常见的三角形网格(用GL_TRIANGLES方式进行渲染)为例。
在拾取模式为GeometryType.Point时,你可以拾取单个的顶点。
在拾取模式为GeometryType.Line时,你可以拾取任意一个三角形里的任意一条线。即同时拾取此线段的两个顶点。
在拾取模式为GeometryType.Triangle时,你可以拾取任意一个三角形。即同时拾取此三角形的三个顶点。
实际上,CSharpGL实现了在所有渲染模式下拾取Point、Line、Triangle、Quad和Polygon的功能。(当然,你可以想象,如果想在一个GL_TRIANGLES渲染方式下拾取一个Quad,那是什么都拾取不到的)下面是描述这一功能的图示。由于我的白板小,就没有列出GL_TRIANGLES_ADJACENCY、GL_TRIANGLE_STRIP_ADJACENCY、GL_LINES_ADJACENCY、GL_LINE_STRIP_ADJCANCEY这几个情况。
下载
CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL)
规定
为了简便描述,我用GL_LINE*代表GL_LINES、GL_LINE_STRIP、GL_LINES_ADJACENCY、GL_LINE_STRIP_ADJACENCY,用GL_TRIANGLE*代表GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN、GL_TRIANGLES_ADJACENCY、GL_TRIANGLE_STRIP_ADJACENCY,用GL_QUAD*代表GL_QUADS、GL_QUAD_STRIP。
如何使用
使用方式十分简单,只需给RenderEventArgs传入如下的参数:
1 GeometryType PickingGeometryType = Geometry.Point; 2 var arg = new RenderEventArgs( 3 // 为了拾取而进行的渲染 4 RenderModes.ColorCodedPicking, 5 this.glCanvas1.ClientRectangle, 6 this.camera, 7 // 我想拾取的类型(Geometry) 8 this.PickingGeometryType); 9 // 要拾取的位置(鼠标位置) 10 Point mousePostion = GetMousePosition(); 11 // 支持Picking的Renderer列表 12 PickableRenderer[] pickableElements = GetRenderersInScene(); 13 // 执行拾取操作 14 PickedGeometry pickedGeometry = ColorCodedPicking.Pick(arg, mousePostion, pickableElements);
具体用法详见(CSharpGL(20)用unProject和Project实现鼠标拖拽图元)
如何实现
在GL_POINTS时拾取Point,在GL_LINE*时拾取Line,在GL_TRIANGL*时拾取Triangle,在GL_QUAD*时拾取Quad,在GL_POLYGON时拾取Polygon,这都是已经实现了的(CSharpGL(18)分别处理glDrawArrays()和glDrawElements()两种方式下的拾取(ColorCodedPicking))。这些不再详述。
拾取Point
ZeroIndexRenderer
在除了GL_POINTS时,想拾取一个Point,只能用 glDrawArrays(GL_POINTS, ..); 来代替原有的 glDrawArrays(OriginalMode, ..); 。但这会渲染所有的顶点。而在OriginalMode下,未必渲染所有的顶点。所以在拾取到一个Point后要判断一下是否真的应该拾取到它。
1 /// <summary> 2 /// 现在,已经判定了鼠标在某个点上。 3 /// 我需要判定此点是否出现在图元上。 4 /// now that I know the mouse is picking on some point, 5 /// I need to make sure that point should appear. 6 /// </summary> 7 /// <param name="lastVertexId"></param> 8 /// <param name="mode"></param> 9 /// <returns></returns> 10 private bool OnPrimitiveTest(uint lastVertexId, DrawMode mode) 11 { 12 bool result = false; 13 int first = this.zeroIndexBufferPtr.FirstVertex; 14 if (first < 0) { return false; } 15 int vertexCount = this.zeroIndexBufferPtr.VertexCount; 16 if (vertexCount <= 0) { return false; } 17 int last = first + vertexCount - 1; 18 switch (mode) 19 { 20 case DrawMode.Points: 21 result = true; 22 break; 23 case DrawMode.LineStrip: 24 result = vertexCount > 1; 25 break; 26 case DrawMode.LineLoop: 27 result = vertexCount > 1; 28 break; 29 case DrawMode.Lines: 30 if (vertexCount > 1) 31 { 32 if (vertexCount % 2 == 0) 33 { 34 result = (first <= lastVertexId && lastVertexId <= last); 35 } 36 else 37 { 38 result = (first <= lastVertexId && lastVertexId <= last - 1); 39 } 40 } 41 break; 42 case DrawMode.LineStripAdjacency: 43 if (vertexCount > 3) 44 { 45 result = (first < lastVertexId && lastVertexId < last); 46 } 47 break; 48 case DrawMode.LinesAdjacency: 49 if (vertexCount > 3) 50 { 51 var lastPart = last - (last + 1 - first) % 4; 52 if (first <= lastVertexId && lastVertexId <= lastPart) 53 { 54 var m = (lastVertexId - first) % 4; 55 result = (m == 1 || m == 2); 56 } 57 } 58 break; 59 case DrawMode.TriangleStrip: 60 if (vertexCount > 2) 61 { 62 result = vertexCount > 2; 63 } 64 break; 65 case DrawMode.TriangleFan: 66 if (vertexCount > 2) 67 { 68 result = vertexCount > 2; 69 } 70 break; 71 case DrawMode.Triangles: 72 if (vertexCount > 2) 73 { 74 if (first <= lastVertexId) 75 { 76 result = ((vertexCount % 3 == 0) && (lastVertexId <= last)) 77 || ((vertexCount % 3 == 1) && (lastVertexId < last)) 78 || ((vertexCount % 3 == 2) && (lastVertexId + 1 < last)); 79 } 80 } 81 break; 82 case DrawMode.TriangleStripAdjacency: 83 if (vertexCount > 5) 84 { 85 var lastPart = last - (last + 1 - first) % 2; 86 if (first <= lastVertexId && lastVertexId <= lastPart) 87 { 88 result = (lastVertexId - first) % 2 == 0; 89 } 90 } 91 break; 92 case DrawMode.TrianglesAdjacency: 93 if (vertexCount > 5) 94 { 95 var lastPart = last - (last + 1 - first) % 6; 96 if (first <= lastVertexId && lastVertexId <= lastPart) 97 { 98 result = (lastVertexId - first) % 2 == 0; 99 } 100 } 101 break; 102 case DrawMode.Patches: 103 // not know what to do for now 104 break; 105 case DrawMode.QuadStrip: 106 if (vertexCount > 3) 107 { 108 if (first <= lastVertexId && lastVertexId <= last) 109 { 110 result = (vertexCount % 2 == 0) 111 || (lastVertexId < last); 112 } 113 } 114 break; 115 case DrawMode.Quads: 116 if (vertexCount > 3) 117 { 118 if (first <= lastVertexId && lastVertexId <= last) 119 { 120 var m = vertexCount % 4; 121 if (m == 0) { result = true; } 122 else if (m == 1) { result = lastVertexId + 0 < last; } 123 else if (m == 2) { result = lastVertexId + 1 < last; } 124 else if (m == 3) { result = lastVertexId + 2 < last; } 125 else { throw new Exception("This should never happen!"); } 126 } 127 } 128 break; 129 case DrawMode.Polygon: 130 if (vertexCount > 2) 131 { 132 result = (first <= lastVertexId && lastVertexId <= last); 133 } 134 break; 135 default: 136 throw new NotImplementedException(); 137 } 138 139 return result; 140 }