aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/Meshing
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/Physics/Meshing/Extruder.cs21
-rw-r--r--OpenSim/Region/Physics/Meshing/HelperTypes.cs10
-rw-r--r--OpenSim/Region/Physics/Meshing/Mesh.cs42
-rw-r--r--OpenSim/Region/Physics/Meshing/Meshmerizer.cs74
-rw-r--r--OpenSim/Region/Physics/Meshing/SimpleHull.cs120
-rw-r--r--OpenSim/Region/Physics/Meshing/Simplex.cs49
6 files changed, 151 insertions, 165 deletions
diff --git a/OpenSim/Region/Physics/Meshing/Extruder.cs b/OpenSim/Region/Physics/Meshing/Extruder.cs
index eecac5a..7ef5b5b 100644
--- a/OpenSim/Region/Physics/Meshing/Extruder.cs
+++ b/OpenSim/Region/Physics/Meshing/Extruder.cs
@@ -26,24 +26,22 @@
26* 26*
27*/ 27*/
28 28
29using System; 29using OpenSim.Region.Physics.Manager;
30using System.Collections.Generic;
31using System.Text;
32 30
33namespace OpenSim.Region.Physics.Meshing 31namespace OpenSim.Region.Physics.Meshing
34{ 32{
35 class Extruder 33 internal class Extruder
36 { 34 {
37 public float startParameter; 35 public float startParameter;
38 public float stopParameter; 36 public float stopParameter;
39 public Manager.PhysicsVector size; 37 public PhysicsVector size;
40 38
41 public Mesh Extrude(Mesh m) 39 public Mesh Extrude(Mesh m)
42 { 40 {
43 // Currently only works for iSteps=1; 41 // Currently only works for iSteps=1;
44 Mesh result = new Mesh(); 42 Mesh result = new Mesh();
45 43
46 Mesh workingPlus = m.Clone(); 44 Mesh workingPlus = m.Clone();
47 Mesh workingMinus = m.Clone(); 45 Mesh workingMinus = m.Clone();
48 46
49 foreach (Vertex v in workingPlus.vertices) 47 foreach (Vertex v in workingPlus.vertices)
@@ -80,14 +78,14 @@ namespace OpenSim.Region.Physics.Meshing
80 for (int i = 0; i < workingPlus.vertices.Count; i++) 78 for (int i = 0; i < workingPlus.vertices.Count; i++)
81 { 79 {
82 int iNext = (i + 1); 80 int iNext = (i + 1);
83 81
84 if (workingPlus.vertices[i] == null) // Can't make a simplex here 82 if (workingPlus.vertices[i] == null) // Can't make a simplex here
85 { 83 {
86 iLastNull = i+1; 84 iLastNull = i + 1;
87 continue; 85 continue;
88 } 86 }
89 87
90 if (i == workingPlus.vertices.Count-1) // End of list 88 if (i == workingPlus.vertices.Count - 1) // End of list
91 { 89 {
92 iNext = iLastNull; 90 iNext = iLastNull;
93 } 91 }
@@ -101,11 +99,12 @@ namespace OpenSim.Region.Physics.Meshing
101 tSide = new Triangle(workingPlus.vertices[i], workingMinus.vertices[i], workingPlus.vertices[iNext]); 99 tSide = new Triangle(workingPlus.vertices[i], workingMinus.vertices[i], workingPlus.vertices[iNext]);
102 result.Add(tSide); 100 result.Add(tSide);
103 101
104 tSide = new Triangle(workingPlus.vertices[iNext], workingMinus.vertices[i], workingMinus.vertices[iNext]); 102 tSide =
103 new Triangle(workingPlus.vertices[iNext], workingMinus.vertices[i], workingMinus.vertices[iNext]);
105 result.Add(tSide); 104 result.Add(tSide);
106 } 105 }
107 106
108 return result; 107 return result;
109 } 108 }
110 } 109 }
111} 110} \ No newline at end of file
diff --git a/OpenSim/Region/Physics/Meshing/HelperTypes.cs b/OpenSim/Region/Physics/Meshing/HelperTypes.cs
index be82c32..ac06614 100644
--- a/OpenSim/Region/Physics/Meshing/HelperTypes.cs
+++ b/OpenSim/Region/Physics/Meshing/HelperTypes.cs
@@ -30,9 +30,7 @@ using System;
30using System.Collections.Generic; 30using System.Collections.Generic;
31using System.Diagnostics; 31using System.Diagnostics;
32using System.Globalization; 32using System.Globalization;
33using OpenSim.Framework.Console;
34using OpenSim.Region.Physics.Manager; 33using OpenSim.Region.Physics.Manager;
35
36using OpenSim.Region.Physics.Meshing; 34using OpenSim.Region.Physics.Meshing;
37 35
38public class Vertex : PhysicsVector, IComparable<Vertex> 36public class Vertex : PhysicsVector, IComparable<Vertex>
@@ -54,7 +52,7 @@ public class Vertex : PhysicsVector, IComparable<Vertex>
54 52
55 public static Vertex FromAngle(double angle) 53 public static Vertex FromAngle(double angle)
56 { 54 {
57 return new Vertex((float)Math.Cos(angle), (float)Math.Sin(angle), 0.0f); 55 return new Vertex((float) Math.Cos(angle), (float) Math.Sin(angle), 0.0f);
58 } 56 }
59 57
60 58
@@ -101,6 +99,7 @@ public class Vertex : PhysicsVector, IComparable<Vertex>
101 { 99 {
102 return me.CompareTo(other) < 0; 100 return me.CompareTo(other) < 0;
103 } 101 }
102
104 public String ToRaw() 103 public String ToRaw()
105 { 104 {
106 // Why this stuff with the number formatter? 105 // Why this stuff with the number formatter?
@@ -117,7 +116,6 @@ public class Vertex : PhysicsVector, IComparable<Vertex>
117 116
118 return s1; 117 return s1;
119 } 118 }
120
121} 119}
122 120
123public class Triangle 121public class Triangle
@@ -283,7 +281,7 @@ public class Triangle
283 float l = n.length(); 281 float l = n.length();
284 282
285 // Normalized "normal" 283 // Normalized "normal"
286 n = n / l; 284 n = n/l;
287 285
288 return n; 286 return n;
289 } 287 }
@@ -300,7 +298,7 @@ public class Triangle
300 // debugging purposes 298 // debugging purposes
301 public String ToStringRaw() 299 public String ToStringRaw()
302 { 300 {
303 String output = v1.ToRaw() + " " + v2.ToRaw() + " " +v3.ToRaw(); 301 String output = v1.ToRaw() + " " + v2.ToRaw() + " " + v3.ToRaw();
304 return output; 302 return output;
305 } 303 }
306} \ No newline at end of file 304} \ No newline at end of file
diff --git a/OpenSim/Region/Physics/Meshing/Mesh.cs b/OpenSim/Region/Physics/Meshing/Mesh.cs
index 2ebfa1c..9c2667e 100644
--- a/OpenSim/Region/Physics/Meshing/Mesh.cs
+++ b/OpenSim/Region/Physics/Meshing/Mesh.cs
@@ -26,14 +26,11 @@
26* 26*
27*/ 27*/
28 28
29
29using System; 30using System;
30using System.IO;
31using System.Collections.Generic; 31using System.Collections.Generic;
32using System.Text; 32using System.IO;
33
34using System.Runtime.InteropServices; 33using System.Runtime.InteropServices;
35
36
37using OpenSim.Region.Physics.Manager; 34using OpenSim.Region.Physics.Manager;
38 35
39namespace OpenSim.Region.Physics.Meshing 36namespace OpenSim.Region.Physics.Meshing
@@ -66,9 +63,9 @@ namespace OpenSim.Region.Physics.Meshing
66 foreach (Triangle t in triangles) 63 foreach (Triangle t in triangles)
67 { 64 {
68 int iV1, iV2, iV3; 65 int iV1, iV2, iV3;
69 iV1 = this.vertices.IndexOf(t.v1); 66 iV1 = vertices.IndexOf(t.v1);
70 iV2 = this.vertices.IndexOf(t.v2); 67 iV2 = vertices.IndexOf(t.v2);
71 iV3 = this.vertices.IndexOf(t.v3); 68 iV3 = vertices.IndexOf(t.v3);
72 69
73 Triangle newT = new Triangle(result.vertices[iV1], result.vertices[iV2], result.vertices[iV3]); 70 Triangle newT = new Triangle(result.vertices[iV1], result.vertices[iV2], result.vertices[iV3]);
74 result.Add(newT); 71 result.Add(newT);
@@ -77,8 +74,7 @@ namespace OpenSim.Region.Physics.Meshing
77 return result; 74 return result;
78 } 75 }
79 76
80 77
81
82 public void Add(Triangle triangle) 78 public void Add(Triangle triangle)
83 { 79 {
84 int i; 80 int i;
@@ -160,15 +156,15 @@ namespace OpenSim.Region.Physics.Meshing
160 156
161 public float[] getVertexListAsFloatLocked() 157 public float[] getVertexListAsFloatLocked()
162 { 158 {
163 float[] result = new float[vertices.Count * 3]; 159 float[] result = new float[vertices.Count*3];
164 for (int i = 0; i < vertices.Count; i++) 160 for (int i = 0; i < vertices.Count; i++)
165 { 161 {
166 Vertex v = vertices[i]; 162 Vertex v = vertices[i];
167 if (v == null) 163 if (v == null)
168 continue; 164 continue;
169 result[3 * i + 0] = v.X; 165 result[3*i + 0] = v.X;
170 result[3 * i + 1] = v.Y; 166 result[3*i + 1] = v.Y;
171 result[3 * i + 2] = v.Z; 167 result[3*i + 2] = v.Z;
172 } 168 }
173 GCHandle.Alloc(result, GCHandleType.Pinned); 169 GCHandle.Alloc(result, GCHandleType.Pinned);
174 return result; 170 return result;
@@ -176,13 +172,13 @@ namespace OpenSim.Region.Physics.Meshing
176 172
177 public int[] getIndexListAsInt() 173 public int[] getIndexListAsInt()
178 { 174 {
179 int[] result = new int[triangles.Count * 3]; 175 int[] result = new int[triangles.Count*3];
180 for (int i = 0; i < triangles.Count; i++) 176 for (int i = 0; i < triangles.Count; i++)
181 { 177 {
182 Triangle t = triangles[i]; 178 Triangle t = triangles[i];
183 result[3 * i + 0] = vertices.IndexOf(t.v1); 179 result[3*i + 0] = vertices.IndexOf(t.v1);
184 result[3 * i + 1] = vertices.IndexOf(t.v2); 180 result[3*i + 1] = vertices.IndexOf(t.v2);
185 result[3 * i + 2] = vertices.IndexOf(t.v3); 181 result[3*i + 2] = vertices.IndexOf(t.v3);
186 } 182 }
187 return result; 183 return result;
188 } 184 }
@@ -202,7 +198,6 @@ namespace OpenSim.Region.Physics.Meshing
202 198
203 foreach (Triangle t in newMesh.triangles) 199 foreach (Triangle t in newMesh.triangles)
204 Add(t); 200 Add(t);
205
206 } 201 }
207 202
208 // Do a linear transformation of mesh. 203 // Do a linear transformation of mesh.
@@ -213,9 +208,9 @@ namespace OpenSim.Region.Physics.Meshing
213 if (v == null) 208 if (v == null)
214 continue; 209 continue;
215 float x, y, z; 210 float x, y, z;
216 x = v.X * matrix[0, 0] + v.Y * matrix[1, 0] + v.Z * matrix[2, 0]; 211 x = v.X*matrix[0, 0] + v.Y*matrix[1, 0] + v.Z*matrix[2, 0];
217 y = v.X * matrix[0, 1] + v.Y * matrix[1, 1] + v.Z * matrix[2, 1]; 212 y = v.X*matrix[0, 1] + v.Y*matrix[1, 1] + v.Z*matrix[2, 1];
218 z = v.X * matrix[0, 2] + v.Y * matrix[1, 2] + v.Z * matrix[2, 2]; 213 z = v.X*matrix[0, 2] + v.Y*matrix[1, 2] + v.Z*matrix[2, 2];
219 v.X = x + offset[0]; 214 v.X = x + offset[0];
220 v.Y = y + offset[1]; 215 v.Y = y + offset[1];
221 v.Z = z + offset[2]; 216 v.Z = z + offset[2];
@@ -237,5 +232,4 @@ namespace OpenSim.Region.Physics.Meshing
237 sw.Close(); 232 sw.Close();
238 } 233 }
239 } 234 }
240 235} \ No newline at end of file
241}
diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
index 9c35f81..3217dd8 100644
--- a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
+++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
@@ -27,18 +27,13 @@
27*/ 27*/
28 28
29using System; 29using System;
30using System.IO;
31using System.Globalization;
32using System.Diagnostics;
33using System.Collections.Generic; 30using System.Collections.Generic;
34using System.Runtime.InteropServices;
35using OpenSim.Framework; 31using OpenSim.Framework;
36using OpenSim.Framework.Console; 32using OpenSim.Framework.Console;
37using OpenSim.Region.Physics.Manager; 33using OpenSim.Region.Physics.Manager;
38 34
39namespace OpenSim.Region.Physics.Meshing 35namespace OpenSim.Region.Physics.Meshing
40{ 36{
41
42 public class MeshmerizerPlugin : IMeshingPlugin 37 public class MeshmerizerPlugin : IMeshingPlugin
43 { 38 {
44 public MeshmerizerPlugin() 39 public MeshmerizerPlugin()
@@ -61,10 +56,11 @@ namespace OpenSim.Region.Physics.Meshing
61 // Setting baseDir to a path will enable the dumping of raw files 56 // Setting baseDir to a path will enable the dumping of raw files
62 // raw files can be imported by blender so a visual inspection of the results can be done 57 // raw files can be imported by blender so a visual inspection of the results can be done
63 // const string baseDir = "rawFiles"; 58 // const string baseDir = "rawFiles";
64 const string baseDir = null; 59 private const string baseDir = null;
65 60
66 static void IntersectionParameterPD(PhysicsVector p1, PhysicsVector r1, PhysicsVector p2, PhysicsVector r2, ref float lambda, ref float mu) 61 private static void IntersectionParameterPD(PhysicsVector p1, PhysicsVector r1, PhysicsVector p2,
67 { 62 PhysicsVector r2, ref float lambda, ref float mu)
63 {
68 // p1, p2, points on the straight 64 // p1, p2, points on the straight
69 // r1, r2, directional vectors of the straight. Not necessarily of length 1! 65 // r1, r2, directional vectors of the straight. Not necessarily of length 1!
70 // note, that l, m can be scaled such, that the range 0..1 is mapped to the area between two points, 66 // note, that l, m can be scaled such, that the range 0..1 is mapped to the area between two points,
@@ -88,9 +84,8 @@ namespace OpenSim.Region.Physics.Meshing
88 float p1y = p1.Y; 84 float p1y = p1.Y;
89 float p2x = p2.X; 85 float p2x = p2.X;
90 float p2y = p2.Y; 86 float p2y = p2.Y;
91 lambda = (-p2x * r2y + p1x * r2y + (p2y - p1y) * r2x) / denom; 87 lambda = (-p2x*r2y + p1x*r2y + (p2y - p1y)*r2x)/denom;
92 mu = (-p2x * r1y + p1x * r1y + (p2y - p1y) * r1x) / denom; 88 mu = (-p2x*r1y + p1x*r1y + (p2y - p1y)*r1x)/denom;
93
94 } 89 }
95 90
96 private static List<Triangle> FindInfluencedTriangles(List<Triangle> triangles, Vertex v) 91 private static List<Triangle> FindInfluencedTriangles(List<Triangle> triangles, Vertex v)
@@ -105,8 +100,8 @@ namespace OpenSim.Region.Physics.Meshing
105 } 100 }
106 return influenced; 101 return influenced;
107 } 102 }
108 103
109 104
110 private static void InsertVertices(List<Vertex> vertices, int usedForSeed, List<Triangle> triangles) 105 private static void InsertVertices(List<Vertex> vertices, int usedForSeed, List<Triangle> triangles)
111 { 106 {
112 // This is a variant of the delaunay algorithm 107 // This is a variant of the delaunay algorithm
@@ -126,10 +121,10 @@ namespace OpenSim.Region.Physics.Meshing
126 // do not fulfill this condition with respect to the new triangle 121 // do not fulfill this condition with respect to the new triangle
127 122
128 // Find the triangles that are influenced by the new vertex 123 // Find the triangles that are influenced by the new vertex
129 Vertex v=vertices[iCurrentVertex]; 124 Vertex v = vertices[iCurrentVertex];
130 if (v == null) 125 if (v == null)
131 continue; // Null is polygon stop marker. Ignore it 126 continue; // Null is polygon stop marker. Ignore it
132 List<Triangle> influencedTriangles=FindInfluencedTriangles(triangles, v); 127 List<Triangle> influencedTriangles = FindInfluencedTriangles(triangles, v);
133 128
134 List<Simplex> simplices = new List<Simplex>(); 129 List<Simplex> simplices = new List<Simplex>();
135 130
@@ -177,12 +172,11 @@ namespace OpenSim.Region.Physics.Meshing
177 } 172 }
178 } 173 }
179 } 174 }
180
181 } 175 }
182 176
183 177
184 static Mesh CreateBoxMesh(String primName, PrimitiveBaseShape primShape, PhysicsVector size) 178 private static Mesh CreateBoxMesh(String primName, PrimitiveBaseShape primShape, PhysicsVector size)
185 // Builds the z (+ and -) surfaces of a box shaped prim 179 // Builds the z (+ and -) surfaces of a box shaped prim
186 { 180 {
187 UInt16 hollowFactor = primShape.ProfileHollow; 181 UInt16 hollowFactor = primShape.ProfileHollow;
188 UInt16 profileBegin = primShape.ProfileBegin; 182 UInt16 profileBegin = primShape.ProfileBegin;
@@ -201,7 +195,7 @@ namespace OpenSim.Region.Physics.Meshing
201 Vertex MP = new Vertex(-0.5f, +0.5f, 0.0f); 195 Vertex MP = new Vertex(-0.5f, +0.5f, 0.0f);
202 Vertex PP = new Vertex(+0.5f, +0.5f, 0.0f); 196 Vertex PP = new Vertex(+0.5f, +0.5f, 0.0f);
203 197
204 Meshing.SimpleHull outerHull = new SimpleHull(); 198 SimpleHull outerHull = new SimpleHull();
205 outerHull.AddVertex(MM); 199 outerHull.AddVertex(MM);
206 outerHull.AddVertex(PM); 200 outerHull.AddVertex(PM);
207 outerHull.AddVertex(PP); 201 outerHull.AddVertex(PP);
@@ -210,9 +204,10 @@ namespace OpenSim.Region.Physics.Meshing
210 // Deal with cuts now 204 // Deal with cuts now
211 if ((profileBegin != 0) || (profileEnd != 0)) 205 if ((profileBegin != 0) || (profileEnd != 0))
212 { 206 {
213 double fProfileBeginAngle = profileBegin / 50000.0 * 360.0; // In degree, for easier debugging and understanding 207 double fProfileBeginAngle = profileBegin/50000.0*360.0;
214 fProfileBeginAngle -= (90.0 + 45.0); // for some reasons, the SL client counts from the corner -X/-Y 208 // In degree, for easier debugging and understanding
215 double fProfileEndAngle = 360.0 - profileEnd / 50000.0 * 360.0; // Pathend comes as complement to 1.0 209 fProfileBeginAngle -= (90.0 + 45.0); // for some reasons, the SL client counts from the corner -X/-Y
210 double fProfileEndAngle = 360.0 - profileEnd/50000.0*360.0; // Pathend comes as complement to 1.0
216 fProfileEndAngle -= (90.0 + 45.0); 211 fProfileEndAngle -= (90.0 + 45.0);
217 if (fProfileBeginAngle < fProfileEndAngle) 212 if (fProfileBeginAngle < fProfileEndAngle)
218 fProfileEndAngle -= 360.0; 213 fProfileEndAngle -= 360.0;
@@ -222,20 +217,23 @@ namespace OpenSim.Region.Physics.Meshing
222 // and we approximate this arc by a polygon chain 217 // and we approximate this arc by a polygon chain
223 // Also note, that these vectors are of length 1.0 and thus their endpoints lay outside the model space 218 // Also note, that these vectors are of length 1.0 and thus their endpoints lay outside the model space
224 // So it can easily be subtracted from the outer hull 219 // So it can easily be subtracted from the outer hull
225 int iSteps = (int)(((fProfileBeginAngle - fProfileEndAngle) / 45.0) + .5); // how many steps do we need with approximately 45 degree 220 int iSteps = (int) (((fProfileBeginAngle - fProfileEndAngle)/45.0) + .5);
226 double dStepWidth=(fProfileBeginAngle-fProfileEndAngle)/iSteps; 221 // how many steps do we need with approximately 45 degree
222 double dStepWidth = (fProfileBeginAngle - fProfileEndAngle)/iSteps;
227 223
228 Vertex origin = new Vertex(0.0f, 0.0f, 0.0f); 224 Vertex origin = new Vertex(0.0f, 0.0f, 0.0f);
229 225
230 // Note the sequence of vertices here. It's important to have the other rotational sense than in outerHull 226 // Note the sequence of vertices here. It's important to have the other rotational sense than in outerHull
231 SimpleHull cutHull = new SimpleHull(); 227 SimpleHull cutHull = new SimpleHull();
232 cutHull.AddVertex(origin); 228 cutHull.AddVertex(origin);
233 for (int i=0; i<iSteps; i++) { 229 for (int i = 0; i < iSteps; i++)
234 double angle=fProfileBeginAngle-i*dStepWidth; // we count against the angle orientation!!!! 230 {
235 Vertex v = Vertex.FromAngle(angle * Math.PI / 180.0); 231 double angle = fProfileBeginAngle - i*dStepWidth; // we count against the angle orientation!!!!
232 Vertex v = Vertex.FromAngle(angle*Math.PI/180.0);
236 cutHull.AddVertex(v); 233 cutHull.AddVertex(v);
237 } 234 }
238 Vertex legEnd = Vertex.FromAngle(fProfileEndAngle * Math.PI / 180.0); // Calculated separately to avoid errors 235 Vertex legEnd = Vertex.FromAngle(fProfileEndAngle*Math.PI/180.0);
236 // Calculated separately to avoid errors
239 cutHull.AddVertex(legEnd); 237 cutHull.AddVertex(legEnd);
240 238
241 MainLog.Instance.Debug("Starting cutting of the hollow shape from the prim {1}", 0, primName); 239 MainLog.Instance.Debug("Starting cutting of the hollow shape from the prim {1}", 0, primName);
@@ -248,10 +246,10 @@ namespace OpenSim.Region.Physics.Meshing
248 if (hollowFactor > 0) 246 if (hollowFactor > 0)
249 { 247 {
250 float hollowFactorF = (float) hollowFactor/(float) 50000; 248 float hollowFactorF = (float) hollowFactor/(float) 50000;
251 Vertex IMM = new Vertex(-0.5f * hollowFactorF, -0.5f * hollowFactorF, 0.0f); 249 Vertex IMM = new Vertex(-0.5f*hollowFactorF, -0.5f*hollowFactorF, 0.0f);
252 Vertex IPM = new Vertex(+0.5f * hollowFactorF, -0.5f * hollowFactorF, 0.0f); 250 Vertex IPM = new Vertex(+0.5f*hollowFactorF, -0.5f*hollowFactorF, 0.0f);
253 Vertex IMP = new Vertex(-0.5f * hollowFactorF, +0.5f * hollowFactorF, 0.0f); 251 Vertex IMP = new Vertex(-0.5f*hollowFactorF, +0.5f*hollowFactorF, 0.0f);
254 Vertex IPP = new Vertex(+0.5f * hollowFactorF, +0.5f * hollowFactorF, 0.0f); 252 Vertex IPP = new Vertex(+0.5f*hollowFactorF, +0.5f*hollowFactorF, 0.0f);
255 253
256 SimpleHull holeHull = new SimpleHull(); 254 SimpleHull holeHull = new SimpleHull();
257 255
@@ -263,7 +261,6 @@ namespace OpenSim.Region.Physics.Meshing
263 SimpleHull hollowedHull = SimpleHull.SubtractHull(outerHull, holeHull); 261 SimpleHull hollowedHull = SimpleHull.SubtractHull(outerHull, holeHull);
264 262
265 outerHull = hollowedHull; 263 outerHull = hollowedHull;
266
267 } 264 }
268 265
269 Mesh m = new Mesh(); 266 Mesh m = new Mesh();
@@ -286,7 +283,7 @@ namespace OpenSim.Region.Physics.Meshing
286 m.Remove(Seed2); 283 m.Remove(Seed2);
287 m.Remove(Seed3); 284 m.Remove(Seed3);
288 m.DumpRaw(baseDir, primName, "Proto seeds removed"); 285 m.DumpRaw(baseDir, primName, "Proto seeds removed");
289 286
290 m.RemoveTrianglesOutside(outerHull); 287 m.RemoveTrianglesOutside(outerHull);
291 m.DumpRaw(baseDir, primName, "Proto outsides removed"); 288 m.DumpRaw(baseDir, primName, "Proto outsides removed");
292 289
@@ -374,7 +371,7 @@ namespace OpenSim.Region.Physics.Meshing
374 switch (primShape.ProfileShape) 371 switch (primShape.ProfileShape)
375 { 372 {
376 case ProfileShape.Square: 373 case ProfileShape.Square:
377 mesh=CreateBoxMesh(primName, primShape, size); 374 mesh = CreateBoxMesh(primName, primShape, size);
378 CalcNormals(mesh); 375 CalcNormals(mesh);
379 break; 376 break;
380 default: 377 default:
@@ -389,5 +386,4 @@ namespace OpenSim.Region.Physics.Meshing
389 return mesh; 386 return mesh;
390 } 387 }
391 } 388 }
392 389} \ No newline at end of file
393}
diff --git a/OpenSim/Region/Physics/Meshing/SimpleHull.cs b/OpenSim/Region/Physics/Meshing/SimpleHull.cs
index a769053..809f3d5 100644
--- a/OpenSim/Region/Physics/Meshing/SimpleHull.cs
+++ b/OpenSim/Region/Physics/Meshing/SimpleHull.cs
@@ -28,9 +28,8 @@
28 28
29using System; 29using System;
30using System.Collections.Generic; 30using System.Collections.Generic;
31using System.Text;
32
33using OpenSim.Framework.Console; 31using OpenSim.Framework.Console;
32using OpenSim.Region.Physics.Manager;
34 33
35namespace OpenSim.Region.Physics.Meshing 34namespace OpenSim.Region.Physics.Meshing
36{ 35{
@@ -43,17 +42,18 @@ namespace OpenSim.Region.Physics.Meshing
43 // is defined by the hull lies inside or outside the simplex chain 42 // is defined by the hull lies inside or outside the simplex chain
44 public class SimpleHull 43 public class SimpleHull
45 { 44 {
46 List<Vertex> vertices = new List<Vertex>(); 45 private List<Vertex> vertices = new List<Vertex>();
47 List<Vertex> holeVertices = new List<Vertex>(); // Only used, when the hull is hollow 46 private List<Vertex> holeVertices = new List<Vertex>(); // Only used, when the hull is hollow
48 47
49 // Adds a vertex to the end of the list 48 // Adds a vertex to the end of the list
50 public void AddVertex(Vertex v) { 49 public void AddVertex(Vertex v)
50 {
51 vertices.Add(v); 51 vertices.Add(v);
52 } 52 }
53 53
54 override public String ToString() 54 public override String ToString()
55 { 55 {
56 String result=""; 56 String result = "";
57 foreach (Vertex v in vertices) 57 foreach (Vertex v in vertices)
58 { 58 {
59 result += "b:" + v.ToString() + "\n"; 59 result += "b:" + v.ToString() + "\n";
@@ -63,7 +63,8 @@ namespace OpenSim.Region.Physics.Meshing
63 } 63 }
64 64
65 65
66 public List<Vertex> getVertices() { 66 public List<Vertex> getVertices()
67 {
67 List<Vertex> newVertices = new List<Vertex>(); 68 List<Vertex> newVertices = new List<Vertex>();
68 69
69 newVertices.AddRange(vertices); 70 newVertices.AddRange(vertices);
@@ -81,27 +82,27 @@ namespace OpenSim.Region.Physics.Meshing
81 result.AddVertex(v.Clone()); 82 result.AddVertex(v.Clone());
82 } 83 }
83 84
84 foreach (Vertex v in this.holeVertices) 85 foreach (Vertex v in holeVertices)
85 { 86 {
86 result.holeVertices.Add(v.Clone()); 87 result.holeVertices.Add(v.Clone());
87 } 88 }
88 89
89 return result; 90 return result;
90 } 91 }
91 92
92 public bool IsPointIn(Vertex v1) 93 public bool IsPointIn(Vertex v1)
93 { 94 {
94 int iCounter=0; 95 int iCounter = 0;
95 List<Simplex> simplices=buildSimplexList(); 96 List<Simplex> simplices = buildSimplexList();
96 foreach (Simplex s in simplices) 97 foreach (Simplex s in simplices)
97 { 98 {
98 // Send a ray along the positive X-Direction 99 // Send a ray along the positive X-Direction
99 // Note, that this direction must correlate with the "below" interpretation 100 // Note, that this direction must correlate with the "below" interpretation
100 // of handling for the special cases below 101 // of handling for the special cases below
101 Manager.PhysicsVector intersection = s.RayIntersect(v1, new Manager.PhysicsVector(1.0f, 0.0f, 0.0f), true); 102 PhysicsVector intersection = s.RayIntersect(v1, new PhysicsVector(1.0f, 0.0f, 0.0f), true);
102 103
103 if (intersection == null) 104 if (intersection == null)
104 continue; // No intersection. Done. More tests to follow otherwise 105 continue; // No intersection. Done. More tests to follow otherwise
105 106
106 // Did we hit the end of a simplex? 107 // Did we hit the end of a simplex?
107 // Then this can be one of two special cases: 108 // Then this can be one of two special cases:
@@ -111,19 +112,21 @@ namespace OpenSim.Region.Physics.Meshing
111 // Solution: If the other vertex is "below" the ray, we don't count it 112 // Solution: If the other vertex is "below" the ray, we don't count it
112 // Thus corners pointing down are counted twice, corners pointing up are not counted 113 // Thus corners pointing down are counted twice, corners pointing up are not counted
113 // borders are counted once 114 // borders are counted once
114 if (intersection.IsIdentical(s.v1, 0.001f)) { 115 if (intersection.IsIdentical(s.v1, 0.001f))
116 {
115 if (s.v2.Y < v1.Y) 117 if (s.v2.Y < v1.Y)
116 continue; 118 continue;
117 } 119 }
118 // Do this for the other vertex two 120 // Do this for the other vertex two
119 if (intersection.IsIdentical(s.v2, 0.001f)) { 121 if (intersection.IsIdentical(s.v2, 0.001f))
120 if (s.v1.Y<v1.Y) 122 {
123 if (s.v1.Y < v1.Y)
121 continue; 124 continue;
122 } 125 }
123 iCounter++; 126 iCounter++;
124 } 127 }
125 128
126 return iCounter % 2 == 1; // Point is inside if the number of intersections is odd 129 return iCounter%2 == 1; // Point is inside if the number of intersections is odd
127 } 130 }
128 131
129 public bool containsPointsFrom(SimpleHull otherHull) 132 public bool containsPointsFrom(SimpleHull otherHull)
@@ -138,19 +141,20 @@ namespace OpenSim.Region.Physics.Meshing
138 } 141 }
139 142
140 143
141 List<Simplex> buildSimplexList() { 144 private List<Simplex> buildSimplexList()
142 145 {
143 List<Simplex> result = new List<Simplex>(); 146 List<Simplex> result = new List<Simplex>();
144 147
145 // Not asserted but assumed: at least three vertices 148 // Not asserted but assumed: at least three vertices
146 for (int i=0; i<vertices.Count-1; i++) { 149 for (int i = 0; i < vertices.Count - 1; i++)
147 Simplex s=new Simplex(vertices[i], vertices[i+1]); 150 {
151 Simplex s = new Simplex(vertices[i], vertices[i + 1]);
148 result.Add(s); 152 result.Add(s);
149 } 153 }
150 Simplex s1=new Simplex(vertices[vertices.Count-1], vertices[0]); 154 Simplex s1 = new Simplex(vertices[vertices.Count - 1], vertices[0]);
151 result.Add(s1); 155 result.Add(s1);
152 156
153 if (holeVertices.Count==0) 157 if (holeVertices.Count == 0)
154 return result; 158 return result;
155 159
156 // Same here. At least three vertices in hole assumed 160 // Same here. At least three vertices in hole assumed
@@ -159,19 +163,19 @@ namespace OpenSim.Region.Physics.Meshing
159 Simplex s = new Simplex(holeVertices[i], holeVertices[i + 1]); 163 Simplex s = new Simplex(holeVertices[i], holeVertices[i + 1]);
160 result.Add(s); 164 result.Add(s);
161 } 165 }
162 166
163 s1 = new Simplex(holeVertices[holeVertices.Count - 1], holeVertices[0]); 167 s1 = new Simplex(holeVertices[holeVertices.Count - 1], holeVertices[0]);
164 result.Add(s1); 168 result.Add(s1);
165 return result; 169 return result;
166 } 170 }
167 171
168 bool InsertVertex(Vertex v, int iAfter) 172 private bool InsertVertex(Vertex v, int iAfter)
169 { 173 {
170 vertices.Insert(iAfter + 1, v); 174 vertices.Insert(iAfter + 1, v);
171 return true; 175 return true;
172 } 176 }
173 177
174 Vertex getNextVertex(Vertex currentVertex) 178 private Vertex getNextVertex(Vertex currentVertex)
175 { 179 {
176 int iCurrentIndex; 180 int iCurrentIndex;
177 iCurrentIndex = vertices.IndexOf(currentVertex); 181 iCurrentIndex = vertices.IndexOf(currentVertex);
@@ -185,8 +189,10 @@ namespace OpenSim.Region.Physics.Meshing
185 return vertices[iCurrentIndex]; 189 return vertices[iCurrentIndex];
186 } 190 }
187 191
188 public Vertex FindVertex(Vertex vBase, float tolerance) { 192 public Vertex FindVertex(Vertex vBase, float tolerance)
189 foreach (Vertex v in vertices) { 193 {
194 foreach (Vertex v in vertices)
195 {
190 if (v.IsIdentical(vBase, tolerance)) 196 if (v.IsIdentical(vBase, tolerance))
191 return v; 197 return v;
192 } 198 }
@@ -196,32 +202,31 @@ namespace OpenSim.Region.Physics.Meshing
196 202
197 public void FindIntersection(Simplex s, ref Vertex Intersection, ref Vertex nextVertex) 203 public void FindIntersection(Simplex s, ref Vertex Intersection, ref Vertex nextVertex)
198 { 204 {
199 Vertex bestIntersection=null; 205 Vertex bestIntersection = null;
200 float distToV1=Single.PositiveInfinity; 206 float distToV1 = Single.PositiveInfinity;
201 Simplex bestIntersectingSimplex=null; 207 Simplex bestIntersectingSimplex = null;
202 208
203 List<Simplex> simple = buildSimplexList(); 209 List<Simplex> simple = buildSimplexList();
204 foreach (Simplex sTest in simple) 210 foreach (Simplex sTest in simple)
205 { 211 {
206 Manager.PhysicsVector vvTemp = Simplex.Intersect(sTest, s, -.001f, -.001f, 0.999f, .999f); 212 PhysicsVector vvTemp = Simplex.Intersect(sTest, s, -.001f, -.001f, 0.999f, .999f);
207 213
208 Vertex vTemp=null; 214 Vertex vTemp = null;
209 if (vvTemp != null) 215 if (vvTemp != null)
210 vTemp = new Vertex(vvTemp); 216 vTemp = new Vertex(vvTemp);
211 217
212 if (vTemp!=null) { 218 if (vTemp != null)
213 219 {
214 Manager.PhysicsVector diff=(s.v1-vTemp); 220 PhysicsVector diff = (s.v1 - vTemp);
215 float distTemp=diff.length(); 221 float distTemp = diff.length();
216 222
217 if (bestIntersection==null || distTemp<distToV1) { 223 if (bestIntersection == null || distTemp < distToV1)
218 bestIntersection=vTemp; 224 {
219 distToV1=distTemp; 225 bestIntersection = vTemp;
226 distToV1 = distTemp;
220 bestIntersectingSimplex = sTest; 227 bestIntersectingSimplex = sTest;
221 } 228 }
222
223 } // end if vTemp 229 } // end if vTemp
224
225 } // end foreach 230 } // end foreach
226 231
227 Intersection = bestIntersection; 232 Intersection = bestIntersection;
@@ -234,7 +239,6 @@ namespace OpenSim.Region.Physics.Meshing
234 239
235 public static SimpleHull SubtractHull(SimpleHull baseHull, SimpleHull otherHull) 240 public static SimpleHull SubtractHull(SimpleHull baseHull, SimpleHull otherHull)
236 { 241 {
237
238 SimpleHull baseHullClone = baseHull.Clone(); 242 SimpleHull baseHullClone = baseHull.Clone();
239 SimpleHull otherHullClone = otherHull.Clone(); 243 SimpleHull otherHullClone = otherHull.Clone();
240 bool intersects = false; 244 bool intersects = false;
@@ -249,15 +253,16 @@ namespace OpenSim.Region.Physics.Meshing
249 // Insert into baseHull 253 // Insert into baseHull
250 for (iBase = 0; iBase < baseHullClone.vertices.Count; iBase++) 254 for (iBase = 0; iBase < baseHullClone.vertices.Count; iBase++)
251 { 255 {
252 int iBaseNext = (iBase + 1) % baseHullClone.vertices.Count; 256 int iBaseNext = (iBase + 1)%baseHullClone.vertices.Count;
253 Simplex sBase = new Simplex(baseHullClone.vertices[iBase], baseHullClone.vertices[iBaseNext]); 257 Simplex sBase = new Simplex(baseHullClone.vertices[iBase], baseHullClone.vertices[iBaseNext]);
254 258
255 for (iOther = 0; iOther < otherHullClone.vertices.Count; iOther++) 259 for (iOther = 0; iOther < otherHullClone.vertices.Count; iOther++)
256 { 260 {
257 int iOtherNext = (iOther + 1) % otherHullClone.vertices.Count; 261 int iOtherNext = (iOther + 1)%otherHullClone.vertices.Count;
258 Simplex sOther = new Simplex(otherHullClone.vertices[iOther], otherHullClone.vertices[iOtherNext]); 262 Simplex sOther =
263 new Simplex(otherHullClone.vertices[iOther], otherHullClone.vertices[iOtherNext]);
259 264
260 Manager.PhysicsVector intersect = Simplex.Intersect(sBase, sOther, 0.001f, -.001f, 0.999f, 1.001f); 265 PhysicsVector intersect = Simplex.Intersect(sBase, sOther, 0.001f, -.001f, 0.999f, 1.001f);
261 if (intersect != null) 266 if (intersect != null)
262 { 267 {
263 Vertex vIntersect = new Vertex(intersect); 268 Vertex vIntersect = new Vertex(intersect);
@@ -278,15 +283,15 @@ namespace OpenSim.Region.Physics.Meshing
278 // Insert into otherHull 283 // Insert into otherHull
279 for (iOther = 0; iOther < otherHullClone.vertices.Count; iOther++) 284 for (iOther = 0; iOther < otherHullClone.vertices.Count; iOther++)
280 { 285 {
281 int iOtherNext = (iOther + 1) % otherHullClone.vertices.Count; 286 int iOtherNext = (iOther + 1)%otherHullClone.vertices.Count;
282 Simplex sOther = new Simplex(otherHullClone.vertices[iOther], otherHullClone.vertices[iOtherNext]); 287 Simplex sOther = new Simplex(otherHullClone.vertices[iOther], otherHullClone.vertices[iOtherNext]);
283 288
284 for (iBase = 0; iBase < baseHullClone.vertices.Count; iBase++) 289 for (iBase = 0; iBase < baseHullClone.vertices.Count; iBase++)
285 { 290 {
286 int iBaseNext = (iBase + 1) % baseHullClone.vertices.Count; 291 int iBaseNext = (iBase + 1)%baseHullClone.vertices.Count;
287 Simplex sBase = new Simplex(baseHullClone.vertices[iBase], baseHullClone.vertices[iBaseNext]); 292 Simplex sBase = new Simplex(baseHullClone.vertices[iBase], baseHullClone.vertices[iBaseNext]);
288 293
289 Manager.PhysicsVector intersect = Simplex.Intersect(sBase, sOther, -.001f, 0.001f, 1.001f, 0.999f); 294 PhysicsVector intersect = Simplex.Intersect(sBase, sOther, -.001f, 0.001f, 1.001f, 0.999f);
290 if (intersect != null) 295 if (intersect != null)
291 { 296 {
292 Vertex vIntersect = new Vertex(intersect); 297 Vertex vIntersect = new Vertex(intersect);
@@ -321,8 +326,8 @@ namespace OpenSim.Region.Physics.Meshing
321 int iBase; 326 int iBase;
322 for (iBase = 0; iBase < baseHullClone.vertices.Count; iBase++) 327 for (iBase = 0; iBase < baseHullClone.vertices.Count; iBase++)
323 { 328 {
324 int iBaseNext = (iBase + 1) % baseHullClone.vertices.Count; 329 int iBaseNext = (iBase + 1)%baseHullClone.vertices.Count;
325 Vertex center = new Vertex((baseHullClone.vertices[iBase] + baseHullClone.vertices[iBaseNext]) / 2.0f); 330 Vertex center = new Vertex((baseHullClone.vertices[iBase] + baseHullClone.vertices[iBaseNext])/2.0f);
326 bool isOutside = !otherHullClone.IsPointIn(center); 331 bool isOutside = !otherHullClone.IsPointIn(center);
327 if (isOutside) 332 if (isOutside)
328 { 333 {
@@ -334,7 +339,7 @@ namespace OpenSim.Region.Physics.Meshing
334 339
335 340
336 if (baseStartVertex == null) // i.e. no simplex fulfilled the "outside" condition. 341 if (baseStartVertex == null) // i.e. no simplex fulfilled the "outside" condition.
337 // In otherwords, subtractHull completely embraces baseHull 342 // In otherwords, subtractHull completely embraces baseHull
338 { 343 {
339 return result; 344 return result;
340 } 345 }
@@ -369,7 +374,7 @@ namespace OpenSim.Region.Physics.Meshing
369 374
370 if (nextVertex != null) // A node that represents an intersection 375 if (nextVertex != null) // A node that represents an intersection
371 { 376 {
372 V1 = nextVertex; // Needed to find the next vertex on the other hull 377 V1 = nextVertex; // Needed to find the next vertex on the other hull
373 onBase = !onBase; 378 onBase = !onBase;
374 } 379 }
375 380
@@ -385,7 +390,6 @@ namespace OpenSim.Region.Physics.Meshing
385 MainLog.Instance.Debug("The resulting Hull is:\n{1}", 0, result.ToString()); 390 MainLog.Instance.Debug("The resulting Hull is:\n{1}", 0, result.ToString());
386 391
387 return result; 392 return result;
388
389 } 393 }
390 } 394 }
391} 395} \ No newline at end of file
diff --git a/OpenSim/Region/Physics/Meshing/Simplex.cs b/OpenSim/Region/Physics/Meshing/Simplex.cs
index 98e693a..8fba2bf 100644
--- a/OpenSim/Region/Physics/Meshing/Simplex.cs
+++ b/OpenSim/Region/Physics/Meshing/Simplex.cs
@@ -27,8 +27,6 @@
27*/ 27*/
28 28
29using System; 29using System;
30using System.Collections.Generic;
31using System.Text;
32using OpenSim.Region.Physics.Manager; 30using OpenSim.Region.Physics.Manager;
33 31
34namespace OpenSim.Region.Physics.Meshing 32namespace OpenSim.Region.Physics.Meshing
@@ -49,7 +47,6 @@ namespace OpenSim.Region.Physics.Meshing
49 47
50 public int CompareTo(Simplex other) 48 public int CompareTo(Simplex other)
51 { 49 {
52
53 Vertex lv1, lv2, ov1, ov2, temp; 50 Vertex lv1, lv2, ov1, ov2, temp;
54 51
55 lv1 = v1; 52 lv1 = v1;
@@ -92,8 +89,9 @@ namespace OpenSim.Region.Physics.Meshing
92 return 0; 89 return 0;
93 } 90 }
94 91
95 private static void intersectParameter(PhysicsVector p1, PhysicsVector r1, PhysicsVector p2, PhysicsVector r2, ref float lambda, ref float mu) 92 private static void intersectParameter(PhysicsVector p1, PhysicsVector r1, PhysicsVector p2, PhysicsVector r2,
96 { 93 ref float lambda, ref float mu)
94 {
97 // Intersects two straights 95 // Intersects two straights
98 // p1, p2, points on the straight 96 // p1, p2, points on the straight
99 // r1, r2, directional vectors of the straight. Not necessarily of length 1! 97 // r1, r2, directional vectors of the straight. Not necessarily of length 1!
@@ -112,27 +110,28 @@ namespace OpenSim.Region.Physics.Meshing
112 float p2x = p2.X; 110 float p2x = p2.X;
113 float p2y = p2.Y; 111 float p2y = p2.Y;
114 112
115 float z1=-p2x * r2y + p1x * r2y + (p2y - p1y) * r2x; 113 float z1 = -p2x*r2y + p1x*r2y + (p2y - p1y)*r2x;
116 float z2=-p2x * r1y + p1x * r1y + (p2y - p1y) * r1x; 114 float z2 = -p2x*r1y + p1x*r1y + (p2y - p1y)*r1x;
117 115
118 if (denom == 0.0f) // Means the straights are parallel. Either no intersection or an infinite number of them 116 if (denom == 0.0f) // Means the straights are parallel. Either no intersection or an infinite number of them
119 { 117 {
120 if (z1==0.0f) {// Means they are identical -> many, many intersections 118 if (z1 == 0.0f)
119 {
120// Means they are identical -> many, many intersections
121 lambda = Single.NaN; 121 lambda = Single.NaN;
122 mu = Single.NaN; 122 mu = Single.NaN;
123 } else { 123 }
124 else
125 {
124 lambda = Single.PositiveInfinity; 126 lambda = Single.PositiveInfinity;
125 mu = Single.PositiveInfinity; 127 mu = Single.PositiveInfinity;
126 } 128 }
127 return; 129 return;
128
129 } 130 }
130 131
131 132
132 133 lambda = z1/denom;
133 lambda = z1 / denom; 134 mu = z2/denom;
134 mu = z2 / denom;
135
136 } 135 }
137 136
138 137
@@ -145,12 +144,12 @@ namespace OpenSim.Region.Physics.Meshing
145 // upperBorder2 : 1.0 144 // upperBorder2 : 1.0
146 // Set these to values near the given parameters (e.g. 0.001 instead of 1 to exclude simplex starts safely, or to -0.001 to include them safely) 145 // Set these to values near the given parameters (e.g. 0.001 instead of 1 to exclude simplex starts safely, or to -0.001 to include them safely)
147 public static PhysicsVector Intersect( 146 public static PhysicsVector Intersect(
148 Simplex s1, 147 Simplex s1,
149 Simplex s2, 148 Simplex s2,
150 float lowerBorder1, 149 float lowerBorder1,
151 float lowerBorder2, 150 float lowerBorder2,
152 float upperBorder1, 151 float upperBorder1,
153 float upperBorder2) 152 float upperBorder2)
154 { 153 {
155 PhysicsVector firstSimplexDirection = s1.v2 - s1.v1; 154 PhysicsVector firstSimplexDirection = s1.v2 - s1.v1;
156 PhysicsVector secondSimplexDirection = s2.v2 - s2.v1; 155 PhysicsVector secondSimplexDirection = s2.v2 - s2.v1;
@@ -181,8 +180,7 @@ namespace OpenSim.Region.Physics.Meshing
181 if (mu > upperBorder2) // outside simplex 2 180 if (mu > upperBorder2) // outside simplex 2
182 return null; 181 return null;
183 182
184 return s1.v1 + lambda * firstSimplexDirection; 183 return s1.v1 + lambda*firstSimplexDirection;
185
186 } 184 }
187 185
188 // Intersects the simplex with a ray. The ray is defined as all p=origin + lambda*direction 186 // Intersects the simplex with a ray. The ray is defined as all p=origin + lambda*direction
@@ -212,15 +210,12 @@ namespace OpenSim.Region.Physics.Meshing
212 return null; 210 return null;
213 211
214 if (lambda == 1.0 && !bEndsIncluded) 212 if (lambda == 1.0 && !bEndsIncluded)
215 return null; // The end of the simplices are not included 213 return null; // The end of the simplices are not included
216 214
217 if (lambda < 0.0f) // we're before v1; 215 if (lambda < 0.0f) // we're before v1;
218 return null; 216 return null;
219 217
220 return this.v1 + lambda * simplexDirection; 218 return v1 + lambda*simplexDirection;
221
222 } 219 }
223
224
225 } 220 }
226} \ No newline at end of file 221} \ No newline at end of file