aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/ModifiedBulletX/ModifiedBulletX/Collision/CollisionShapes/OptimizedBvh.cs
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/ModifiedBulletX/ModifiedBulletX/Collision/CollisionShapes/OptimizedBvh.cs')
-rw-r--r--libraries/ModifiedBulletX/ModifiedBulletX/Collision/CollisionShapes/OptimizedBvh.cs586
1 files changed, 293 insertions, 293 deletions
diff --git a/libraries/ModifiedBulletX/ModifiedBulletX/Collision/CollisionShapes/OptimizedBvh.cs b/libraries/ModifiedBulletX/ModifiedBulletX/Collision/CollisionShapes/OptimizedBvh.cs
index c22c990..5b7a5e4 100644
--- a/libraries/ModifiedBulletX/ModifiedBulletX/Collision/CollisionShapes/OptimizedBvh.cs
+++ b/libraries/ModifiedBulletX/ModifiedBulletX/Collision/CollisionShapes/OptimizedBvh.cs
@@ -1,293 +1,293 @@
1/* 1/*
2 Bullet for XNA Copyright (c) 2003-2007 Vsevolod Klementjev http://www.codeplex.com/xnadevru 2 Bullet for XNA Copyright (c) 2003-2007 Vsevolod Klementjev http://www.codeplex.com/xnadevru
3 Bullet original C++ version Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com 3 Bullet original C++ version Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com
4 4
5 This software is provided 'as-is', without any express or implied 5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages 6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software. 7 arising from the use of this software.
8 8
9 Permission is granted to anyone to use this software for any purpose, 9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it 10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions: 11 freely, subject to the following restrictions:
12 12
13 1. The origin of this software must not be misrepresented; you must not 13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software 14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be 15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required. 16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be 17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software. 18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution. 19 3. This notice may not be removed or altered from any source distribution.
20*/ 20*/
21 21
22using System; 22using System;
23using System.Collections.Generic; 23using System.Collections.Generic;
24using System.Text; 24using System.Text;
25using MonoXnaCompactMaths; 25using MonoXnaCompactMaths;
26 26
27namespace XnaDevRu.BulletX 27namespace XnaDevRu.BulletX
28{ 28{
29 /// <summary> 29 /// <summary>
30 /// OptimizedBvh store an AABB tree that can be quickly traversed on CPU (and SPU,GPU in future) 30 /// OptimizedBvh store an AABB tree that can be quickly traversed on CPU (and SPU,GPU in future)
31 /// </summary> 31 /// </summary>
32 public class OptimizedBvh 32 public class OptimizedBvh
33 { 33 {
34 private static int _maxIterations = 0; 34 private static int _maxIterations = 0;
35 private OptimizedBvhNode _rootNode; 35 private OptimizedBvhNode _rootNode;
36 36
37 private OptimizedBvhNode[] _contiguousNodes; 37 private OptimizedBvhNode[] _contiguousNodes;
38 private int _curNodeIndex; 38 private int _curNodeIndex;
39 39
40 private List<OptimizedBvhNode> _leafNodes = new List<OptimizedBvhNode>(); 40 private List<OptimizedBvhNode> _leafNodes = new List<OptimizedBvhNode>();
41 41
42 public OptimizedBvh() { } 42 public OptimizedBvh() { }
43 43
44 public void Build(StridingMeshInterface triangles) 44 public void Build(StridingMeshInterface triangles)
45 { 45 {
46 NodeTriangleCallback callback = new NodeTriangleCallback(_leafNodes); 46 NodeTriangleCallback callback = new NodeTriangleCallback(_leafNodes);
47 47
48 Vector3 aabbMin = new Vector3(-1e30f, -1e30f, -1e30f); 48 Vector3 aabbMin = new Vector3(-1e30f, -1e30f, -1e30f);
49 Vector3 aabbMax = new Vector3(1e30f, 1e30f, 1e30f); 49 Vector3 aabbMax = new Vector3(1e30f, 1e30f, 1e30f);
50 50
51 triangles.InternalProcessAllTriangles(callback, aabbMin, aabbMax); 51 triangles.InternalProcessAllTriangles(callback, aabbMin, aabbMax);
52 52
53 //now we have an array of leafnodes in m_leafNodes 53 //now we have an array of leafnodes in m_leafNodes
54 _contiguousNodes = new OptimizedBvhNode[2 * _leafNodes.Count]; 54 _contiguousNodes = new OptimizedBvhNode[2 * _leafNodes.Count];
55 for (int i = 0; i < _contiguousNodes.Length; i++) 55 for (int i = 0; i < _contiguousNodes.Length; i++)
56 _contiguousNodes[i] = new OptimizedBvhNode(); 56 _contiguousNodes[i] = new OptimizedBvhNode();
57 _curNodeIndex = 0; 57 _curNodeIndex = 0;
58 58
59 _rootNode = BuildTree(_leafNodes, 0, _leafNodes.Count); 59 _rootNode = BuildTree(_leafNodes, 0, _leafNodes.Count);
60 } 60 }
61 61
62 public OptimizedBvhNode BuildTree(List<OptimizedBvhNode> leafNodes, int startIndex, int endIndex) 62 public OptimizedBvhNode BuildTree(List<OptimizedBvhNode> leafNodes, int startIndex, int endIndex)
63 { 63 {
64 OptimizedBvhNode internalNode; 64 OptimizedBvhNode internalNode;
65 65
66 int splitAxis, splitIndex, i; 66 int splitAxis, splitIndex, i;
67 int numIndices = endIndex - startIndex; 67 int numIndices = endIndex - startIndex;
68 int curIndex = _curNodeIndex; 68 int curIndex = _curNodeIndex;
69 69
70 if (numIndices <= 0) 70 if (numIndices <= 0)
71 throw new BulletException(); 71 throw new BulletException();
72 72
73 if (numIndices == 1) 73 if (numIndices == 1)
74 { 74 {
75 _contiguousNodes[_curNodeIndex++] = leafNodes[startIndex]; 75 _contiguousNodes[_curNodeIndex++] = leafNodes[startIndex];
76 //return new (&m_contiguousNodes[m_curNodeIndex++]) btOptimizedBvhNode(leafNodes[startIndex]); 76 //return new (&m_contiguousNodes[m_curNodeIndex++]) btOptimizedBvhNode(leafNodes[startIndex]);
77 return leafNodes[startIndex]; 77 return leafNodes[startIndex];
78 } 78 }
79 79
80 //calculate Best Splitting Axis and where to split it. Sort the incoming 'leafNodes' array within range 'startIndex/endIndex'. 80 //calculate Best Splitting Axis and where to split it. Sort the incoming 'leafNodes' array within range 'startIndex/endIndex'.
81 splitAxis = CalculateSplittingAxis(leafNodes, startIndex, endIndex); 81 splitAxis = CalculateSplittingAxis(leafNodes, startIndex, endIndex);
82 82
83 splitIndex = SortAndCalculateSplittingIndex(leafNodes, startIndex, endIndex, splitAxis); 83 splitIndex = SortAndCalculateSplittingIndex(leafNodes, startIndex, endIndex, splitAxis);
84 84
85 internalNode = _contiguousNodes[_curNodeIndex++]; 85 internalNode = _contiguousNodes[_curNodeIndex++];
86 86
87 internalNode.AabbMax = new Vector3(-1e30f, -1e30f, -1e30f); 87 internalNode.AabbMax = new Vector3(-1e30f, -1e30f, -1e30f);
88 internalNode.AabbMin = new Vector3(1e30f, 1e30f, 1e30f); 88 internalNode.AabbMin = new Vector3(1e30f, 1e30f, 1e30f);
89 89
90 for (i = startIndex; i < endIndex; i++) 90 for (i = startIndex; i < endIndex; i++)
91 { 91 {
92 internalNode.AabbMax = MathHelper.SetMax(internalNode.AabbMax, leafNodes[i].AabbMax); 92 internalNode.AabbMax = MathHelper.SetMax(internalNode.AabbMax, leafNodes[i].AabbMax);
93 internalNode.AabbMin = MathHelper.SetMin(internalNode.AabbMin, leafNodes[i].AabbMin); 93 internalNode.AabbMin = MathHelper.SetMin(internalNode.AabbMin, leafNodes[i].AabbMin);
94 } 94 }
95 95
96 //internalNode->m_escapeIndex; 96 //internalNode->m_escapeIndex;
97 internalNode.LeftChild = BuildTree(leafNodes, startIndex, splitIndex); 97 internalNode.LeftChild = BuildTree(leafNodes, startIndex, splitIndex);
98 internalNode.RightChild = BuildTree(leafNodes, splitIndex, endIndex); 98 internalNode.RightChild = BuildTree(leafNodes, splitIndex, endIndex);
99 99
100 internalNode.EscapeIndex = _curNodeIndex - curIndex; 100 internalNode.EscapeIndex = _curNodeIndex - curIndex;
101 return internalNode; 101 return internalNode;
102 } 102 }
103 103
104 public int CalculateSplittingAxis(List<OptimizedBvhNode> leafNodes, int startIndex, int endIndex) 104 public int CalculateSplittingAxis(List<OptimizedBvhNode> leafNodes, int startIndex, int endIndex)
105 { 105 {
106 Vector3 means = new Vector3(); 106 Vector3 means = new Vector3();
107 Vector3 variance = new Vector3(); 107 Vector3 variance = new Vector3();
108 int numIndices = endIndex - startIndex; 108 int numIndices = endIndex - startIndex;
109 109
110 for (int i = startIndex; i < endIndex; i++) 110 for (int i = startIndex; i < endIndex; i++)
111 { 111 {
112 Vector3 center = 0.5f * (leafNodes[i].AabbMax + leafNodes[i].AabbMin); 112 Vector3 center = 0.5f * (leafNodes[i].AabbMax + leafNodes[i].AabbMin);
113 means += center; 113 means += center;
114 } 114 }
115 means *= (1f / (float)numIndices); 115 means *= (1f / (float)numIndices);
116 116
117 for (int i = startIndex; i < endIndex; i++) 117 for (int i = startIndex; i < endIndex; i++)
118 { 118 {
119 Vector3 center = 0.5f * (leafNodes[i].AabbMax + leafNodes[i].AabbMin); 119 Vector3 center = 0.5f * (leafNodes[i].AabbMax + leafNodes[i].AabbMin);
120 Vector3 diff2 = center - means; 120 Vector3 diff2 = center - means;
121 diff2 = diff2 * diff2; 121 diff2 = diff2 * diff2;
122 variance += diff2; 122 variance += diff2;
123 } 123 }
124 variance *= (1f / ((float)numIndices - 1)); 124 variance *= (1f / ((float)numIndices - 1));
125 125
126 return MathHelper.MaxAxis(variance); 126 return MathHelper.MaxAxis(variance);
127 } 127 }
128 128
129 public int SortAndCalculateSplittingIndex(List<OptimizedBvhNode> leafNodes, int startIndex, int endIndex, int splitAxis) 129 public int SortAndCalculateSplittingIndex(List<OptimizedBvhNode> leafNodes, int startIndex, int endIndex, int splitAxis)
130 { 130 {
131 int splitIndex = startIndex; 131 int splitIndex = startIndex;
132 int numIndices = endIndex - startIndex; 132 int numIndices = endIndex - startIndex;
133 float splitValue; 133 float splitValue;
134 134
135 Vector3 means = new Vector3(); 135 Vector3 means = new Vector3();
136 for (int i = startIndex; i < endIndex; i++) 136 for (int i = startIndex; i < endIndex; i++)
137 { 137 {
138 Vector3 center = 0.5f * (leafNodes[i].AabbMax + leafNodes[i].AabbMin); 138 Vector3 center = 0.5f * (leafNodes[i].AabbMax + leafNodes[i].AabbMin);
139 means += center; 139 means += center;
140 } 140 }
141 means *= (1f / (float)numIndices); 141 means *= (1f / (float)numIndices);
142 142
143 if (splitAxis == 0) 143 if (splitAxis == 0)
144 splitValue = means.X; 144 splitValue = means.X;
145 else if (splitAxis == 1) 145 else if (splitAxis == 1)
146 splitValue = means.Y; 146 splitValue = means.Y;
147 else if (splitAxis == 2) 147 else if (splitAxis == 2)
148 splitValue = means.Z; 148 splitValue = means.Z;
149 else 149 else
150 throw new ArgumentException(); 150 throw new ArgumentException();
151 151
152 //sort leafNodes so all values larger then splitValue comes first, and smaller values start from 'splitIndex'. 152 //sort leafNodes so all values larger then splitValue comes first, and smaller values start from 'splitIndex'.
153 for (int i = startIndex; i < endIndex; i++) 153 for (int i = startIndex; i < endIndex; i++)
154 { 154 {
155 Vector3 center = 0.5f * (leafNodes[i].AabbMax + leafNodes[i].AabbMin); 155 Vector3 center = 0.5f * (leafNodes[i].AabbMax + leafNodes[i].AabbMin);
156 float centerSplit; 156 float centerSplit;
157 157
158 if (splitAxis == 0) 158 if (splitAxis == 0)
159 centerSplit = means.X; 159 centerSplit = means.X;
160 else if (splitAxis == 1) 160 else if (splitAxis == 1)
161 centerSplit = means.Y; 161 centerSplit = means.Y;
162 else if (splitAxis == 2) 162 else if (splitAxis == 2)
163 centerSplit = means.Z; 163 centerSplit = means.Z;
164 else 164 else
165 throw new ArgumentException(); 165 throw new ArgumentException();
166 166
167 if (centerSplit > splitValue) 167 if (centerSplit > splitValue)
168 { 168 {
169 //swap 169 //swap
170 OptimizedBvhNode tmp = leafNodes[i]; 170 OptimizedBvhNode tmp = leafNodes[i];
171 leafNodes[i] = leafNodes[splitIndex]; 171 leafNodes[i] = leafNodes[splitIndex];
172 leafNodes[splitIndex] = tmp; 172 leafNodes[splitIndex] = tmp;
173 splitIndex++; 173 splitIndex++;
174 } 174 }
175 } 175 }
176 if ((splitIndex == startIndex) || (splitIndex == (endIndex - 1))) 176 if ((splitIndex == startIndex) || (splitIndex == (endIndex - 1)))
177 { 177 {
178 splitIndex = startIndex + (numIndices >> 1); 178 splitIndex = startIndex + (numIndices >> 1);
179 } 179 }
180 return splitIndex; 180 return splitIndex;
181 } 181 }
182 182
183 public void WalkTree(OptimizedBvhNode rootNode, INodeOverlapCallback nodeCallback, Vector3 aabbMin, Vector3 aabbMax) 183 public void WalkTree(OptimizedBvhNode rootNode, INodeOverlapCallback nodeCallback, Vector3 aabbMin, Vector3 aabbMax)
184 { 184 {
185 bool isLeafNode, aabbOverlap = MathHelper.TestAabbAgainstAabb2(aabbMin, aabbMax, rootNode.AabbMin, rootNode.AabbMax); 185 bool isLeafNode, aabbOverlap = MathHelper.TestAabbAgainstAabb2(aabbMin, aabbMax, rootNode.AabbMin, rootNode.AabbMax);
186 if (aabbOverlap) 186 if (aabbOverlap)
187 { 187 {
188 isLeafNode = (rootNode.LeftChild == null && rootNode.RightChild == null); 188 isLeafNode = (rootNode.LeftChild == null && rootNode.RightChild == null);
189 if (isLeafNode) 189 if (isLeafNode)
190 { 190 {
191 nodeCallback.ProcessNode(rootNode); 191 nodeCallback.ProcessNode(rootNode);
192 } 192 }
193 else 193 else
194 { 194 {
195 WalkTree(rootNode.LeftChild, nodeCallback, aabbMin, aabbMax); 195 WalkTree(rootNode.LeftChild, nodeCallback, aabbMin, aabbMax);
196 WalkTree(rootNode.RightChild, nodeCallback, aabbMin, aabbMax); 196 WalkTree(rootNode.RightChild, nodeCallback, aabbMin, aabbMax);
197 } 197 }
198 } 198 }
199 } 199 }
200 200
201 public void WalkStacklessTree(OptimizedBvhNode[] rootNodeArray, INodeOverlapCallback nodeCallback, Vector3 aabbMin, Vector3 aabbMax) 201 public void WalkStacklessTree(OptimizedBvhNode[] rootNodeArray, INodeOverlapCallback nodeCallback, Vector3 aabbMin, Vector3 aabbMax)
202 { 202 {
203 int escapeIndex, curIndex = 0; 203 int escapeIndex, curIndex = 0;
204 int walkIterations = 0; 204 int walkIterations = 0;
205 bool aabbOverlap, isLeafNode; 205 bool aabbOverlap, isLeafNode;
206 int rootNodeIndex = 0; 206 int rootNodeIndex = 0;
207 OptimizedBvhNode rootNode = rootNodeArray[rootNodeIndex]; 207 OptimizedBvhNode rootNode = rootNodeArray[rootNodeIndex];
208 208
209 while (curIndex < _curNodeIndex) 209 while (curIndex < _curNodeIndex)
210 { 210 {
211 //catch bugs in tree data 211 //catch bugs in tree data
212 if (walkIterations >= _curNodeIndex) 212 if (walkIterations >= _curNodeIndex)
213 throw new BulletException(); 213 throw new BulletException();
214 214
215 walkIterations++; 215 walkIterations++;
216 aabbOverlap = MathHelper.TestAabbAgainstAabb2(aabbMin, aabbMax, rootNode.AabbMin, rootNode.AabbMax); 216 aabbOverlap = MathHelper.TestAabbAgainstAabb2(aabbMin, aabbMax, rootNode.AabbMin, rootNode.AabbMax);
217 isLeafNode = (rootNode.LeftChild == null && rootNode.RightChild == null); 217 isLeafNode = (rootNode.LeftChild == null && rootNode.RightChild == null);
218 218
219 if (isLeafNode && aabbOverlap) 219 if (isLeafNode && aabbOverlap)
220 { 220 {
221 nodeCallback.ProcessNode(rootNode); 221 nodeCallback.ProcessNode(rootNode);
222 } 222 }
223 223
224 if (aabbOverlap || isLeafNode) 224 if (aabbOverlap || isLeafNode)
225 { 225 {
226 rootNodeIndex++; // this 226 rootNodeIndex++; // this
227 curIndex++; 227 curIndex++;
228 if (rootNodeIndex < rootNodeArray.Length) 228 if (rootNodeIndex < rootNodeArray.Length)
229 rootNode = rootNodeArray[rootNodeIndex]; 229 rootNode = rootNodeArray[rootNodeIndex];
230 } 230 }
231 else 231 else
232 { 232 {
233 escapeIndex = rootNode.EscapeIndex; 233 escapeIndex = rootNode.EscapeIndex;
234 rootNodeIndex += escapeIndex; // and this 234 rootNodeIndex += escapeIndex; // and this
235 curIndex += escapeIndex; 235 curIndex += escapeIndex;
236 if (rootNodeIndex < rootNodeArray.Length) 236 if (rootNodeIndex < rootNodeArray.Length)
237 rootNode = rootNodeArray[rootNodeIndex]; 237 rootNode = rootNodeArray[rootNodeIndex];
238 } 238 }
239 239
240 } 240 }
241 241
242 if (_maxIterations < walkIterations) 242 if (_maxIterations < walkIterations)
243 _maxIterations = walkIterations; 243 _maxIterations = walkIterations;
244 } 244 }
245 245
246 public void ReportAabbOverlappingNodex(INodeOverlapCallback nodeCallback, Vector3 aabbMin, Vector3 aabbMax) 246 public void ReportAabbOverlappingNodex(INodeOverlapCallback nodeCallback, Vector3 aabbMin, Vector3 aabbMax)
247 { 247 {
248 //either choose recursive traversal (walkTree) or stackless (walkStacklessTree) 248 //either choose recursive traversal (walkTree) or stackless (walkStacklessTree)
249 //walkTree(m_rootNode1,nodeCallback,aabbMin,aabbMax); 249 //walkTree(m_rootNode1,nodeCallback,aabbMin,aabbMax);
250 //WalkStacklessTree(_rootNode, nodeCallback, aabbMin, aabbMax); 250 //WalkStacklessTree(_rootNode, nodeCallback, aabbMin, aabbMax);
251 WalkStacklessTree(_contiguousNodes, nodeCallback, aabbMin, aabbMax); 251 WalkStacklessTree(_contiguousNodes, nodeCallback, aabbMin, aabbMax);
252 } 252 }
253 253
254 public void ReportSphereOverlappingNodex(INodeOverlapCallback nodeCallback, Vector3 aabbMin, Vector3 aabbMax) { } 254 public void ReportSphereOverlappingNodex(INodeOverlapCallback nodeCallback, Vector3 aabbMin, Vector3 aabbMax) { }
255 } 255 }
256 256
257 public class NodeTriangleCallback : ITriangleIndexCallback 257 public class NodeTriangleCallback : ITriangleIndexCallback
258 { 258 {
259 private List<OptimizedBvhNode> _triangleNodes; 259 private List<OptimizedBvhNode> _triangleNodes;
260 260
261 public NodeTriangleCallback(List<OptimizedBvhNode> triangleNodes) 261 public NodeTriangleCallback(List<OptimizedBvhNode> triangleNodes)
262 { 262 {
263 _triangleNodes = triangleNodes; 263 _triangleNodes = triangleNodes;
264 } 264 }
265 265
266 public List<OptimizedBvhNode> TriangleNodes { get { return _triangleNodes; } set { _triangleNodes = value; } } 266 public List<OptimizedBvhNode> TriangleNodes { get { return _triangleNodes; } set { _triangleNodes = value; } }
267 267
268 public void ProcessTriangleIndex(Vector3[] triangle, int partId, int triangleIndex) 268 public void ProcessTriangleIndex(Vector3[] triangle, int partId, int triangleIndex)
269 { 269 {
270 270
271 OptimizedBvhNode node = new OptimizedBvhNode(); 271 OptimizedBvhNode node = new OptimizedBvhNode();
272 node.AabbMin = new Vector3(1e30f, 1e30f, 1e30f); 272 node.AabbMin = new Vector3(1e30f, 1e30f, 1e30f);
273 node.AabbMax = new Vector3(-1e30f, -1e30f, -1e30f); 273 node.AabbMax = new Vector3(-1e30f, -1e30f, -1e30f);
274 274
275 node.AabbMin = MathHelper.SetMin(node.AabbMin, triangle[0]); 275 node.AabbMin = MathHelper.SetMin(node.AabbMin, triangle[0]);
276 node.AabbMax = MathHelper.SetMax(node.AabbMax, triangle[0]); 276 node.AabbMax = MathHelper.SetMax(node.AabbMax, triangle[0]);
277 node.AabbMin = MathHelper.SetMin(node.AabbMin, triangle[1]); 277 node.AabbMin = MathHelper.SetMin(node.AabbMin, triangle[1]);
278 node.AabbMax = MathHelper.SetMax(node.AabbMax, triangle[1]); 278 node.AabbMax = MathHelper.SetMax(node.AabbMax, triangle[1]);
279 node.AabbMin = MathHelper.SetMin(node.AabbMin, triangle[2]); 279 node.AabbMin = MathHelper.SetMin(node.AabbMin, triangle[2]);
280 node.AabbMax = MathHelper.SetMax(node.AabbMax, triangle[2]); 280 node.AabbMax = MathHelper.SetMax(node.AabbMax, triangle[2]);
281 281
282 node.EscapeIndex = -1; 282 node.EscapeIndex = -1;
283 node.LeftChild = null; 283 node.LeftChild = null;
284 node.RightChild = null; 284 node.RightChild = null;
285 285
286 //for child nodes 286 //for child nodes
287 node.SubPart = partId; 287 node.SubPart = partId;
288 node.TriangleIndex = triangleIndex; 288 node.TriangleIndex = triangleIndex;
289 289
290 _triangleNodes.Add(node); 290 _triangleNodes.Add(node);
291 } 291 }
292 } 292 }
293} 293}