aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/PhysicsModules/ubOdeMeshing
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/PhysicsModules/ubOdeMeshing')
-rw-r--r--OpenSim/Region/PhysicsModules/ubOdeMeshing/HelperTypes.cs340
-rw-r--r--OpenSim/Region/PhysicsModules/ubOdeMeshing/Mesh.cs633
-rw-r--r--OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs1465
-rw-r--r--OpenSim/Region/PhysicsModules/ubOdeMeshing/PrimMesher.cs1708
-rw-r--r--OpenSim/Region/PhysicsModules/ubOdeMeshing/Properties/AssemblyInfo.cs36
-rw-r--r--OpenSim/Region/PhysicsModules/ubOdeMeshing/SculptMap.cs244
-rw-r--r--OpenSim/Region/PhysicsModules/ubOdeMeshing/SculptMesh.cs220
7 files changed, 4646 insertions, 0 deletions
diff --git a/OpenSim/Region/PhysicsModules/ubOdeMeshing/HelperTypes.cs b/OpenSim/Region/PhysicsModules/ubOdeMeshing/HelperTypes.cs
new file mode 100644
index 0000000..ea37301
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/ubOdeMeshing/HelperTypes.cs
@@ -0,0 +1,340 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Diagnostics;
31using System.Globalization;
32using OpenMetaverse;
33using OpenSim.Region.PhysicsModules.SharedBase;
34using OpenSim.Region.PhysicsModule.ubODEMeshing;
35
36public class Vertex : IComparable<Vertex>
37{
38 Vector3 vector;
39
40 public float X
41 {
42 get { return vector.X; }
43 set { vector.X = value; }
44 }
45
46 public float Y
47 {
48 get { return vector.Y; }
49 set { vector.Y = value; }
50 }
51
52 public float Z
53 {
54 get { return vector.Z; }
55 set { vector.Z = value; }
56 }
57
58 public Vertex(float x, float y, float z)
59 {
60 vector.X = x;
61 vector.Y = y;
62 vector.Z = z;
63 }
64
65 public Vertex normalize()
66 {
67 float tlength = vector.Length();
68 if (tlength != 0f)
69 {
70 float mul = 1.0f / tlength;
71 return new Vertex(vector.X * mul, vector.Y * mul, vector.Z * mul);
72 }
73 else
74 {
75 return new Vertex(0f, 0f, 0f);
76 }
77 }
78
79 public Vertex cross(Vertex v)
80 {
81 return new Vertex(vector.Y * v.Z - vector.Z * v.Y, vector.Z * v.X - vector.X * v.Z, vector.X * v.Y - vector.Y * v.X);
82 }
83
84 // disable warning: mono compiler moans about overloading
85 // operators hiding base operator but should not according to C#
86 // language spec
87#pragma warning disable 0108
88 public static Vertex operator *(Vertex v, Quaternion q)
89 {
90 // From http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/
91
92 Vertex v2 = new Vertex(0f, 0f, 0f);
93
94 v2.X = q.W * q.W * v.X +
95 2f * q.Y * q.W * v.Z -
96 2f * q.Z * q.W * v.Y +
97 q.X * q.X * v.X +
98 2f * q.Y * q.X * v.Y +
99 2f * q.Z * q.X * v.Z -
100 q.Z * q.Z * v.X -
101 q.Y * q.Y * v.X;
102
103 v2.Y =
104 2f * q.X * q.Y * v.X +
105 q.Y * q.Y * v.Y +
106 2f * q.Z * q.Y * v.Z +
107 2f * q.W * q.Z * v.X -
108 q.Z * q.Z * v.Y +
109 q.W * q.W * v.Y -
110 2f * q.X * q.W * v.Z -
111 q.X * q.X * v.Y;
112
113 v2.Z =
114 2f * q.X * q.Z * v.X +
115 2f * q.Y * q.Z * v.Y +
116 q.Z * q.Z * v.Z -
117 2f * q.W * q.Y * v.X -
118 q.Y * q.Y * v.Z +
119 2f * q.W * q.X * v.Y -
120 q.X * q.X * v.Z +
121 q.W * q.W * v.Z;
122
123 return v2;
124 }
125
126 public static Vertex operator +(Vertex v1, Vertex v2)
127 {
128 return new Vertex(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
129 }
130
131 public static Vertex operator -(Vertex v1, Vertex v2)
132 {
133 return new Vertex(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
134 }
135
136 public static Vertex operator *(Vertex v1, Vertex v2)
137 {
138 return new Vertex(v1.X * v2.X, v1.Y * v2.Y, v1.Z * v2.Z);
139 }
140
141 public static Vertex operator +(Vertex v1, float am)
142 {
143 v1.X += am;
144 v1.Y += am;
145 v1.Z += am;
146 return v1;
147 }
148
149 public static Vertex operator -(Vertex v1, float am)
150 {
151 v1.X -= am;
152 v1.Y -= am;
153 v1.Z -= am;
154 return v1;
155 }
156
157 public static Vertex operator *(Vertex v1, float am)
158 {
159 v1.X *= am;
160 v1.Y *= am;
161 v1.Z *= am;
162 return v1;
163 }
164
165 public static Vertex operator /(Vertex v1, float am)
166 {
167 if (am == 0f)
168 {
169 return new Vertex(0f,0f,0f);
170 }
171 float mul = 1.0f / am;
172 v1.X *= mul;
173 v1.Y *= mul;
174 v1.Z *= mul;
175 return v1;
176 }
177#pragma warning restore 0108
178
179
180 public float dot(Vertex v)
181 {
182 return X * v.X + Y * v.Y + Z * v.Z;
183 }
184
185 public Vertex(Vector3 v)
186 {
187 vector = v;
188 }
189
190 public Vertex Clone()
191 {
192 return new Vertex(X, Y, Z);
193 }
194
195 public static Vertex FromAngle(double angle)
196 {
197 return new Vertex((float) Math.Cos(angle), (float) Math.Sin(angle), 0.0f);
198 }
199
200 public float Length()
201 {
202 return vector.Length();
203 }
204
205 public virtual bool Equals(Vertex v, float tolerance)
206 {
207 Vertex diff = this - v;
208 float d = diff.Length();
209 if (d < tolerance)
210 return true;
211
212 return false;
213 }
214
215
216 public int CompareTo(Vertex other)
217 {
218 if (X < other.X)
219 return -1;
220
221 if (X > other.X)
222 return 1;
223
224 if (Y < other.Y)
225 return -1;
226
227 if (Y > other.Y)
228 return 1;
229
230 if (Z < other.Z)
231 return -1;
232
233 if (Z > other.Z)
234 return 1;
235
236 return 0;
237 }
238
239 public static bool operator >(Vertex me, Vertex other)
240 {
241 return me.CompareTo(other) > 0;
242 }
243
244 public static bool operator <(Vertex me, Vertex other)
245 {
246 return me.CompareTo(other) < 0;
247 }
248
249 public String ToRaw()
250 {
251 // Why this stuff with the number formatter?
252 // Well, the raw format uses the english/US notation of numbers
253 // where the "," separates groups of 1000 while the "." marks the border between 1 and 10E-1.
254 // The german notation uses these characters exactly vice versa!
255 // The Float.ToString() routine is a localized one, giving different results depending on the country
256 // settings your machine works with. Unusable for a machine readable file format :-(
257 NumberFormatInfo nfi = new NumberFormatInfo();
258 nfi.NumberDecimalSeparator = ".";
259 nfi.NumberDecimalDigits = 6;
260
261 String s1 = X.ToString(nfi) + " " + Y.ToString(nfi) + " " + Z.ToString(nfi);
262
263 return s1;
264 }
265}
266
267public class Triangle
268{
269 public Vertex v1;
270 public Vertex v2;
271 public Vertex v3;
272
273 public Triangle(Vertex _v1, Vertex _v2, Vertex _v3)
274 {
275 v1 = _v1;
276 v2 = _v2;
277 v3 = _v3;
278 }
279
280 public Triangle(float _v1x,float _v1y,float _v1z,
281 float _v2x,float _v2y,float _v2z,
282 float _v3x,float _v3y,float _v3z)
283 {
284 v1 = new Vertex(_v1x, _v1y, _v1z);
285 v2 = new Vertex(_v2x, _v2y, _v2z);
286 v3 = new Vertex(_v3x, _v3y, _v3z);
287 }
288
289 public override String ToString()
290 {
291 NumberFormatInfo nfi = new NumberFormatInfo();
292 nfi.CurrencyDecimalDigits = 2;
293 nfi.CurrencyDecimalSeparator = ".";
294
295 String s1 = "<" + v1.X.ToString(nfi) + "," + v1.Y.ToString(nfi) + "," + v1.Z.ToString(nfi) + ">";
296 String s2 = "<" + v2.X.ToString(nfi) + "," + v2.Y.ToString(nfi) + "," + v2.Z.ToString(nfi) + ">";
297 String s3 = "<" + v3.X.ToString(nfi) + "," + v3.Y.ToString(nfi) + "," + v3.Z.ToString(nfi) + ">";
298
299 return s1 + ";" + s2 + ";" + s3;
300 }
301
302 public Vector3 getNormal()
303 {
304 // Vertices
305
306 // Vectors for edges
307 Vector3 e1;
308 Vector3 e2;
309
310 e1 = new Vector3(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
311 e2 = new Vector3(v1.X - v3.X, v1.Y - v3.Y, v1.Z - v3.Z);
312
313 // Cross product for normal
314 Vector3 n = Vector3.Cross(e1, e2);
315
316 // Length
317 float l = n.Length();
318
319 // Normalized "normal"
320 n = n/l;
321
322 return n;
323 }
324
325 public void invertNormal()
326 {
327 Vertex vt;
328 vt = v1;
329 v1 = v2;
330 v2 = vt;
331 }
332
333 // Dumps a triangle in the "raw faces" format, blender can import. This is for visualisation and
334 // debugging purposes
335 public String ToStringRaw()
336 {
337 String output = v1.ToRaw() + " " + v2.ToRaw() + " " + v3.ToRaw();
338 return output;
339 }
340}
diff --git a/OpenSim/Region/PhysicsModules/ubOdeMeshing/Mesh.cs b/OpenSim/Region/PhysicsModules/ubOdeMeshing/Mesh.cs
new file mode 100644
index 0000000..97501a4
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/ubOdeMeshing/Mesh.cs
@@ -0,0 +1,633 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Runtime.InteropServices;
32using OpenSim.Region.PhysicsModules.SharedBase;
33using PrimMesher;
34using OpenMetaverse;
35using System.Runtime.Serialization;
36using System.Runtime.Serialization.Formatters.Binary;
37
38namespace OpenSim.Region.PhysicsModule.ubODEMeshing
39{
40 public class MeshBuildingData
41 {
42 public Dictionary<Vertex, int> m_vertices;
43 public List<Triangle> m_triangles;
44 public float m_obbXmin;
45 public float m_obbXmax;
46 public float m_obbYmin;
47 public float m_obbYmax;
48 public float m_obbZmin;
49 public float m_obbZmax;
50 public Vector3 m_centroid;
51 public int m_centroidDiv;
52 }
53
54 [Serializable()]
55 public class Mesh : IMesh
56 {
57 float[] vertices;
58 int[] indexes;
59 Vector3 m_obb;
60 Vector3 m_obboffset;
61 [NonSerialized()]
62 MeshBuildingData m_bdata;
63 [NonSerialized()]
64 GCHandle vhandler;
65 [NonSerialized()]
66 GCHandle ihandler;
67 [NonSerialized()]
68 IntPtr m_verticesPtr = IntPtr.Zero;
69 [NonSerialized()]
70 IntPtr m_indicesPtr = IntPtr.Zero;
71 [NonSerialized()]
72 int m_vertexCount = 0;
73 [NonSerialized()]
74 int m_indexCount = 0;
75
76 public int RefCount { get; set; }
77 public AMeshKey Key { get; set; }
78
79 private class vertexcomp : IEqualityComparer<Vertex>
80 {
81 public bool Equals(Vertex v1, Vertex v2)
82 {
83 if (v1.X == v2.X && v1.Y == v2.Y && v1.Z == v2.Z)
84 return true;
85 else
86 return false;
87 }
88 public int GetHashCode(Vertex v)
89 {
90 int a = v.X.GetHashCode();
91 int b = v.Y.GetHashCode();
92 int c = v.Z.GetHashCode();
93 return (a << 16) ^ (b << 8) ^ c;
94 }
95 }
96
97 public Mesh()
98 {
99 vertexcomp vcomp = new vertexcomp();
100
101 m_bdata = new MeshBuildingData();
102 m_bdata.m_vertices = new Dictionary<Vertex, int>(vcomp);
103 m_bdata.m_triangles = new List<Triangle>();
104 m_bdata.m_centroid = Vector3.Zero;
105 m_bdata.m_centroidDiv = 0;
106 m_bdata.m_obbXmin = float.MaxValue;
107 m_bdata.m_obbXmax = float.MinValue;
108 m_bdata.m_obbYmin = float.MaxValue;
109 m_bdata.m_obbYmax = float.MinValue;
110 m_bdata.m_obbZmin = float.MaxValue;
111 m_bdata.m_obbZmax = float.MinValue;
112 m_obb = new Vector3(0.5f, 0.5f, 0.5f);
113 m_obboffset = Vector3.Zero;
114 }
115
116
117 public Mesh Scale(Vector3 scale)
118 {
119 if (m_verticesPtr == null || m_indicesPtr == null)
120 return null;
121
122 Mesh result = new Mesh();
123
124 float x = scale.X;
125 float y = scale.Y;
126 float z = scale.Z;
127
128 float tmp;
129 tmp = m_obb.X * x;
130 if(tmp < 0.0005f)
131 tmp = 0.0005f;
132 result.m_obb.X = tmp;
133
134 tmp = m_obb.Y * y;
135 if(tmp < 0.0005f)
136 tmp = 0.0005f;
137 result.m_obb.Y = tmp;
138
139 tmp = m_obb.Z * z;
140 if(tmp < 0.0005f)
141 tmp = 0.0005f;
142 result.m_obb.Z = tmp;
143
144 result.m_obboffset.X = m_obboffset.X * x;
145 result.m_obboffset.Y = m_obboffset.Y * y;
146 result.m_obboffset.Z = m_obboffset.Z * z;
147
148 result.vertices = new float[vertices.Length];
149 int j = 0;
150 for (int i = 0; i < m_vertexCount; i++)
151 {
152 result.vertices[j] = vertices[j] * x;
153 j++;
154 result.vertices[j] = vertices[j] * y;
155 j++;
156 result.vertices[j] = vertices[j] * z;
157 j++;
158 }
159
160 result.indexes = new int[indexes.Length];
161 indexes.CopyTo(result.indexes,0);
162
163 result.pinMemory();
164
165 return result;
166 }
167
168 public Mesh Clone()
169 {
170 Mesh result = new Mesh();
171
172 if (m_bdata != null)
173 {
174 result.m_bdata = new MeshBuildingData();
175 foreach (Triangle t in m_bdata.m_triangles)
176 {
177 result.Add(new Triangle(t.v1.Clone(), t.v2.Clone(), t.v3.Clone()));
178 }
179 result.m_bdata.m_centroid = m_bdata.m_centroid;
180 result.m_bdata.m_centroidDiv = m_bdata.m_centroidDiv;
181 result.m_bdata.m_obbXmin = m_bdata.m_obbXmin;
182 result.m_bdata.m_obbXmax = m_bdata.m_obbXmax;
183 result.m_bdata.m_obbYmin = m_bdata.m_obbYmin;
184 result.m_bdata.m_obbYmax = m_bdata.m_obbYmax;
185 result.m_bdata.m_obbZmin = m_bdata.m_obbZmin;
186 result.m_bdata.m_obbZmax = m_bdata.m_obbZmax;
187 }
188 result.m_obb = m_obb;
189 result.m_obboffset = m_obboffset;
190 return result;
191 }
192
193 public void addVertexLStats(Vertex v)
194 {
195 float x = v.X;
196 float y = v.Y;
197 float z = v.Z;
198
199 m_bdata.m_centroid.X += x;
200 m_bdata.m_centroid.Y += y;
201 m_bdata.m_centroid.Z += z;
202 m_bdata.m_centroidDiv++;
203
204 if (x > m_bdata.m_obbXmax)
205 m_bdata.m_obbXmax = x;
206 if (x < m_bdata.m_obbXmin)
207 m_bdata.m_obbXmin = x;
208
209 if (y > m_bdata.m_obbYmax)
210 m_bdata.m_obbYmax = y;
211 if (y < m_bdata.m_obbYmin)
212 m_bdata.m_obbYmin = y;
213
214 if (z > m_bdata.m_obbZmax)
215 m_bdata.m_obbZmax = z;
216 if (z < m_bdata.m_obbZmin)
217 m_bdata.m_obbZmin = z;
218
219 }
220
221 public void Add(Triangle triangle)
222 {
223 if (m_indicesPtr != IntPtr.Zero || m_verticesPtr != IntPtr.Zero)
224 throw new NotSupportedException("Attempt to Add to a pinned Mesh");
225
226
227 triangle.v1.X = (float)Math.Round(triangle.v1.X, 6);
228 triangle.v1.Y = (float)Math.Round(triangle.v1.Y, 6);
229 triangle.v1.Z = (float)Math.Round(triangle.v1.Z, 6);
230 triangle.v2.X = (float)Math.Round(triangle.v2.X, 6);
231 triangle.v2.Y = (float)Math.Round(triangle.v2.Y, 6);
232 triangle.v2.Z = (float)Math.Round(triangle.v2.Z, 6);
233 triangle.v3.X = (float)Math.Round(triangle.v3.X, 6);
234 triangle.v3.Y = (float)Math.Round(triangle.v3.Y, 6);
235 triangle.v3.Z = (float)Math.Round(triangle.v3.Z, 6);
236
237 if ((triangle.v1.X == triangle.v2.X && triangle.v1.Y == triangle.v2.Y && triangle.v1.Z == triangle.v2.Z)
238 || (triangle.v1.X == triangle.v3.X && triangle.v1.Y == triangle.v3.Y && triangle.v1.Z == triangle.v3.Z)
239 || (triangle.v2.X == triangle.v3.X && triangle.v2.Y == triangle.v3.Y && triangle.v2.Z == triangle.v3.Z)
240 )
241 {
242 return;
243 }
244
245 if (m_bdata.m_vertices.Count == 0)
246 {
247 m_bdata.m_centroidDiv = 0;
248 m_bdata.m_centroid = Vector3.Zero;
249 }
250
251 if (!m_bdata.m_vertices.ContainsKey(triangle.v1))
252 {
253 m_bdata.m_vertices[triangle.v1] = m_bdata.m_vertices.Count;
254 addVertexLStats(triangle.v1);
255 }
256 if (!m_bdata.m_vertices.ContainsKey(triangle.v2))
257 {
258 m_bdata.m_vertices[triangle.v2] = m_bdata.m_vertices.Count;
259 addVertexLStats(triangle.v2);
260 }
261 if (!m_bdata.m_vertices.ContainsKey(triangle.v3))
262 {
263 m_bdata.m_vertices[triangle.v3] = m_bdata.m_vertices.Count;
264 addVertexLStats(triangle.v3);
265 }
266 m_bdata.m_triangles.Add(triangle);
267 }
268
269 public Vector3 GetCentroid()
270 {
271 return m_obboffset;
272
273 }
274
275 public Vector3 GetOBB()
276 {
277 return m_obb;
278/*
279 float x, y, z;
280 if (m_bdata.m_centroidDiv > 0)
281 {
282 x = (m_bdata.m_obbXmax - m_bdata.m_obbXmin) * 0.5f;
283 y = (m_bdata.m_obbYmax - m_bdata.m_obbYmin) * 0.5f;
284 z = (m_bdata.m_obbZmax - m_bdata.m_obbZmin) * 0.5f;
285 }
286 else // ??
287 {
288 x = 0.5f;
289 y = 0.5f;
290 z = 0.5f;
291 }
292 return new Vector3(x, y, z);
293*/
294 }
295
296 public int numberVertices()
297 {
298 return m_bdata.m_vertices.Count;
299 }
300
301 public int numberTriangles()
302 {
303 return m_bdata.m_triangles.Count;
304 }
305
306 public List<Vector3> getVertexList()
307 {
308 List<Vector3> result = new List<Vector3>();
309 foreach (Vertex v in m_bdata.m_vertices.Keys)
310 {
311 result.Add(new Vector3(v.X, v.Y, v.Z));
312 }
313 return result;
314 }
315
316 public float[] getVertexListAsFloat()
317 {
318 if (m_bdata.m_vertices == null)
319 throw new NotSupportedException();
320 float[] result = new float[m_bdata.m_vertices.Count * 3];
321 foreach (KeyValuePair<Vertex, int> kvp in m_bdata.m_vertices)
322 {
323 Vertex v = kvp.Key;
324 int i = kvp.Value;
325 result[3 * i + 0] = v.X;
326 result[3 * i + 1] = v.Y;
327 result[3 * i + 2] = v.Z;
328 }
329 return result;
330 }
331
332 public float[] getVertexListAsFloatLocked()
333 {
334 return null;
335 }
336
337 public void getVertexListAsPtrToFloatArray(out IntPtr _vertices, out int vertexStride, out int vertexCount)
338 {
339 // A vertex is 3 floats
340 vertexStride = 3 * sizeof(float);
341
342 // If there isn't an unmanaged array allocated yet, do it now
343 if (m_verticesPtr == IntPtr.Zero && m_bdata != null)
344 {
345 vertices = getVertexListAsFloat();
346 // Each vertex is 3 elements (floats)
347 m_vertexCount = vertices.Length / 3;
348 vhandler = GCHandle.Alloc(vertices, GCHandleType.Pinned);
349 m_verticesPtr = vhandler.AddrOfPinnedObject();
350 GC.AddMemoryPressure(Buffer.ByteLength(vertices));
351 }
352 _vertices = m_verticesPtr;
353 vertexCount = m_vertexCount;
354 }
355
356 public int[] getIndexListAsInt()
357 {
358 if (m_bdata.m_triangles == null)
359 throw new NotSupportedException();
360 int[] result = new int[m_bdata.m_triangles.Count * 3];
361 for (int i = 0; i < m_bdata.m_triangles.Count; i++)
362 {
363 Triangle t = m_bdata.m_triangles[i];
364 result[3 * i + 0] = m_bdata.m_vertices[t.v1];
365 result[3 * i + 1] = m_bdata.m_vertices[t.v2];
366 result[3 * i + 2] = m_bdata.m_vertices[t.v3];
367 }
368 return result;
369 }
370
371 /// <summary>
372 /// creates a list of index values that defines triangle faces. THIS METHOD FREES ALL NON-PINNED MESH DATA
373 /// </summary>
374 /// <returns></returns>
375 public int[] getIndexListAsIntLocked()
376 {
377 return null;
378 }
379
380 public void getIndexListAsPtrToIntArray(out IntPtr indices, out int triStride, out int indexCount)
381 {
382 // If there isn't an unmanaged array allocated yet, do it now
383 if (m_indicesPtr == IntPtr.Zero && m_bdata != null)
384 {
385 indexes = getIndexListAsInt();
386 m_indexCount = indexes.Length;
387 ihandler = GCHandle.Alloc(indexes, GCHandleType.Pinned);
388 m_indicesPtr = ihandler.AddrOfPinnedObject();
389 GC.AddMemoryPressure(Buffer.ByteLength(indexes));
390 }
391 // A triangle is 3 ints (indices)
392 triStride = 3 * sizeof(int);
393 indices = m_indicesPtr;
394 indexCount = m_indexCount;
395 }
396
397 public void releasePinned()
398 {
399 if (m_verticesPtr != IntPtr.Zero)
400 {
401 vhandler.Free();
402 vertices = null;
403 m_verticesPtr = IntPtr.Zero;
404 }
405 if (m_indicesPtr != IntPtr.Zero)
406 {
407 ihandler.Free();
408 indexes = null;
409 m_indicesPtr = IntPtr.Zero;
410 }
411 }
412
413 /// <summary>
414 /// frees up the source mesh data to minimize memory - call this method after calling get*Locked() functions
415 /// </summary>
416 public void releaseSourceMeshData()
417 {
418 if (m_bdata != null)
419 {
420 m_bdata.m_triangles = null;
421 m_bdata.m_vertices = null;
422 }
423 }
424
425 public void releaseBuildingMeshData()
426 {
427 if (m_bdata != null)
428 {
429 m_bdata.m_triangles = null;
430 m_bdata.m_vertices = null;
431 m_bdata = null;
432 }
433 }
434
435 public void Append(IMesh newMesh)
436 {
437 if (m_indicesPtr != IntPtr.Zero || m_verticesPtr != IntPtr.Zero)
438 throw new NotSupportedException("Attempt to Append to a pinned Mesh");
439
440 if (!(newMesh is Mesh))
441 return;
442
443 foreach (Triangle t in ((Mesh)newMesh).m_bdata.m_triangles)
444 Add(t);
445 }
446
447 // Do a linear transformation of mesh.
448 public void TransformLinear(float[,] matrix, float[] offset)
449 {
450 if (m_indicesPtr != IntPtr.Zero || m_verticesPtr != IntPtr.Zero)
451 throw new NotSupportedException("Attempt to TransformLinear a pinned Mesh");
452
453 foreach (Vertex v in m_bdata.m_vertices.Keys)
454 {
455 if (v == null)
456 continue;
457 float x, y, z;
458 x = v.X*matrix[0, 0] + v.Y*matrix[1, 0] + v.Z*matrix[2, 0];
459 y = v.X*matrix[0, 1] + v.Y*matrix[1, 1] + v.Z*matrix[2, 1];
460 z = v.X*matrix[0, 2] + v.Y*matrix[1, 2] + v.Z*matrix[2, 2];
461 v.X = x + offset[0];
462 v.Y = y + offset[1];
463 v.Z = z + offset[2];
464 }
465 }
466
467 public void DumpRaw(String path, String name, String title)
468 {
469 if (path == null)
470 return;
471 if (m_bdata == null)
472 return;
473 String fileName = name + "_" + title + ".raw";
474 String completePath = System.IO.Path.Combine(path, fileName);
475 StreamWriter sw = new StreamWriter(completePath);
476 foreach (Triangle t in m_bdata.m_triangles)
477 {
478 String s = t.ToStringRaw();
479 sw.WriteLine(s);
480 }
481 sw.Close();
482 }
483
484 public void TrimExcess()
485 {
486 m_bdata.m_triangles.TrimExcess();
487 }
488
489 public void pinMemory()
490 {
491 m_vertexCount = vertices.Length / 3;
492 vhandler = GCHandle.Alloc(vertices, GCHandleType.Pinned);
493 m_verticesPtr = vhandler.AddrOfPinnedObject();
494 GC.AddMemoryPressure(Buffer.ByteLength(vertices));
495
496 m_indexCount = indexes.Length;
497 ihandler = GCHandle.Alloc(indexes, GCHandleType.Pinned);
498 m_indicesPtr = ihandler.AddrOfPinnedObject();
499 GC.AddMemoryPressure(Buffer.ByteLength(indexes));
500 }
501
502 public void PrepForOde()
503 {
504 // If there isn't an unmanaged array allocated yet, do it now
505 if (m_verticesPtr == IntPtr.Zero)
506 vertices = getVertexListAsFloat();
507
508 // If there isn't an unmanaged array allocated yet, do it now
509 if (m_indicesPtr == IntPtr.Zero)
510 indexes = getIndexListAsInt();
511
512 pinMemory();
513
514 float x, y, z;
515
516 if (m_bdata.m_centroidDiv > 0)
517 {
518 m_obboffset = new Vector3(m_bdata.m_centroid.X / m_bdata.m_centroidDiv, m_bdata.m_centroid.Y / m_bdata.m_centroidDiv, m_bdata.m_centroid.Z / m_bdata.m_centroidDiv);
519 x = (m_bdata.m_obbXmax - m_bdata.m_obbXmin) * 0.5f;
520 if(x < 0.0005f)
521 x = 0.0005f;
522 y = (m_bdata.m_obbYmax - m_bdata.m_obbYmin) * 0.5f;
523 if(y < 0.0005f)
524 y = 0.0005f;
525 z = (m_bdata.m_obbZmax - m_bdata.m_obbZmin) * 0.5f;
526 if(z < 0.0005f)
527 z = 0.0005f;
528 }
529
530 else
531 {
532 m_obboffset = Vector3.Zero;
533 x = 0.5f;
534 y = 0.5f;
535 z = 0.5f;
536 }
537
538 m_obb = new Vector3(x, y, z);
539
540 releaseBuildingMeshData();
541 }
542 public bool ToStream(Stream st)
543 {
544 if (m_indicesPtr == IntPtr.Zero || m_verticesPtr == IntPtr.Zero)
545 return false;
546
547 BinaryWriter bw = new BinaryWriter(st);
548 bool ok = true;
549
550 try
551 {
552
553 bw.Write(m_vertexCount);
554 bw.Write(m_indexCount);
555
556 for (int i = 0; i < 3 * m_vertexCount; i++)
557 bw.Write(vertices[i]);
558 for (int i = 0; i < m_indexCount; i++)
559 bw.Write(indexes[i]);
560 bw.Write(m_obb.X);
561 bw.Write(m_obb.Y);
562 bw.Write(m_obb.Z);
563 bw.Write(m_obboffset.X);
564 bw.Write(m_obboffset.Y);
565 bw.Write(m_obboffset.Z);
566 }
567 catch
568 {
569 ok = false;
570 }
571
572 if (bw != null)
573 {
574 bw.Flush();
575 bw.Close();
576 }
577
578 return ok;
579 }
580
581 public static Mesh FromStream(Stream st, AMeshKey key)
582 {
583 Mesh mesh = new Mesh();
584 mesh.releaseBuildingMeshData();
585
586 BinaryReader br = new BinaryReader(st);
587
588 bool ok = true;
589 try
590 {
591 mesh.m_vertexCount = br.ReadInt32();
592 mesh.m_indexCount = br.ReadInt32();
593
594 int n = 3 * mesh.m_vertexCount;
595 mesh.vertices = new float[n];
596 for (int i = 0; i < n; i++)
597 mesh.vertices[i] = br.ReadSingle();
598
599 mesh.indexes = new int[mesh.m_indexCount];
600 for (int i = 0; i < mesh.m_indexCount; i++)
601 mesh.indexes[i] = br.ReadInt32();
602
603 mesh.m_obb.X = br.ReadSingle();
604 mesh.m_obb.Y = br.ReadSingle();
605 mesh.m_obb.Z = br.ReadSingle();
606
607 mesh.m_obboffset.X = br.ReadSingle();
608 mesh.m_obboffset.Y = br.ReadSingle();
609 mesh.m_obboffset.Z = br.ReadSingle();
610 }
611 catch
612 {
613 ok = false;
614 }
615
616 br.Close();
617
618 if (ok)
619 {
620 mesh.pinMemory();
621
622 mesh.Key = key;
623 mesh.RefCount = 1;
624
625 return mesh;
626 }
627
628 mesh.vertices = null;
629 mesh.indexes = null;
630 return null;
631 }
632 }
633}
diff --git a/OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs b/OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs
new file mode 100644
index 0000000..a6e303a
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs
@@ -0,0 +1,1465 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27//#define SPAM
28
29using System;
30using System.Collections.Generic;
31using OpenSim.Framework;
32using OpenSim.Region.Framework.Scenes;
33using OpenSim.Region.Framework.Interfaces;
34using OpenSim.Region.PhysicsModules.SharedBase;
35using OpenSim.Region.PhysicsModules.ConvexDecompositionDotNet;
36using OpenMetaverse;
37using OpenMetaverse.StructuredData;
38using System.Drawing;
39using System.Drawing.Imaging;
40using System.IO.Compression;
41using PrimMesher;
42using log4net;
43using Nini.Config;
44using System.Reflection;
45using System.IO;
46using System.Runtime.Serialization;
47using System.Runtime.Serialization.Formatters.Binary;
48
49using Mono.Addins;
50
51namespace OpenSim.Region.PhysicsModule.ubODEMeshing
52{
53 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "ubODEMeshmerizer")]
54 public class ubMeshmerizer : IMesher, INonSharedRegionModule
55 {
56 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
57
58 // Setting baseDir to a path will enable the dumping of raw files
59 // raw files can be imported by blender so a visual inspection of the results can be done
60
61 private bool m_Enabled = false;
62
63 public object diskLock = new object();
64
65 public bool doMeshFileCache = true;
66
67 public string cachePath = "MeshCache";
68 public TimeSpan CacheExpire;
69 public bool doCacheExpire = true;
70
71// const string baseDir = "rawFiles";
72 private const string baseDir = null; //"rawFiles";
73
74 private bool useMeshiesPhysicsMesh = false;
75
76 private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh
77
78 private Dictionary<AMeshKey, Mesh> m_uniqueMeshes = new Dictionary<AMeshKey, Mesh>();
79 private Dictionary<AMeshKey, Mesh> m_uniqueReleasedMeshes = new Dictionary<AMeshKey, Mesh>();
80
81 #region INonSharedRegionModule
82 public string Name
83 {
84 get { return "ubODEMeshmerizer"; }
85 }
86
87 public Type ReplaceableInterface
88 {
89 get { return null; }
90 }
91
92 public void Initialise(IConfigSource config)
93 {
94 IConfig start_config = config.Configs["Startup"];
95
96 string mesher = start_config.GetString("meshing", string.Empty);
97 if (mesher == Name)
98 {
99 float fcache = 48.0f;
100 // float fcache = 0.02f;
101
102 IConfig mesh_config = config.Configs["Mesh"];
103 if (mesh_config != null)
104 {
105 useMeshiesPhysicsMesh = mesh_config.GetBoolean("UseMeshiesPhysicsMesh", useMeshiesPhysicsMesh);
106 if (useMeshiesPhysicsMesh)
107 {
108 doMeshFileCache = mesh_config.GetBoolean("MeshFileCache", doMeshFileCache);
109 cachePath = mesh_config.GetString("MeshFileCachePath", cachePath);
110 fcache = mesh_config.GetFloat("MeshFileCacheExpireHours", fcache);
111 doCacheExpire = mesh_config.GetBoolean("MeshFileCacheDoExpire", doCacheExpire);
112 }
113 else
114 {
115 doMeshFileCache = false;
116 doCacheExpire = false;
117 }
118
119 m_Enabled = true;
120 }
121
122 CacheExpire = TimeSpan.FromHours(fcache);
123
124 }
125 }
126
127 public void Close()
128 {
129 }
130
131 public void AddRegion(Scene scene)
132 {
133 if (!m_Enabled)
134 return;
135
136 scene.RegisterModuleInterface<IMesher>(this);
137 }
138
139 public void RemoveRegion(Scene scene)
140 {
141 if (!m_Enabled)
142 return;
143
144 scene.UnregisterModuleInterface<IMesher>(this);
145 }
146
147 public void RegionLoaded(Scene scene)
148 {
149 if (!m_Enabled)
150 return;
151 }
152
153 #endregion
154
155 /// <summary>
156 /// creates a simple box mesh of the specified size. This mesh is of very low vertex count and may
157 /// be useful as a backup proxy when level of detail is not needed or when more complex meshes fail
158 /// for some reason
159 /// </summary>
160 /// <param name="minX"></param>
161 /// <param name="maxX"></param>
162 /// <param name="minY"></param>
163 /// <param name="maxY"></param>
164 /// <param name="minZ"></param>
165 /// <param name="maxZ"></param>
166 /// <returns></returns>
167 private static Mesh CreateSimpleBoxMesh(float minX, float maxX, float minY, float maxY, float minZ, float maxZ)
168 {
169 Mesh box = new Mesh();
170 List<Vertex> vertices = new List<Vertex>();
171 // bottom
172
173 vertices.Add(new Vertex(minX, maxY, minZ));
174 vertices.Add(new Vertex(maxX, maxY, minZ));
175 vertices.Add(new Vertex(maxX, minY, minZ));
176 vertices.Add(new Vertex(minX, minY, minZ));
177
178 box.Add(new Triangle(vertices[0], vertices[1], vertices[2]));
179 box.Add(new Triangle(vertices[0], vertices[2], vertices[3]));
180
181 // top
182
183 vertices.Add(new Vertex(maxX, maxY, maxZ));
184 vertices.Add(new Vertex(minX, maxY, maxZ));
185 vertices.Add(new Vertex(minX, minY, maxZ));
186 vertices.Add(new Vertex(maxX, minY, maxZ));
187
188 box.Add(new Triangle(vertices[4], vertices[5], vertices[6]));
189 box.Add(new Triangle(vertices[4], vertices[6], vertices[7]));
190
191 // sides
192
193 box.Add(new Triangle(vertices[5], vertices[0], vertices[3]));
194 box.Add(new Triangle(vertices[5], vertices[3], vertices[6]));
195
196 box.Add(new Triangle(vertices[1], vertices[0], vertices[5]));
197 box.Add(new Triangle(vertices[1], vertices[5], vertices[4]));
198
199 box.Add(new Triangle(vertices[7], vertices[1], vertices[4]));
200 box.Add(new Triangle(vertices[7], vertices[2], vertices[1]));
201
202 box.Add(new Triangle(vertices[3], vertices[2], vertices[7]));
203 box.Add(new Triangle(vertices[3], vertices[7], vertices[6]));
204
205 return box;
206 }
207
208 /// <summary>
209 /// Creates a simple bounding box mesh for a complex input mesh
210 /// </summary>
211 /// <param name="meshIn"></param>
212 /// <returns></returns>
213 private static Mesh CreateBoundingBoxMesh(Mesh meshIn)
214 {
215 float minX = float.MaxValue;
216 float maxX = float.MinValue;
217 float minY = float.MaxValue;
218 float maxY = float.MinValue;
219 float minZ = float.MaxValue;
220 float maxZ = float.MinValue;
221
222 foreach (Vector3 v in meshIn.getVertexList())
223 {
224 if (v.X < minX) minX = v.X;
225 if (v.Y < minY) minY = v.Y;
226 if (v.Z < minZ) minZ = v.Z;
227
228 if (v.X > maxX) maxX = v.X;
229 if (v.Y > maxY) maxY = v.Y;
230 if (v.Z > maxZ) maxZ = v.Z;
231 }
232
233 return CreateSimpleBoxMesh(minX, maxX, minY, maxY, minZ, maxZ);
234 }
235
236 private void ReportPrimError(string message, string primName, PrimMesh primMesh)
237 {
238 m_log.Error(message);
239 m_log.Error("\nPrim Name: " + primName);
240 m_log.Error("****** PrimMesh Parameters ******\n" + primMesh.ParamsToDisplayString());
241 }
242
243 /// <summary>
244 /// Add a submesh to an existing list of coords and faces.
245 /// </summary>
246 /// <param name="subMeshData"></param>
247 /// <param name="size">Size of entire object</param>
248 /// <param name="coords"></param>
249 /// <param name="faces"></param>
250 private void AddSubMesh(OSDMap subMeshData, List<Coord> coords, List<Face> faces)
251 {
252 // Console.WriteLine("subMeshMap for {0} - {1}", primName, Util.GetFormattedXml((OSD)subMeshMap));
253
254 // As per http://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format, some Mesh Level
255 // of Detail Blocks (maps) contain just a NoGeometry key to signal there is no
256 // geometry for this submesh.
257 if (subMeshData.ContainsKey("NoGeometry") && ((OSDBoolean)subMeshData["NoGeometry"]))
258 return;
259
260 OpenMetaverse.Vector3 posMax;
261 OpenMetaverse.Vector3 posMin;
262 if (subMeshData.ContainsKey("PositionDomain"))
263 {
264 posMax = ((OSDMap)subMeshData["PositionDomain"])["Max"].AsVector3();
265 posMin = ((OSDMap)subMeshData["PositionDomain"])["Min"].AsVector3();
266 }
267 else
268 {
269 posMax = new Vector3(0.5f, 0.5f, 0.5f);
270 posMin = new Vector3(-0.5f, -0.5f, -0.5f);
271 }
272
273 ushort faceIndexOffset = (ushort)coords.Count;
274
275 byte[] posBytes = subMeshData["Position"].AsBinary();
276 for (int i = 0; i < posBytes.Length; i += 6)
277 {
278 ushort uX = Utils.BytesToUInt16(posBytes, i);
279 ushort uY = Utils.BytesToUInt16(posBytes, i + 2);
280 ushort uZ = Utils.BytesToUInt16(posBytes, i + 4);
281
282 Coord c = new Coord(
283 Utils.UInt16ToFloat(uX, posMin.X, posMax.X),
284 Utils.UInt16ToFloat(uY, posMin.Y, posMax.Y),
285 Utils.UInt16ToFloat(uZ, posMin.Z, posMax.Z));
286
287 coords.Add(c);
288 }
289
290 byte[] triangleBytes = subMeshData["TriangleList"].AsBinary();
291 for (int i = 0; i < triangleBytes.Length; i += 6)
292 {
293 ushort v1 = (ushort)(Utils.BytesToUInt16(triangleBytes, i) + faceIndexOffset);
294 ushort v2 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 2) + faceIndexOffset);
295 ushort v3 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 4) + faceIndexOffset);
296 Face f = new Face(v1, v2, v3);
297 faces.Add(f);
298 }
299 }
300
301 /// <summary>
302 /// Create a physics mesh from data that comes with the prim. The actual data used depends on the prim type.
303 /// </summary>
304 /// <param name="primName"></param>
305 /// <param name="primShape"></param>
306 /// <param name="size"></param>
307 /// <param name="lod"></param>
308 /// <returns></returns>
309 private Mesh CreateMeshFromPrimMesher(string primName, PrimitiveBaseShape primShape, float lod, bool convex)
310 {
311// m_log.DebugFormat(
312// "[MESH]: Creating physics proxy for {0}, shape {1}",
313// primName, (OpenMetaverse.SculptType)primShape.SculptType);
314
315 List<Coord> coords;
316 List<Face> faces;
317
318 if (primShape.SculptEntry)
319 {
320 if (((OpenMetaverse.SculptType)primShape.SculptType) == SculptType.Mesh)
321 {
322 if (!useMeshiesPhysicsMesh)
323 return null;
324
325 if (!GenerateCoordsAndFacesFromPrimMeshData(primName, primShape, out coords, out faces, convex))
326 return null;
327 }
328 else
329 {
330 if (!GenerateCoordsAndFacesFromPrimSculptData(primName, primShape, lod, out coords, out faces))
331 return null;
332 }
333 }
334 else
335 {
336 if (!GenerateCoordsAndFacesFromPrimShapeData(primName, primShape, lod, out coords, out faces))
337 return null;
338 }
339
340
341 int numCoords = coords.Count;
342 int numFaces = faces.Count;
343
344 Mesh mesh = new Mesh();
345 // Add the corresponding triangles to the mesh
346 for (int i = 0; i < numFaces; i++)
347 {
348 Face f = faces[i];
349 mesh.Add(new Triangle(coords[f.v1].X, coords[f.v1].Y, coords[f.v1].Z,
350 coords[f.v2].X, coords[f.v2].Y, coords[f.v2].Z,
351 coords[f.v3].X, coords[f.v3].Y, coords[f.v3].Z));
352 }
353
354 coords.Clear();
355 faces.Clear();
356
357 if(mesh.numberVertices() < 3 || mesh.numberTriangles() < 1)
358 {
359 m_log.ErrorFormat("[MESH]: invalid degenerated mesh for prim " + primName + " ignored");
360 return null;
361 }
362
363 primShape.SculptData = Utils.EmptyBytes;
364
365 return mesh;
366 }
367
368 /// <summary>
369 /// Generate the co-ords and faces necessary to construct a mesh from the mesh data the accompanies a prim.
370 /// </summary>
371 /// <param name="primName"></param>
372 /// <param name="primShape"></param>
373 /// <param name="size"></param>
374 /// <param name="coords">Coords are added to this list by the method.</param>
375 /// <param name="faces">Faces are added to this list by the method.</param>
376 /// <returns>true if coords and faces were successfully generated, false if not</returns>
377 private bool GenerateCoordsAndFacesFromPrimMeshData(
378 string primName, PrimitiveBaseShape primShape, out List<Coord> coords, out List<Face> faces, bool convex)
379 {
380// m_log.DebugFormat("[MESH]: experimental mesh proxy generation for {0}", primName);
381
382 bool usemesh = false;
383
384 coords = new List<Coord>();
385 faces = new List<Face>();
386 OSD meshOsd = null;
387
388 if (primShape.SculptData.Length <= 0)
389 {
390// m_log.InfoFormat("[MESH]: asset data for {0} is zero length", primName);
391 return false;
392 }
393
394 long start = 0;
395 using (MemoryStream data = new MemoryStream(primShape.SculptData))
396 {
397 try
398 {
399 OSD osd = OSDParser.DeserializeLLSDBinary(data);
400 if (osd is OSDMap)
401 meshOsd = (OSDMap)osd;
402 else
403 {
404 m_log.Warn("[Mesh}: unable to cast mesh asset to OSDMap");
405 return false;
406 }
407 }
408 catch (Exception e)
409 {
410 m_log.Error("[MESH]: Exception deserializing mesh asset header:" + e.ToString());
411 }
412
413 start = data.Position;
414 }
415
416 if (meshOsd is OSDMap)
417 {
418 OSDMap physicsParms = null;
419 OSDMap map = (OSDMap)meshOsd;
420
421 if (!convex)
422 {
423 if (map.ContainsKey("physics_shape"))
424 physicsParms = (OSDMap)map["physics_shape"]; // old asset format
425 else if (map.ContainsKey("physics_mesh"))
426 physicsParms = (OSDMap)map["physics_mesh"]; // new asset format
427
428 if (physicsParms != null)
429 usemesh = true;
430 }
431
432 if(!usemesh && (map.ContainsKey("physics_convex")))
433 physicsParms = (OSDMap)map["physics_convex"];
434
435
436 if (physicsParms == null)
437 {
438 m_log.Warn("[MESH]: unknown mesh type");
439 return false;
440 }
441
442 int physOffset = physicsParms["offset"].AsInteger() + (int)start;
443 int physSize = physicsParms["size"].AsInteger();
444
445 if (physOffset < 0 || physSize == 0)
446 return false; // no mesh data in asset
447
448 OSD decodedMeshOsd = new OSD();
449 byte[] meshBytes = new byte[physSize];
450 System.Buffer.BlockCopy(primShape.SculptData, physOffset, meshBytes, 0, physSize);
451
452 try
453 {
454 using (MemoryStream inMs = new MemoryStream(meshBytes))
455 {
456 using (MemoryStream outMs = new MemoryStream())
457 {
458 using (DeflateStream decompressionStream = new DeflateStream(inMs, CompressionMode.Decompress))
459 {
460 byte[] readBuffer = new byte[2048];
461 inMs.Read(readBuffer, 0, 2); // skip first 2 bytes in header
462 int readLen = 0;
463
464 while ((readLen = decompressionStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
465 outMs.Write(readBuffer, 0, readLen);
466
467 outMs.Flush();
468 outMs.Seek(0, SeekOrigin.Begin);
469
470 byte[] decompressedBuf = outMs.GetBuffer();
471
472 decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
473 }
474 }
475 }
476 }
477 catch (Exception e)
478 {
479 m_log.Error("[MESH]: exception decoding physical mesh prim " + primName +" : " + e.ToString());
480 return false;
481 }
482
483 if (usemesh)
484 {
485 OSDArray decodedMeshOsdArray = null;
486
487 // physics_shape is an array of OSDMaps, one for each submesh
488 if (decodedMeshOsd is OSDArray)
489 {
490// Console.WriteLine("decodedMeshOsd for {0} - {1}", primName, Util.GetFormattedXml(decodedMeshOsd));
491
492 decodedMeshOsdArray = (OSDArray)decodedMeshOsd;
493 foreach (OSD subMeshOsd in decodedMeshOsdArray)
494 {
495 if (subMeshOsd is OSDMap)
496 AddSubMesh(subMeshOsd as OSDMap, coords, faces);
497 }
498 }
499 }
500 else
501 {
502 OSDMap cmap = (OSDMap)decodedMeshOsd;
503 if (cmap == null)
504 return false;
505
506 byte[] data;
507
508 List<float3> vs = new List<float3>();
509 PHullResult hullr = new PHullResult();
510 float3 f3;
511 Coord c;
512 Face f;
513 Vector3 range;
514 Vector3 min;
515
516 const float invMaxU16 = 1.0f / 65535f;
517 int t1;
518 int t2;
519 int t3;
520 int i;
521 int nverts;
522 int nindexs;
523
524 if (cmap.ContainsKey("Max"))
525 range = cmap["Max"].AsVector3();
526 else
527 range = new Vector3(0.5f, 0.5f, 0.5f);
528
529 if (cmap.ContainsKey("Min"))
530 min = cmap["Min"].AsVector3();
531 else
532 min = new Vector3(-0.5f, -0.5f, -0.5f);
533
534 range = range - min;
535 range *= invMaxU16;
536
537 if (!convex && cmap.ContainsKey("HullList") && cmap.ContainsKey("Positions"))
538 {
539 List<int> hsizes = new List<int>();
540 int totalpoints = 0;
541 data = cmap["HullList"].AsBinary();
542 for (i = 0; i < data.Length; i++)
543 {
544 t1 = data[i];
545 if (t1 == 0)
546 t1 = 256;
547 totalpoints += t1;
548 hsizes.Add(t1);
549 }
550
551 data = cmap["Positions"].AsBinary();
552 int ptr = 0;
553 int vertsoffset = 0;
554
555 if (totalpoints == data.Length / 6) // 2 bytes per coord, 3 coords per point
556 {
557 foreach (int hullsize in hsizes)
558 {
559 for (i = 0; i < hullsize; i++ )
560 {
561 t1 = data[ptr++];
562 t1 += data[ptr++] << 8;
563 t2 = data[ptr++];
564 t2 += data[ptr++] << 8;
565 t3 = data[ptr++];
566 t3 += data[ptr++] << 8;
567
568 f3 = new float3((t1 * range.X + min.X),
569 (t2 * range.Y + min.Y),
570 (t3 * range.Z + min.Z));
571 vs.Add(f3);
572 }
573
574 if(hullsize <3)
575 {
576 vs.Clear();
577 continue;
578 }
579
580 if (hullsize <5)
581 {
582 foreach (float3 point in vs)
583 {
584 c.X = point.x;
585 c.Y = point.y;
586 c.Z = point.z;
587 coords.Add(c);
588 }
589 f = new Face(vertsoffset, vertsoffset + 1, vertsoffset + 2);
590 faces.Add(f);
591
592 if (hullsize == 4)
593 {
594 // not sure about orientation..
595 f = new Face(vertsoffset, vertsoffset + 2, vertsoffset + 3);
596 faces.Add(f);
597 f = new Face(vertsoffset, vertsoffset + 3, vertsoffset + 1);
598 faces.Add(f);
599 f = new Face(vertsoffset + 3, vertsoffset + 2, vertsoffset + 1);
600 faces.Add(f);
601 }
602 vertsoffset += vs.Count;
603 vs.Clear();
604 continue;
605 }
606
607 if (!HullUtils.ComputeHull(vs, ref hullr, 0, 0.0f))
608 {
609 vs.Clear();
610 continue;
611 }
612
613 nverts = hullr.Vertices.Count;
614 nindexs = hullr.Indices.Count;
615
616 if (nindexs % 3 != 0)
617 {
618 vs.Clear();
619 continue;
620 }
621
622 for (i = 0; i < nverts; i++)
623 {
624 c.X = hullr.Vertices[i].x;
625 c.Y = hullr.Vertices[i].y;
626 c.Z = hullr.Vertices[i].z;
627 coords.Add(c);
628 }
629
630 for (i = 0; i < nindexs; i += 3)
631 {
632 t1 = hullr.Indices[i];
633 if (t1 > nverts)
634 break;
635 t2 = hullr.Indices[i + 1];
636 if (t2 > nverts)
637 break;
638 t3 = hullr.Indices[i + 2];
639 if (t3 > nverts)
640 break;
641 f = new Face(vertsoffset + t1, vertsoffset + t2, vertsoffset + t3);
642 faces.Add(f);
643 }
644 vertsoffset += nverts;
645 vs.Clear();
646 }
647 }
648 if (coords.Count > 0 && faces.Count > 0)
649 return true;
650 }
651
652 vs.Clear();
653
654 if (cmap.ContainsKey("BoundingVerts"))
655 {
656 data = cmap["BoundingVerts"].AsBinary();
657
658 for (i = 0; i < data.Length; )
659 {
660 t1 = data[i++];
661 t1 += data[i++] << 8;
662 t2 = data[i++];
663 t2 += data[i++] << 8;
664 t3 = data[i++];
665 t3 += data[i++] << 8;
666
667 f3 = new float3((t1 * range.X + min.X),
668 (t2 * range.Y + min.Y),
669 (t3 * range.Z + min.Z));
670 vs.Add(f3);
671 }
672
673 if (vs.Count < 3)
674 {
675 vs.Clear();
676 return false;
677 }
678
679 if (vs.Count < 5)
680 {
681 foreach (float3 point in vs)
682 {
683 c.X = point.x;
684 c.Y = point.y;
685 c.Z = point.z;
686 coords.Add(c);
687 }
688 f = new Face(0, 1, 2);
689 faces.Add(f);
690
691 if (vs.Count == 4)
692 {
693 f = new Face(0, 2, 3);
694 faces.Add(f);
695 f = new Face(0, 3, 1);
696 faces.Add(f);
697 f = new Face( 3, 2, 1);
698 faces.Add(f);
699 }
700 vs.Clear();
701 return true;
702 }
703
704 if (!HullUtils.ComputeHull(vs, ref hullr, 0, 0.0f))
705 return false;
706
707 nverts = hullr.Vertices.Count;
708 nindexs = hullr.Indices.Count;
709
710 if (nindexs % 3 != 0)
711 return false;
712
713 for (i = 0; i < nverts; i++)
714 {
715 c.X = hullr.Vertices[i].x;
716 c.Y = hullr.Vertices[i].y;
717 c.Z = hullr.Vertices[i].z;
718 coords.Add(c);
719 }
720 for (i = 0; i < nindexs; i += 3)
721 {
722 t1 = hullr.Indices[i];
723 if (t1 > nverts)
724 break;
725 t2 = hullr.Indices[i + 1];
726 if (t2 > nverts)
727 break;
728 t3 = hullr.Indices[i + 2];
729 if (t3 > nverts)
730 break;
731 f = new Face(t1, t2, t3);
732 faces.Add(f);
733 }
734
735 if (coords.Count > 0 && faces.Count > 0)
736 return true;
737 }
738 else
739 return false;
740 }
741 }
742
743 return true;
744 }
745
746 /// <summary>
747 /// Generate the co-ords and faces necessary to construct a mesh from the sculpt data the accompanies a prim.
748 /// </summary>
749 /// <param name="primName"></param>
750 /// <param name="primShape"></param>
751 /// <param name="size"></param>
752 /// <param name="lod"></param>
753 /// <param name="coords">Coords are added to this list by the method.</param>
754 /// <param name="faces">Faces are added to this list by the method.</param>
755 /// <returns>true if coords and faces were successfully generated, false if not</returns>
756 private bool GenerateCoordsAndFacesFromPrimSculptData(
757 string primName, PrimitiveBaseShape primShape, float lod, out List<Coord> coords, out List<Face> faces)
758 {
759 coords = new List<Coord>();
760 faces = new List<Face>();
761 PrimMesher.SculptMesh sculptMesh;
762 Image idata = null;
763
764 if (primShape.SculptData == null || primShape.SculptData.Length == 0)
765 return false;
766
767 try
768 {
769 OpenMetaverse.Imaging.ManagedImage unusedData;
770 OpenMetaverse.Imaging.OpenJPEG.DecodeToImage(primShape.SculptData, out unusedData, out idata);
771
772 unusedData = null;
773
774 if (idata == null)
775 {
776 // In some cases it seems that the decode can return a null bitmap without throwing
777 // an exception
778 m_log.WarnFormat("[PHYSICS]: OpenJPEG decoded sculpt data for {0} to a null bitmap. Ignoring.", primName);
779 return false;
780 }
781 }
782 catch (DllNotFoundException)
783 {
784 m_log.Error("[PHYSICS]: OpenJpeg is not installed correctly on this system. Physics Proxy generation failed. Often times this is because of an old version of GLIBC. You must have version 2.4 or above!");
785 return false;
786 }
787 catch (IndexOutOfRangeException)
788 {
789 m_log.Error("[PHYSICS]: OpenJpeg was unable to decode this. Physics Proxy generation failed");
790 return false;
791 }
792 catch (Exception ex)
793 {
794 m_log.Error("[PHYSICS]: Unable to generate a Sculpty physics proxy. Sculpty texture decode failed: " + ex.Message);
795 return false;
796 }
797
798 PrimMesher.SculptMesh.SculptType sculptType;
799 // remove mirror and invert bits
800 OpenMetaverse.SculptType pbsSculptType = ((OpenMetaverse.SculptType)(primShape.SculptType & 0x3f));
801 switch (pbsSculptType)
802 {
803 case OpenMetaverse.SculptType.Cylinder:
804 sculptType = PrimMesher.SculptMesh.SculptType.cylinder;
805 break;
806 case OpenMetaverse.SculptType.Plane:
807 sculptType = PrimMesher.SculptMesh.SculptType.plane;
808 break;
809 case OpenMetaverse.SculptType.Torus:
810 sculptType = PrimMesher.SculptMesh.SculptType.torus;
811 break;
812 case OpenMetaverse.SculptType.Sphere:
813 sculptType = PrimMesher.SculptMesh.SculptType.sphere;
814 break;
815 default:
816 sculptType = PrimMesher.SculptMesh.SculptType.plane;
817 break;
818 }
819
820 bool mirror = ((primShape.SculptType & 128) != 0);
821 bool invert = ((primShape.SculptType & 64) != 0);
822
823 sculptMesh = new PrimMesher.SculptMesh((Bitmap)idata, sculptType, (int)lod, mirror, invert);
824
825 idata.Dispose();
826
827// sculptMesh.DumpRaw(baseDir, primName, "primMesh");
828
829 coords = sculptMesh.coords;
830 faces = sculptMesh.faces;
831
832 return true;
833 }
834
835 /// <summary>
836 /// Generate the co-ords and faces necessary to construct a mesh from the shape data the accompanies a prim.
837 /// </summary>
838 /// <param name="primName"></param>
839 /// <param name="primShape"></param>
840 /// <param name="size"></param>
841 /// <param name="coords">Coords are added to this list by the method.</param>
842 /// <param name="faces">Faces are added to this list by the method.</param>
843 /// <returns>true if coords and faces were successfully generated, false if not</returns>
844 private bool GenerateCoordsAndFacesFromPrimShapeData(
845 string primName, PrimitiveBaseShape primShape, float lod, out List<Coord> coords, out List<Face> faces)
846 {
847 PrimMesh primMesh;
848 coords = new List<Coord>();
849 faces = new List<Face>();
850
851 float pathShearX = primShape.PathShearX < 128 ? (float)primShape.PathShearX * 0.01f : (float)(primShape.PathShearX - 256) * 0.01f;
852 float pathShearY = primShape.PathShearY < 128 ? (float)primShape.PathShearY * 0.01f : (float)(primShape.PathShearY - 256) * 0.01f;
853 float pathBegin = (float)primShape.PathBegin * 2.0e-5f;
854 float pathEnd = 1.0f - (float)primShape.PathEnd * 2.0e-5f;
855 float pathScaleX = (float)(primShape.PathScaleX - 100) * 0.01f;
856 float pathScaleY = (float)(primShape.PathScaleY - 100) * 0.01f;
857
858 float profileBegin = (float)primShape.ProfileBegin * 2.0e-5f;
859 float profileEnd = 1.0f - (float)primShape.ProfileEnd * 2.0e-5f;
860
861 if (profileBegin < 0.0f)
862 profileBegin = 0.0f;
863
864 if (profileEnd < 0.02f)
865 profileEnd = 0.02f;
866 else if (profileEnd > 1.0f)
867 profileEnd = 1.0f;
868
869 if (profileBegin >= profileEnd)
870 profileBegin = profileEnd - 0.02f;
871
872 float profileHollow = (float)primShape.ProfileHollow * 2.0e-5f;
873 if (profileHollow > 0.95f)
874 profileHollow = 0.95f;
875
876 int sides = 4;
877 LevelOfDetail iLOD = (LevelOfDetail)lod;
878 byte profshape = (byte)(primShape.ProfileCurve & 0x07);
879
880 if (profshape == (byte)ProfileShape.EquilateralTriangle
881 || profshape == (byte)ProfileShape.IsometricTriangle
882 || profshape == (byte)ProfileShape.RightTriangle)
883 sides = 3;
884 else if (profshape == (byte)ProfileShape.Circle)
885 {
886 switch (iLOD)
887 {
888 case LevelOfDetail.High: sides = 24; break;
889 case LevelOfDetail.Medium: sides = 12; break;
890 case LevelOfDetail.Low: sides = 6; break;
891 case LevelOfDetail.VeryLow: sides = 3; break;
892 default: sides = 24; break;
893 }
894 }
895 else if (profshape == (byte)ProfileShape.HalfCircle)
896 { // half circle, prim is a sphere
897 switch (iLOD)
898 {
899 case LevelOfDetail.High: sides = 24; break;
900 case LevelOfDetail.Medium: sides = 12; break;
901 case LevelOfDetail.Low: sides = 6; break;
902 case LevelOfDetail.VeryLow: sides = 3; break;
903 default: sides = 24; break;
904 }
905
906 profileBegin = 0.5f * profileBegin + 0.5f;
907 profileEnd = 0.5f * profileEnd + 0.5f;
908 }
909
910 int hollowSides = sides;
911 if (primShape.HollowShape == HollowShape.Circle)
912 {
913 switch (iLOD)
914 {
915 case LevelOfDetail.High: hollowSides = 24; break;
916 case LevelOfDetail.Medium: hollowSides = 12; break;
917 case LevelOfDetail.Low: hollowSides = 6; break;
918 case LevelOfDetail.VeryLow: hollowSides = 3; break;
919 default: hollowSides = 24; break;
920 }
921 }
922 else if (primShape.HollowShape == HollowShape.Square)
923 hollowSides = 4;
924 else if (primShape.HollowShape == HollowShape.Triangle)
925 {
926 if (profshape == (byte)ProfileShape.HalfCircle)
927 hollowSides = 6;
928 else
929 hollowSides = 3;
930 }
931
932 primMesh = new PrimMesh(sides, profileBegin, profileEnd, profileHollow, hollowSides);
933
934 if (primMesh.errorMessage != null)
935 if (primMesh.errorMessage.Length > 0)
936 m_log.Error("[ERROR] " + primMesh.errorMessage);
937
938 primMesh.topShearX = pathShearX;
939 primMesh.topShearY = pathShearY;
940 primMesh.pathCutBegin = pathBegin;
941 primMesh.pathCutEnd = pathEnd;
942
943 if (primShape.PathCurve == (byte)Extrusion.Straight || primShape.PathCurve == (byte) Extrusion.Flexible)
944 {
945 primMesh.twistBegin = (primShape.PathTwistBegin * 18) / 10;
946 primMesh.twistEnd = (primShape.PathTwist * 18) / 10;
947 primMesh.taperX = pathScaleX;
948 primMesh.taperY = pathScaleY;
949
950#if SPAM
951 m_log.Debug("****** PrimMesh Parameters (Linear) ******\n" + primMesh.ParamsToDisplayString());
952#endif
953 try
954 {
955 primMesh.ExtrudeLinear();
956 }
957 catch (Exception ex)
958 {
959 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
960 return false;
961 }
962 }
963 else
964 {
965 primMesh.holeSizeX = (200 - primShape.PathScaleX) * 0.01f;
966 primMesh.holeSizeY = (200 - primShape.PathScaleY) * 0.01f;
967 primMesh.radius = 0.01f * primShape.PathRadiusOffset;
968 primMesh.revolutions = 1.0f + 0.015f * primShape.PathRevolutions;
969 primMesh.skew = 0.01f * primShape.PathSkew;
970 primMesh.twistBegin = (primShape.PathTwistBegin * 36) / 10;
971 primMesh.twistEnd = (primShape.PathTwist * 36) / 10;
972 primMesh.taperX = primShape.PathTaperX * 0.01f;
973 primMesh.taperY = primShape.PathTaperY * 0.01f;
974
975#if SPAM
976 m_log.Debug("****** PrimMesh Parameters (Circular) ******\n" + primMesh.ParamsToDisplayString());
977#endif
978 try
979 {
980 primMesh.ExtrudeCircular();
981 }
982 catch (Exception ex)
983 {
984 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
985 return false;
986 }
987 }
988
989// primMesh.DumpRaw(baseDir, primName, "primMesh");
990
991 coords = primMesh.coords;
992 faces = primMesh.faces;
993
994 return true;
995 }
996
997 public AMeshKey GetMeshUniqueKey(PrimitiveBaseShape primShape, Vector3 size, byte lod, bool convex)
998 {
999 AMeshKey key = new AMeshKey();
1000 Byte[] someBytes;
1001
1002 key.hashB = 5181;
1003 key.hashC = 5181;
1004 ulong hash = 5381;
1005
1006 if (primShape.SculptEntry)
1007 {
1008 key.uuid = primShape.SculptTexture;
1009 key.hashC = mdjb2(key.hashC, primShape.SculptType);
1010 key.hashC = mdjb2(key.hashC, primShape.PCode);
1011 }
1012 else
1013 {
1014 hash = mdjb2(hash, primShape.PathCurve);
1015 hash = mdjb2(hash, (byte)primShape.HollowShape);
1016 hash = mdjb2(hash, (byte)primShape.ProfileShape);
1017 hash = mdjb2(hash, primShape.PathBegin);
1018 hash = mdjb2(hash, primShape.PathEnd);
1019 hash = mdjb2(hash, primShape.PathScaleX);
1020 hash = mdjb2(hash, primShape.PathScaleY);
1021 hash = mdjb2(hash, primShape.PathShearX);
1022 key.hashA = hash;
1023 hash = key.hashB;
1024 hash = mdjb2(hash, primShape.PathShearY);
1025 hash = mdjb2(hash, (byte)primShape.PathTwist);
1026 hash = mdjb2(hash, (byte)primShape.PathTwistBegin);
1027 hash = mdjb2(hash, (byte)primShape.PathRadiusOffset);
1028 hash = mdjb2(hash, (byte)primShape.PathTaperX);
1029 hash = mdjb2(hash, (byte)primShape.PathTaperY);
1030 hash = mdjb2(hash, primShape.PathRevolutions);
1031 hash = mdjb2(hash, (byte)primShape.PathSkew);
1032 hash = mdjb2(hash, primShape.ProfileBegin);
1033 hash = mdjb2(hash, primShape.ProfileEnd);
1034 hash = mdjb2(hash, primShape.ProfileHollow);
1035 hash = mdjb2(hash, primShape.PCode);
1036 key.hashB = hash;
1037 }
1038
1039 hash = key.hashC;
1040
1041 hash = mdjb2(hash, lod);
1042
1043 if (size == m_MeshUnitSize)
1044 {
1045 hash = hash << 8;
1046 hash |= 8;
1047 }
1048 else
1049 {
1050 someBytes = size.GetBytes();
1051 for (int i = 0; i < someBytes.Length; i++)
1052 hash = mdjb2(hash, someBytes[i]);
1053 hash = hash << 8;
1054 }
1055
1056 if (convex)
1057 hash |= 4;
1058
1059 if (primShape.SculptEntry)
1060 {
1061 hash |= 1;
1062 if (primShape.SculptType == (byte)SculptType.Mesh)
1063 hash |= 2;
1064 }
1065
1066 key.hashC = hash;
1067
1068 return key;
1069 }
1070
1071 private ulong mdjb2(ulong hash, byte c)
1072 {
1073 return ((hash << 5) + hash) + (ulong)c;
1074 }
1075
1076 private ulong mdjb2(ulong hash, ushort c)
1077 {
1078 hash = ((hash << 5) + hash) + (ulong)((byte)c);
1079 return ((hash << 5) + hash) + (ulong)(c >> 8);
1080 }
1081
1082 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
1083 {
1084 return CreateMesh(primName, primShape, size, lod, false,false,false);
1085 }
1086
1087 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
1088 {
1089 return CreateMesh(primName, primShape, size, lod, false,false,false);
1090 }
1091
1092 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache, bool convex, bool forOde)
1093 {
1094 return CreateMesh(primName, primShape, size, lod, false, false, false);
1095 }
1096
1097 public IMesh GetMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex)
1098 {
1099 Mesh mesh = null;
1100
1101 if (size.X < 0.01f) size.X = 0.01f;
1102 if (size.Y < 0.01f) size.Y = 0.01f;
1103 if (size.Z < 0.01f) size.Z = 0.01f;
1104
1105 AMeshKey key = GetMeshUniqueKey(primShape, size, (byte)lod, convex);
1106 lock (m_uniqueMeshes)
1107 {
1108 m_uniqueMeshes.TryGetValue(key, out mesh);
1109
1110 if (mesh != null)
1111 {
1112 mesh.RefCount++;
1113 return mesh;
1114 }
1115
1116 // try to find a identical mesh on meshs recently released
1117 lock (m_uniqueReleasedMeshes)
1118 {
1119 m_uniqueReleasedMeshes.TryGetValue(key, out mesh);
1120 if (mesh != null)
1121 {
1122 m_uniqueReleasedMeshes.Remove(key);
1123 try
1124 {
1125 m_uniqueMeshes.Add(key, mesh);
1126 }
1127 catch { }
1128 mesh.RefCount = 1;
1129 return mesh;
1130 }
1131 }
1132 }
1133 return null;
1134 }
1135
1136 private static Vector3 m_MeshUnitSize = new Vector3(1.0f, 1.0f, 1.0f);
1137
1138 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex, bool forOde)
1139 {
1140#if SPAM
1141 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName);
1142#endif
1143
1144 Mesh mesh = null;
1145
1146 if (size.X < 0.01f) size.X = 0.01f;
1147 if (size.Y < 0.01f) size.Y = 0.01f;
1148 if (size.Z < 0.01f) size.Z = 0.01f;
1149
1150 // try to find a identical mesh on meshs in use
1151
1152 AMeshKey key = GetMeshUniqueKey(primShape,size,(byte)lod, convex);
1153
1154 lock (m_uniqueMeshes)
1155 {
1156 m_uniqueMeshes.TryGetValue(key, out mesh);
1157
1158 if (mesh != null)
1159 {
1160 mesh.RefCount++;
1161 return mesh;
1162 }
1163
1164 // try to find a identical mesh on meshs recently released
1165 lock (m_uniqueReleasedMeshes)
1166 {
1167 m_uniqueReleasedMeshes.TryGetValue(key, out mesh);
1168 if (mesh != null)
1169 {
1170 m_uniqueReleasedMeshes.Remove(key);
1171 try
1172 {
1173 m_uniqueMeshes.Add(key, mesh);
1174 }
1175 catch { }
1176 mesh.RefCount = 1;
1177 return mesh;
1178 }
1179 }
1180 }
1181
1182 Mesh UnitMesh = null;
1183 AMeshKey unitKey = GetMeshUniqueKey(primShape, m_MeshUnitSize, (byte)lod, convex);
1184
1185 lock (m_uniqueReleasedMeshes)
1186 {
1187 m_uniqueReleasedMeshes.TryGetValue(unitKey, out UnitMesh);
1188 if (UnitMesh != null)
1189 {
1190 UnitMesh.RefCount = 1;
1191 }
1192 }
1193
1194 if (UnitMesh == null && primShape.SculptEntry && doMeshFileCache)
1195 UnitMesh = GetFromFileCache(unitKey);
1196
1197 if (UnitMesh == null)
1198 {
1199 UnitMesh = CreateMeshFromPrimMesher(primName, primShape, lod, convex);
1200
1201 if (UnitMesh == null)
1202 return null;
1203
1204 UnitMesh.DumpRaw(baseDir, unitKey.ToString(), "Z");
1205
1206 if (forOde)
1207 {
1208 // force pinned mem allocation
1209 UnitMesh.PrepForOde();
1210 }
1211 else
1212 UnitMesh.TrimExcess();
1213
1214 UnitMesh.Key = unitKey;
1215 UnitMesh.RefCount = 1;
1216
1217 if (doMeshFileCache && primShape.SculptEntry)
1218 StoreToFileCache(unitKey, UnitMesh);
1219
1220 lock (m_uniqueReleasedMeshes)
1221 {
1222 try
1223 {
1224 m_uniqueReleasedMeshes.Add(unitKey, UnitMesh);
1225 }
1226 catch { }
1227 }
1228 }
1229
1230 mesh = UnitMesh.Scale(size);
1231 mesh.Key = key;
1232 mesh.RefCount = 1;
1233 lock (m_uniqueMeshes)
1234 {
1235 try
1236 {
1237 m_uniqueMeshes.Add(key, mesh);
1238 }
1239 catch { }
1240 }
1241
1242 return mesh;
1243 }
1244
1245 public void ReleaseMesh(IMesh imesh)
1246 {
1247 if (imesh == null)
1248 return;
1249
1250 Mesh mesh = (Mesh)imesh;
1251
1252 lock (m_uniqueMeshes)
1253 {
1254 int curRefCount = mesh.RefCount;
1255 curRefCount--;
1256
1257 if (curRefCount > 0)
1258 {
1259 mesh.RefCount = curRefCount;
1260 return;
1261 }
1262
1263 mesh.RefCount = 0;
1264 m_uniqueMeshes.Remove(mesh.Key);
1265 lock (m_uniqueReleasedMeshes)
1266 {
1267 try
1268 {
1269 m_uniqueReleasedMeshes.Add(mesh.Key, mesh);
1270 }
1271 catch { }
1272 }
1273 }
1274 }
1275
1276 public void ExpireReleaseMeshs()
1277 {
1278 if (m_uniqueReleasedMeshes.Count == 0)
1279 return;
1280
1281 List<Mesh> meshstodelete = new List<Mesh>();
1282 int refcntr;
1283
1284 lock (m_uniqueReleasedMeshes)
1285 {
1286 foreach (Mesh m in m_uniqueReleasedMeshes.Values)
1287 {
1288 refcntr = m.RefCount;
1289 refcntr--;
1290 if (refcntr > -6)
1291 m.RefCount = refcntr;
1292 else
1293 meshstodelete.Add(m);
1294 }
1295
1296 foreach (Mesh m in meshstodelete)
1297 {
1298 m_uniqueReleasedMeshes.Remove(m.Key);
1299 m.releaseBuildingMeshData();
1300 m.releasePinned();
1301 }
1302 }
1303 }
1304
1305 public void FileNames(AMeshKey key, out string dir,out string fullFileName)
1306 {
1307 string id = key.ToString();
1308 string init = id.Substring(0, 1);
1309 dir = System.IO.Path.Combine(cachePath, init);
1310 fullFileName = System.IO.Path.Combine(dir, id);
1311 }
1312
1313 public string FullFileName(AMeshKey key)
1314 {
1315 string id = key.ToString();
1316 string init = id.Substring(0,1);
1317 id = System.IO.Path.Combine(init, id);
1318 id = System.IO.Path.Combine(cachePath, id);
1319 return id;
1320 }
1321
1322 private Mesh GetFromFileCache(AMeshKey key)
1323 {
1324 Mesh mesh = null;
1325 string filename = FullFileName(key);
1326 bool ok = true;
1327
1328 lock (diskLock)
1329 {
1330 if (File.Exists(filename))
1331 {
1332 FileStream stream = null;
1333 try
1334 {
1335 stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
1336 BinaryFormatter bformatter = new BinaryFormatter();
1337
1338 mesh = Mesh.FromStream(stream, key);
1339
1340 }
1341 catch (Exception e)
1342 {
1343 ok = false;
1344 m_log.ErrorFormat(
1345 "[MESH CACHE]: Failed to get file {0}. Exception {1} {2}",
1346 filename, e.Message, e.StackTrace);
1347 }
1348
1349 if (stream != null)
1350 stream.Close();
1351
1352 if (mesh == null || !ok)
1353 File.Delete(filename);
1354 else
1355 File.SetLastAccessTimeUtc(filename, DateTime.UtcNow);
1356 }
1357 }
1358
1359 return mesh;
1360 }
1361
1362 private void StoreToFileCache(AMeshKey key, Mesh mesh)
1363 {
1364 Stream stream = null;
1365 bool ok = false;
1366
1367 // Make sure the target cache directory exists
1368 string dir = String.Empty;
1369 string filename = String.Empty;
1370
1371 FileNames(key, out dir, out filename);
1372
1373 lock (diskLock)
1374 {
1375 try
1376 {
1377 if (!Directory.Exists(dir))
1378 {
1379 Directory.CreateDirectory(dir);
1380 }
1381
1382 stream = File.Open(filename, FileMode.Create);
1383 ok = mesh.ToStream(stream);
1384 }
1385 catch (IOException e)
1386 {
1387 m_log.ErrorFormat(
1388 "[MESH CACHE]: Failed to write file {0}. Exception {1} {2}.",
1389 filename, e.Message, e.StackTrace);
1390 ok = false;
1391 }
1392
1393 if (stream != null)
1394 stream.Close();
1395
1396 if (File.Exists(filename))
1397 {
1398 if (ok)
1399 File.SetLastAccessTimeUtc(filename, DateTime.UtcNow);
1400 else
1401 File.Delete(filename);
1402 }
1403 }
1404 }
1405
1406 public void ExpireFileCache()
1407 {
1408 if (!doCacheExpire)
1409 return;
1410
1411 string controlfile = System.IO.Path.Combine(cachePath, "cntr");
1412
1413 lock (diskLock)
1414 {
1415 try
1416 {
1417 if (File.Exists(controlfile))
1418 {
1419 int ndeleted = 0;
1420 int totalfiles = 0;
1421 int ndirs = 0;
1422 DateTime OlderTime = File.GetLastAccessTimeUtc(controlfile) - CacheExpire;
1423 File.SetLastAccessTimeUtc(controlfile, DateTime.UtcNow);
1424
1425 foreach (string dir in Directory.GetDirectories(cachePath))
1426 {
1427 try
1428 {
1429 foreach (string file in Directory.GetFiles(dir))
1430 {
1431 try
1432 {
1433 if (File.GetLastAccessTimeUtc(file) < OlderTime)
1434 {
1435 File.Delete(file);
1436 ndeleted++;
1437 }
1438 }
1439 catch { }
1440 totalfiles++;
1441 }
1442 }
1443 catch { }
1444 ndirs++;
1445 }
1446
1447 if (ndeleted == 0)
1448 m_log.InfoFormat("[MESH CACHE]: {0} Files in {1} cache folders, no expires",
1449 totalfiles,ndirs);
1450 else
1451 m_log.InfoFormat("[MESH CACHE]: {0} Files in {1} cache folders, expired {2} files accessed before {3}",
1452 totalfiles,ndirs, ndeleted, OlderTime.ToString());
1453 }
1454 else
1455 {
1456 m_log.Info("[MESH CACHE]: Expire delayed to next startup");
1457 FileStream fs = File.Create(controlfile,4096,FileOptions.WriteThrough);
1458 fs.Close();
1459 }
1460 }
1461 catch { }
1462 }
1463 }
1464 }
1465}
diff --git a/OpenSim/Region/PhysicsModules/ubOdeMeshing/PrimMesher.cs b/OpenSim/Region/PhysicsModules/ubOdeMeshing/PrimMesher.cs
new file mode 100644
index 0000000..8eb136b
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/ubOdeMeshing/PrimMesher.cs
@@ -0,0 +1,1708 @@
1/*
2 * Copyright (c) Contributors
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Text;
31using System.IO;
32
33namespace PrimMesher
34{
35 public struct Quat
36 {
37 /// <summary>X value</summary>
38 public float X;
39 /// <summary>Y value</summary>
40 public float Y;
41 /// <summary>Z value</summary>
42 public float Z;
43 /// <summary>W value</summary>
44 public float W;
45
46 public Quat(float x, float y, float z, float w)
47 {
48 X = x;
49 Y = y;
50 Z = z;
51 W = w;
52 }
53
54 public Quat(Coord axis, float angle)
55 {
56 axis = axis.Normalize();
57
58 angle *= 0.5f;
59 float c = (float)Math.Cos(angle);
60 float s = (float)Math.Sin(angle);
61
62 X = axis.X * s;
63 Y = axis.Y * s;
64 Z = axis.Z * s;
65 W = c;
66
67 Normalize();
68 }
69
70 public float Length()
71 {
72 return (float)Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
73 }
74
75 public Quat Normalize()
76 {
77 const float MAG_THRESHOLD = 0.0000001f;
78 float mag = Length();
79
80 // Catch very small rounding errors when normalizing
81 if (mag > MAG_THRESHOLD)
82 {
83 float oomag = 1f / mag;
84 X *= oomag;
85 Y *= oomag;
86 Z *= oomag;
87 W *= oomag;
88 }
89 else
90 {
91 X = 0f;
92 Y = 0f;
93 Z = 0f;
94 W = 1f;
95 }
96
97 return this;
98 }
99
100 public static Quat operator *(Quat q1, Quat q2)
101 {
102 float x = q1.W * q2.X + q1.X * q2.W + q1.Y * q2.Z - q1.Z * q2.Y;
103 float y = q1.W * q2.Y - q1.X * q2.Z + q1.Y * q2.W + q1.Z * q2.X;
104 float z = q1.W * q2.Z + q1.X * q2.Y - q1.Y * q2.X + q1.Z * q2.W;
105 float w = q1.W * q2.W - q1.X * q2.X - q1.Y * q2.Y - q1.Z * q2.Z;
106 return new Quat(x, y, z, w);
107 }
108
109 public override string ToString()
110 {
111 return "< X: " + this.X.ToString() + ", Y: " + this.Y.ToString() + ", Z: " + this.Z.ToString() + ", W: " + this.W.ToString() + ">";
112 }
113 }
114
115 public struct Coord
116 {
117 public float X;
118 public float Y;
119 public float Z;
120
121 public Coord(float x, float y, float z)
122 {
123 this.X = x;
124 this.Y = y;
125 this.Z = z;
126 }
127
128 public float Length()
129 {
130 return (float)Math.Sqrt(this.X * this.X + this.Y * this.Y + this.Z * this.Z);
131 }
132
133 public Coord Invert()
134 {
135 this.X = -this.X;
136 this.Y = -this.Y;
137 this.Z = -this.Z;
138
139 return this;
140 }
141
142 public Coord Normalize()
143 {
144 const float MAG_THRESHOLD = 0.0000001f;
145 float mag = Length();
146
147 // Catch very small rounding errors when normalizing
148 if (mag > MAG_THRESHOLD)
149 {
150 float oomag = 1.0f / mag;
151 this.X *= oomag;
152 this.Y *= oomag;
153 this.Z *= oomag;
154 }
155 else
156 {
157 this.X = 0.0f;
158 this.Y = 0.0f;
159 this.Z = 0.0f;
160 }
161
162 return this;
163 }
164
165 public override string ToString()
166 {
167 return this.X.ToString() + " " + this.Y.ToString() + " " + this.Z.ToString();
168 }
169
170 public static Coord Cross(Coord c1, Coord c2)
171 {
172 return new Coord(
173 c1.Y * c2.Z - c2.Y * c1.Z,
174 c1.Z * c2.X - c2.Z * c1.X,
175 c1.X * c2.Y - c2.X * c1.Y
176 );
177 }
178
179 public static Coord operator +(Coord v, Coord a)
180 {
181 return new Coord(v.X + a.X, v.Y + a.Y, v.Z + a.Z);
182 }
183
184 public static Coord operator *(Coord v, Coord m)
185 {
186 return new Coord(v.X * m.X, v.Y * m.Y, v.Z * m.Z);
187 }
188
189 public static Coord operator *(Coord v, Quat q)
190 {
191 // From http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/
192
193 Coord c2 = new Coord(0.0f, 0.0f, 0.0f);
194
195 c2.X = q.W * q.W * v.X +
196 2f * q.Y * q.W * v.Z -
197 2f * q.Z * q.W * v.Y +
198 q.X * q.X * v.X +
199 2f * q.Y * q.X * v.Y +
200 2f * q.Z * q.X * v.Z -
201 q.Z * q.Z * v.X -
202 q.Y * q.Y * v.X;
203
204 c2.Y =
205 2f * q.X * q.Y * v.X +
206 q.Y * q.Y * v.Y +
207 2f * q.Z * q.Y * v.Z +
208 2f * q.W * q.Z * v.X -
209 q.Z * q.Z * v.Y +
210 q.W * q.W * v.Y -
211 2f * q.X * q.W * v.Z -
212 q.X * q.X * v.Y;
213
214 c2.Z =
215 2f * q.X * q.Z * v.X +
216 2f * q.Y * q.Z * v.Y +
217 q.Z * q.Z * v.Z -
218 2f * q.W * q.Y * v.X -
219 q.Y * q.Y * v.Z +
220 2f * q.W * q.X * v.Y -
221 q.X * q.X * v.Z +
222 q.W * q.W * v.Z;
223
224 return c2;
225 }
226 }
227
228 public struct Face
229 {
230 public int primFace;
231
232 // vertices
233 public int v1;
234 public int v2;
235 public int v3;
236
237 public Face(int v1, int v2, int v3)
238 {
239 primFace = 0;
240
241 this.v1 = v1;
242 this.v2 = v2;
243 this.v3 = v3;
244
245 }
246
247 public Coord SurfaceNormal(List<Coord> coordList)
248 {
249 Coord c1 = coordList[this.v1];
250 Coord c2 = coordList[this.v2];
251 Coord c3 = coordList[this.v3];
252
253 Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z);
254 Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z);
255
256 return Coord.Cross(edge1, edge2).Normalize();
257 }
258 }
259
260 internal struct Angle
261 {
262 internal float angle;
263 internal float X;
264 internal float Y;
265
266 internal Angle(float angle, float x, float y)
267 {
268 this.angle = angle;
269 this.X = x;
270 this.Y = y;
271 }
272 }
273
274 internal class AngleList
275 {
276 private float iX, iY; // intersection point
277
278 private static Angle[] angles3 =
279 {
280 new Angle(0.0f, 1.0f, 0.0f),
281 new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
282 new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
283 new Angle(1.0f, 1.0f, 0.0f)
284 };
285
286 private static Angle[] angles4 =
287 {
288 new Angle(0.0f, 1.0f, 0.0f),
289 new Angle(0.25f, 0.0f, 1.0f),
290 new Angle(0.5f, -1.0f, 0.0f),
291 new Angle(0.75f, 0.0f, -1.0f),
292 new Angle(1.0f, 1.0f, 0.0f)
293 };
294
295 private static Angle[] angles6 =
296 {
297 new Angle(0.0f, 1.0f, 0.0f),
298 new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f),
299 new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
300 new Angle(0.5f, -1.0f, 0.0f),
301 new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
302 new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f),
303 new Angle(1.0f, 1.0f, 0.0f)
304 };
305
306 private static Angle[] angles12 =
307 {
308 new Angle(0.0f, 1.0f, 0.0f),
309 new Angle(0.083333333333333329f, 0.86602540378443871f, 0.5f),
310 new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f),
311 new Angle(0.25f, 0.0f, 1.0f),
312 new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
313 new Angle(0.41666666666666663f, -0.86602540378443849f, 0.5f),
314 new Angle(0.5f, -1.0f, 0.0f),
315 new Angle(0.58333333333333326f, -0.86602540378443882f, -0.5f),
316 new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
317 new Angle(0.75f, 0.0f, -1.0f),
318 new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f),
319 new Angle(0.91666666666666663f, 0.86602540378443837f, -0.5f),
320 new Angle(1.0f, 1.0f, 0.0f)
321 };
322
323 private static Angle[] angles24 =
324 {
325 new Angle(0.0f, 1.0f, 0.0f),
326 new Angle(0.041666666666666664f, 0.96592582628906831f, 0.25881904510252074f),
327 new Angle(0.083333333333333329f, 0.86602540378443871f, 0.5f),
328 new Angle(0.125f, 0.70710678118654757f, 0.70710678118654746f),
329 new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f),
330 new Angle(0.20833333333333331f, 0.25881904510252096f, 0.9659258262890682f),
331 new Angle(0.25f, 0.0f, 1.0f),
332 new Angle(0.29166666666666663f, -0.25881904510252063f, 0.96592582628906831f),
333 new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
334 new Angle(0.375f, -0.70710678118654746f, 0.70710678118654757f),
335 new Angle(0.41666666666666663f, -0.86602540378443849f, 0.5f),
336 new Angle(0.45833333333333331f, -0.9659258262890682f, 0.25881904510252102f),
337 new Angle(0.5f, -1.0f, 0.0f),
338 new Angle(0.54166666666666663f, -0.96592582628906842f, -0.25881904510252035f),
339 new Angle(0.58333333333333326f, -0.86602540378443882f, -0.5f),
340 new Angle(0.62499999999999989f, -0.70710678118654791f, -0.70710678118654713f),
341 new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
342 new Angle(0.70833333333333326f, -0.25881904510252152f, -0.96592582628906809f),
343 new Angle(0.75f, 0.0f, -1.0f),
344 new Angle(0.79166666666666663f, 0.2588190451025203f, -0.96592582628906842f),
345 new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f),
346 new Angle(0.875f, 0.70710678118654735f, -0.70710678118654768f),
347 new Angle(0.91666666666666663f, 0.86602540378443837f, -0.5f),
348 new Angle(0.95833333333333326f, 0.96592582628906809f, -0.25881904510252157f),
349 new Angle(1.0f, 1.0f, 0.0f)
350 };
351
352 private Angle interpolatePoints(float newPoint, Angle p1, Angle p2)
353 {
354 float m = (newPoint - p1.angle) / (p2.angle - p1.angle);
355 return new Angle(newPoint, p1.X + m * (p2.X - p1.X), p1.Y + m * (p2.Y - p1.Y));
356 }
357
358 private void intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
359 { // ref: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
360 double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
361 double uaNumerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
362
363 if (denom != 0.0)
364 {
365 double ua = uaNumerator / denom;
366 iX = (float)(x1 + ua * (x2 - x1));
367 iY = (float)(y1 + ua * (y2 - y1));
368 }
369 }
370
371 internal List<Angle> angles;
372
373 internal void makeAngles(int sides, float startAngle, float stopAngle, bool hasCut)
374 {
375 angles = new List<Angle>();
376
377 const double twoPi = System.Math.PI * 2.0;
378 const float twoPiInv = (float)(1.0d / twoPi);
379
380 if (sides < 1)
381 throw new Exception("number of sides not greater than zero");
382 if (stopAngle <= startAngle)
383 throw new Exception("stopAngle not greater than startAngle");
384
385 if ((sides == 3 || sides == 4 || sides == 6 || sides == 12 || sides == 24))
386 {
387 startAngle *= twoPiInv;
388 stopAngle *= twoPiInv;
389
390 Angle[] sourceAngles;
391 switch (sides)
392 {
393 case 3:
394 sourceAngles = angles3;
395 break;
396 case 4:
397 sourceAngles = angles4;
398 break;
399 case 6:
400 sourceAngles = angles6;
401 break;
402 case 12:
403 sourceAngles = angles12;
404 break;
405 default:
406 sourceAngles = angles24;
407 break;
408 }
409
410 int startAngleIndex = (int)(startAngle * sides);
411 int endAngleIndex = sourceAngles.Length - 1;
412
413 if (hasCut)
414 {
415 if (stopAngle < 1.0f)
416 endAngleIndex = (int)(stopAngle * sides) + 1;
417 if (endAngleIndex == startAngleIndex)
418 endAngleIndex++;
419
420 for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex + 1; angleIndex++)
421 {
422 angles.Add(sourceAngles[angleIndex]);
423 }
424
425 if (startAngle > 0.0f)
426 angles[0] = interpolatePoints(startAngle, angles[0], angles[1]);
427
428 if (stopAngle < 1.0f)
429 {
430 int lastAngleIndex = angles.Count - 1;
431 angles[lastAngleIndex] = interpolatePoints(stopAngle, angles[lastAngleIndex - 1], angles[lastAngleIndex]);
432 }
433 }
434 else
435 {
436 for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex; angleIndex++)
437 angles.Add(sourceAngles[angleIndex]);
438 }
439 }
440 else
441 {
442 double stepSize = twoPi / sides;
443
444 int startStep = (int)(startAngle / stepSize);
445 double angle = stepSize * startStep;
446 int step = startStep;
447 double stopAngleTest = stopAngle;
448 if (stopAngle < twoPi)
449 {
450 stopAngleTest = stepSize * ((int)(stopAngle / stepSize) + 1);
451 if (stopAngleTest < stopAngle)
452 stopAngleTest += stepSize;
453 if (stopAngleTest > twoPi)
454 stopAngleTest = twoPi;
455 }
456
457 while (angle <= stopAngleTest)
458 {
459 Angle newAngle;
460 newAngle.angle = (float)angle;
461 newAngle.X = (float)System.Math.Cos(angle);
462 newAngle.Y = (float)System.Math.Sin(angle);
463 angles.Add(newAngle);
464 step += 1;
465 angle = stepSize * step;
466 }
467
468 if (startAngle > angles[0].angle)
469 {
470 Angle newAngle;
471 intersection(angles[0].X, angles[0].Y, angles[1].X, angles[1].Y, 0.0f, 0.0f, (float)Math.Cos(startAngle), (float)Math.Sin(startAngle));
472 newAngle.angle = startAngle;
473 newAngle.X = iX;
474 newAngle.Y = iY;
475 angles[0] = newAngle;
476 }
477
478 int index = angles.Count - 1;
479 if (stopAngle < angles[index].angle)
480 {
481 Angle newAngle;
482 intersection(angles[index - 1].X, angles[index - 1].Y, angles[index].X, angles[index].Y, 0.0f, 0.0f, (float)Math.Cos(stopAngle), (float)Math.Sin(stopAngle));
483 newAngle.angle = stopAngle;
484 newAngle.X = iX;
485 newAngle.Y = iY;
486 angles[index] = newAngle;
487 }
488 }
489 }
490 }
491
492 /// <summary>
493 /// generates a profile for extrusion
494 /// </summary>
495 public class Profile
496 {
497 private const float twoPi = 2.0f * (float)Math.PI;
498
499 public string errorMessage = null;
500
501 public List<Coord> coords;
502 public List<Face> faces;
503
504 // use these for making individual meshes for each prim face
505 public List<int> outerCoordIndices = null;
506 public List<int> hollowCoordIndices = null;
507
508 public int numOuterVerts = 0;
509 public int numHollowVerts = 0;
510
511 public int outerFaceNumber = -1;
512 public int hollowFaceNumber = -1;
513
514 public int bottomFaceNumber = 0;
515 public int numPrimFaces = 0;
516
517 public Profile()
518 {
519 this.coords = new List<Coord>();
520 this.faces = new List<Face>();
521 }
522
523 public Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool hasProfileCut, bool createFaces)
524 {
525 const float halfSqr2 = 0.7071067811866f;
526
527 this.coords = new List<Coord>();
528 this.faces = new List<Face>();
529
530 List<Coord> hollowCoords = new List<Coord>();
531
532 bool hasHollow = (hollow > 0.0f);
533
534 AngleList angles = new AngleList();
535 AngleList hollowAngles = new AngleList();
536
537 float xScale = 0.5f;
538 float yScale = 0.5f;
539 if (sides == 4) // corners of a square are sqrt(2) from center
540 {
541 xScale = halfSqr2;
542 yScale = halfSqr2;
543 }
544
545 float startAngle = profileStart * twoPi;
546 float stopAngle = profileEnd * twoPi;
547
548 try { angles.makeAngles(sides, startAngle, stopAngle,hasProfileCut); }
549 catch (Exception ex)
550 {
551
552 errorMessage = "makeAngles failed: Exception: " + ex.ToString()
553 + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString();
554
555 return;
556 }
557
558 this.numOuterVerts = angles.angles.Count;
559
560 Angle angle;
561 Coord newVert = new Coord();
562
563 // flag to create as few triangles as possible for 3 or 4 side profile
564 bool simpleFace = (sides < 5 && !hasHollow && !hasProfileCut);
565
566 if (hasHollow)
567 {
568 if (sides == hollowSides)
569 hollowAngles = angles;
570 else
571 {
572 try { hollowAngles.makeAngles(hollowSides, startAngle, stopAngle, hasProfileCut); }
573 catch (Exception ex)
574 {
575 errorMessage = "makeAngles failed: Exception: " + ex.ToString()
576 + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString();
577
578 return;
579 }
580
581 int numHollowAngles = hollowAngles.angles.Count;
582 for (int i = 0; i < numHollowAngles; i++)
583 {
584 angle = hollowAngles.angles[i];
585 newVert.X = hollow * xScale * angle.X;
586 newVert.Y = hollow * yScale * angle.Y;
587 newVert.Z = 0.0f;
588
589 hollowCoords.Add(newVert);
590 }
591 }
592 this.numHollowVerts = hollowAngles.angles.Count;
593 }
594 else if (!simpleFace)
595 {
596 Coord center = new Coord(0.0f, 0.0f, 0.0f);
597 this.coords.Add(center);
598 }
599
600 int numAngles = angles.angles.Count;
601 bool hollowsame = (hasHollow && hollowSides == sides);
602
603 for (int i = 0; i < numAngles; i++)
604 {
605 angle = angles.angles[i];
606 newVert.X = angle.X * xScale;
607 newVert.Y = angle.Y * yScale;
608 newVert.Z = 0.0f;
609 this.coords.Add(newVert);
610 if (hollowsame)
611 {
612 newVert.X *= hollow;
613 newVert.Y *= hollow;
614 hollowCoords.Add(newVert);
615 }
616 }
617
618 if (hasHollow)
619 {
620 hollowCoords.Reverse();
621 this.coords.AddRange(hollowCoords);
622
623 if (createFaces)
624 {
625 int numTotalVerts = this.numOuterVerts + this.numHollowVerts;
626
627 if (this.numOuterVerts == this.numHollowVerts)
628 {
629 Face newFace = new Face();
630
631 for (int coordIndex = 0; coordIndex < this.numOuterVerts - 1; coordIndex++)
632 {
633 newFace.v1 = coordIndex;
634 newFace.v2 = coordIndex + 1;
635 newFace.v3 = numTotalVerts - coordIndex - 1;
636 this.faces.Add(newFace);
637
638 newFace.v1 = coordIndex + 1;
639 newFace.v2 = numTotalVerts - coordIndex - 2;
640 newFace.v3 = numTotalVerts - coordIndex - 1;
641 this.faces.Add(newFace);
642 }
643 if (!hasProfileCut)
644 {
645 newFace.v1 = this.numOuterVerts - 1;
646 newFace.v2 = 0;
647 newFace.v3 = this.numOuterVerts;
648 this.faces.Add(newFace);
649
650 newFace.v1 = 0;
651 newFace.v2 = numTotalVerts - 1;
652 newFace.v3 = this.numOuterVerts;
653 this.faces.Add(newFace);
654 }
655 }
656 else if (this.numOuterVerts < this.numHollowVerts)
657 {
658 Face newFace = new Face();
659 int j = 0; // j is the index for outer vertices
660 int i;
661 int maxJ = this.numOuterVerts - 1;
662 float curHollowAngle = 0;
663 for (i = 0; i < this.numHollowVerts; i++) // i is the index for inner vertices
664 {
665 curHollowAngle = hollowAngles.angles[i].angle;
666 if (j < maxJ)
667 {
668 if (angles.angles[j + 1].angle - curHollowAngle < curHollowAngle - angles.angles[j].angle + 0.000001f)
669 {
670 newFace.v1 = numTotalVerts - i - 1;
671 newFace.v2 = j;
672 newFace.v3 = j + 1;
673 this.faces.Add(newFace);
674 j++;
675 }
676 }
677 else
678 {
679 if (1.0f - curHollowAngle < curHollowAngle - angles.angles[j].angle + 0.000001f)
680 break;
681 }
682
683 newFace.v1 = j;
684 newFace.v2 = numTotalVerts - i - 2;
685 newFace.v3 = numTotalVerts - i - 1;
686
687 this.faces.Add(newFace);
688 }
689
690 if (!hasProfileCut)
691 {
692 if (i == this.numHollowVerts)
693 {
694 newFace.v1 = numTotalVerts - this.numHollowVerts;
695 newFace.v2 = maxJ;
696 newFace.v3 = 0;
697
698 this.faces.Add(newFace);
699 }
700 else
701 {
702 if (1.0f - curHollowAngle < curHollowAngle - angles.angles[maxJ].angle + 0.000001f)
703 {
704 newFace.v1 = numTotalVerts - i - 1;
705 newFace.v2 = maxJ;
706 newFace.v3 = 0;
707
708 this.faces.Add(newFace);
709 }
710
711 for (; i < this.numHollowVerts - 1; i++)
712 {
713 newFace.v1 = 0;
714 newFace.v2 = numTotalVerts - i - 2;
715 newFace.v3 = numTotalVerts - i - 1;
716
717 this.faces.Add(newFace);
718 }
719 }
720
721 newFace.v1 = 0;
722 newFace.v2 = numTotalVerts - this.numHollowVerts;
723 newFace.v3 = numTotalVerts - 1;
724 this.faces.Add(newFace);
725 }
726 }
727 else // numHollowVerts < numOuterVerts
728 {
729 Face newFace = new Face();
730 int j = 0; // j is the index for inner vertices
731 int maxJ = this.numHollowVerts - 1;
732 for (int i = 0; i < this.numOuterVerts; i++)
733 {
734 if (j < maxJ)
735 if (hollowAngles.angles[j + 1].angle - angles.angles[i].angle < angles.angles[i].angle - hollowAngles.angles[j].angle + 0.000001f)
736 {
737 newFace.v1 = i;
738 newFace.v2 = numTotalVerts - j - 2;
739 newFace.v3 = numTotalVerts - j - 1;
740
741 this.faces.Add(newFace);
742 j += 1;
743 }
744
745 newFace.v1 = numTotalVerts - j - 1;
746 newFace.v2 = i;
747 newFace.v3 = i + 1;
748
749 this.faces.Add(newFace);
750 }
751
752 if (!hasProfileCut)
753 {
754 int i = this.numOuterVerts - 1;
755
756 if (hollowAngles.angles[0].angle - angles.angles[i].angle < angles.angles[i].angle - hollowAngles.angles[maxJ].angle + 0.000001f)
757 {
758 newFace.v1 = 0;
759 newFace.v2 = numTotalVerts - maxJ - 1;
760 newFace.v3 = numTotalVerts - 1;
761
762 this.faces.Add(newFace);
763 }
764
765 newFace.v1 = numTotalVerts - maxJ - 1;
766 newFace.v2 = i;
767 newFace.v3 = 0;
768
769 this.faces.Add(newFace);
770 }
771 }
772 }
773
774 }
775
776 else if (createFaces)
777 {
778 if (simpleFace)
779 {
780 if (sides == 3)
781 this.faces.Add(new Face(0, 1, 2));
782 else if (sides == 4)
783 {
784 this.faces.Add(new Face(0, 1, 2));
785 this.faces.Add(new Face(0, 2, 3));
786 }
787 }
788 else
789 {
790 for (int i = 1; i < numAngles ; i++)
791 {
792 Face newFace = new Face();
793 newFace.v1 = 0;
794 newFace.v2 = i;
795 newFace.v3 = i + 1;
796 this.faces.Add(newFace);
797 }
798 if (!hasProfileCut)
799 {
800 Face newFace = new Face();
801 newFace.v1 = 0;
802 newFace.v2 = numAngles;
803 newFace.v3 = 1;
804 this.faces.Add(newFace);
805 }
806 }
807 }
808
809
810 hollowCoords = null;
811 }
812
813
814 public Profile Copy()
815 {
816 return this.Copy(true);
817 }
818
819 public Profile Copy(bool needFaces)
820 {
821 Profile copy = new Profile();
822
823 copy.coords.AddRange(this.coords);
824
825 if (needFaces)
826 copy.faces.AddRange(this.faces);
827
828 copy.numOuterVerts = this.numOuterVerts;
829 copy.numHollowVerts = this.numHollowVerts;
830
831 return copy;
832 }
833
834 public void AddPos(Coord v)
835 {
836 this.AddPos(v.X, v.Y, v.Z);
837 }
838
839 public void AddPos(float x, float y, float z)
840 {
841 int i;
842 int numVerts = this.coords.Count;
843 Coord vert;
844
845 for (i = 0; i < numVerts; i++)
846 {
847 vert = this.coords[i];
848 vert.X += x;
849 vert.Y += y;
850 vert.Z += z;
851 this.coords[i] = vert;
852 }
853 }
854
855 public void AddRot(Quat q)
856 {
857 int i;
858 int numVerts = this.coords.Count;
859
860 for (i = 0; i < numVerts; i++)
861 this.coords[i] *= q;
862 }
863
864 public void Scale(float x, float y)
865 {
866 int i;
867 int numVerts = this.coords.Count;
868 Coord vert;
869
870 for (i = 0; i < numVerts; i++)
871 {
872 vert = this.coords[i];
873 vert.X *= x;
874 vert.Y *= y;
875 this.coords[i] = vert;
876 }
877 }
878
879 /// <summary>
880 /// Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex normals of radial vertices
881 /// </summary>
882 public void FlipNormals()
883 {
884 int i;
885 int numFaces = this.faces.Count;
886 Face tmpFace;
887 int tmp;
888
889 for (i = 0; i < numFaces; i++)
890 {
891 tmpFace = this.faces[i];
892 tmp = tmpFace.v3;
893 tmpFace.v3 = tmpFace.v1;
894 tmpFace.v1 = tmp;
895 this.faces[i] = tmpFace;
896 }
897 }
898
899 public void AddValue2FaceVertexIndices(int num)
900 {
901 int numFaces = this.faces.Count;
902 Face tmpFace;
903 for (int i = 0; i < numFaces; i++)
904 {
905 tmpFace = this.faces[i];
906 tmpFace.v1 += num;
907 tmpFace.v2 += num;
908 tmpFace.v3 += num;
909
910 this.faces[i] = tmpFace;
911 }
912 }
913
914 public void DumpRaw(String path, String name, String title)
915 {
916 if (path == null)
917 return;
918 String fileName = name + "_" + title + ".raw";
919 String completePath = System.IO.Path.Combine(path, fileName);
920 StreamWriter sw = new StreamWriter(completePath);
921
922 for (int i = 0; i < this.faces.Count; i++)
923 {
924 string s = this.coords[this.faces[i].v1].ToString();
925 s += " " + this.coords[this.faces[i].v2].ToString();
926 s += " " + this.coords[this.faces[i].v3].ToString();
927
928 sw.WriteLine(s);
929 }
930
931 sw.Close();
932 }
933 }
934
935 public struct PathNode
936 {
937 public Coord position;
938 public Quat rotation;
939 public float xScale;
940 public float yScale;
941 public float percentOfPath;
942 }
943
944 public enum PathType { Linear = 0, Circular = 1, Flexible = 2 }
945
946 public class Path
947 {
948 public List<PathNode> pathNodes = new List<PathNode>();
949
950 public float twistBegin = 0.0f;
951 public float twistEnd = 0.0f;
952 public float topShearX = 0.0f;
953 public float topShearY = 0.0f;
954 public float pathCutBegin = 0.0f;
955 public float pathCutEnd = 1.0f;
956 public float dimpleBegin = 0.0f;
957 public float dimpleEnd = 1.0f;
958 public float skew = 0.0f;
959 public float holeSizeX = 1.0f; // called pathScaleX in pbs
960 public float holeSizeY = 0.25f;
961 public float taperX = 0.0f;
962 public float taperY = 0.0f;
963 public float radius = 0.0f;
964 public float revolutions = 1.0f;
965 public int stepsPerRevolution = 24;
966
967 private const float twoPi = 2.0f * (float)Math.PI;
968
969 public void Create(PathType pathType, int steps)
970 {
971 if (this.taperX > 0.999f)
972 this.taperX = 0.999f;
973 if (this.taperX < -0.999f)
974 this.taperX = -0.999f;
975 if (this.taperY > 0.999f)
976 this.taperY = 0.999f;
977 if (this.taperY < -0.999f)
978 this.taperY = -0.999f;
979
980 if (pathType == PathType.Linear || pathType == PathType.Flexible)
981 {
982 int step = 0;
983
984 float length = this.pathCutEnd - this.pathCutBegin;
985 float twistTotal = twistEnd - twistBegin;
986 float twistTotalAbs = Math.Abs(twistTotal);
987 if (twistTotalAbs > 0.01f)
988 steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number
989
990 float start = -0.5f;
991 float stepSize = length / (float)steps;
992 float percentOfPathMultiplier = stepSize * 0.999999f;
993 float xOffset = this.topShearX * this.pathCutBegin;
994 float yOffset = this.topShearY * this.pathCutBegin;
995 float zOffset = start;
996 float xOffsetStepIncrement = this.topShearX * length / steps;
997 float yOffsetStepIncrement = this.topShearY * length / steps;
998
999 float percentOfPath = this.pathCutBegin;
1000 zOffset += percentOfPath;
1001
1002 // sanity checks
1003
1004 bool done = false;
1005
1006 while (!done)
1007 {
1008 PathNode newNode = new PathNode();
1009
1010 newNode.xScale = 1.0f;
1011 if (this.taperX == 0.0f)
1012 newNode.xScale = 1.0f;
1013 else if (this.taperX > 0.0f)
1014 newNode.xScale = 1.0f - percentOfPath * this.taperX;
1015 else newNode.xScale = 1.0f + (1.0f - percentOfPath) * this.taperX;
1016
1017 newNode.yScale = 1.0f;
1018 if (this.taperY == 0.0f)
1019 newNode.yScale = 1.0f;
1020 else if (this.taperY > 0.0f)
1021 newNode.yScale = 1.0f - percentOfPath * this.taperY;
1022 else newNode.yScale = 1.0f + (1.0f - percentOfPath) * this.taperY;
1023
1024 float twist = twistBegin + twistTotal * percentOfPath;
1025
1026 newNode.rotation = new Quat(new Coord(0.0f, 0.0f, 1.0f), twist);
1027 newNode.position = new Coord(xOffset, yOffset, zOffset);
1028 newNode.percentOfPath = percentOfPath;
1029
1030 pathNodes.Add(newNode);
1031
1032 if (step < steps)
1033 {
1034 step += 1;
1035 percentOfPath += percentOfPathMultiplier;
1036 xOffset += xOffsetStepIncrement;
1037 yOffset += yOffsetStepIncrement;
1038 zOffset += stepSize;
1039 if (percentOfPath > this.pathCutEnd)
1040 done = true;
1041 }
1042 else done = true;
1043 }
1044 } // end of linear path code
1045
1046 else // pathType == Circular
1047 {
1048 float twistTotal = twistEnd - twistBegin;
1049
1050 // if the profile has a lot of twist, add more layers otherwise the layers may overlap
1051 // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't
1052 // accurately match the viewer
1053 float twistTotalAbs = Math.Abs(twistTotal);
1054 if (twistTotalAbs > 0.01f)
1055 {
1056 if (twistTotalAbs > Math.PI * 1.5f)
1057 steps *= 2;
1058 if (twistTotalAbs > Math.PI * 3.0f)
1059 steps *= 2;
1060 }
1061
1062 float yPathScale = this.holeSizeY * 0.5f;
1063 float pathLength = this.pathCutEnd - this.pathCutBegin;
1064 float totalSkew = this.skew * 2.0f * pathLength;
1065 float skewStart = this.pathCutBegin * 2.0f * this.skew - this.skew;
1066 float xOffsetTopShearXFactor = this.topShearX * (0.25f + 0.5f * (0.5f - this.holeSizeY));
1067 float yShearCompensation = 1.0f + Math.Abs(this.topShearY) * 0.25f;
1068
1069 // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end
1070 // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used
1071 // to calculate the sine for generating the path radius appears to approximate it's effects there
1072 // too, but there are some subtle differences in the radius which are noticeable as the prim size
1073 // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on
1074 // the meshes generated with this technique appear nearly identical in shape to the same prims when
1075 // displayed by the viewer.
1076
1077 float startAngle = (twoPi * this.pathCutBegin * this.revolutions) - this.topShearY * 0.9f;
1078 float endAngle = (twoPi * this.pathCutEnd * this.revolutions) - this.topShearY * 0.9f;
1079 float stepSize = twoPi / this.stepsPerRevolution;
1080
1081 int step = (int)(startAngle / stepSize);
1082 float angle = startAngle;
1083
1084 bool done = false;
1085 while (!done) // loop through the length of the path and add the layers
1086 {
1087 PathNode newNode = new PathNode();
1088
1089 float xProfileScale = (1.0f - Math.Abs(this.skew)) * this.holeSizeX;
1090 float yProfileScale = this.holeSizeY;
1091
1092 float percentOfPath = angle / (twoPi * this.revolutions);
1093 float percentOfAngles = (angle - startAngle) / (endAngle - startAngle);
1094
1095 if (this.taperX > 0.01f)
1096 xProfileScale *= 1.0f - percentOfPath * this.taperX;
1097 else if (this.taperX < -0.01f)
1098 xProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperX;
1099
1100 if (this.taperY > 0.01f)
1101 yProfileScale *= 1.0f - percentOfPath * this.taperY;
1102 else if (this.taperY < -0.01f)
1103 yProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperY;
1104
1105 newNode.xScale = xProfileScale;
1106 newNode.yScale = yProfileScale;
1107
1108 float radiusScale = 1.0f;
1109 if (this.radius > 0.001f)
1110 radiusScale = 1.0f - this.radius * percentOfPath;
1111 else if (this.radius < 0.001f)
1112 radiusScale = 1.0f + this.radius * (1.0f - percentOfPath);
1113
1114 float twist = twistBegin + twistTotal * percentOfPath;
1115
1116 float xOffset = 0.5f * (skewStart + totalSkew * percentOfAngles);
1117 xOffset += (float)Math.Sin(angle) * xOffsetTopShearXFactor;
1118
1119 float yOffset = yShearCompensation * (float)Math.Cos(angle) * (0.5f - yPathScale) * radiusScale;
1120
1121 float zOffset = (float)Math.Sin(angle + this.topShearY) * (0.5f - yPathScale) * radiusScale;
1122
1123 newNode.position = new Coord(xOffset, yOffset, zOffset);
1124
1125 // now orient the rotation of the profile layer relative to it's position on the path
1126 // adding taperY to the angle used to generate the quat appears to approximate the viewer
1127
1128 newNode.rotation = new Quat(new Coord(1.0f, 0.0f, 0.0f), angle + this.topShearY);
1129
1130 // next apply twist rotation to the profile layer
1131 if (twistTotal != 0.0f || twistBegin != 0.0f)
1132 newNode.rotation *= new Quat(new Coord(0.0f, 0.0f, 1.0f), twist);
1133
1134 newNode.percentOfPath = percentOfPath;
1135
1136 pathNodes.Add(newNode);
1137
1138 // calculate terms for next iteration
1139 // calculate the angle for the next iteration of the loop
1140
1141 if (angle >= endAngle - 0.01)
1142 done = true;
1143 else
1144 {
1145 step += 1;
1146 angle = stepSize * step;
1147 if (angle > endAngle)
1148 angle = endAngle;
1149 }
1150 }
1151 }
1152 }
1153 }
1154
1155 public class PrimMesh
1156 {
1157 public string errorMessage = "";
1158 private const float twoPi = 2.0f * (float)Math.PI;
1159
1160 public List<Coord> coords;
1161// public List<Coord> normals;
1162 public List<Face> faces;
1163
1164 private int sides = 4;
1165 private int hollowSides = 4;
1166 private float profileStart = 0.0f;
1167 private float profileEnd = 1.0f;
1168 private float hollow = 0.0f;
1169 public int twistBegin = 0;
1170 public int twistEnd = 0;
1171 public float topShearX = 0.0f;
1172 public float topShearY = 0.0f;
1173 public float pathCutBegin = 0.0f;
1174 public float pathCutEnd = 1.0f;
1175 public float dimpleBegin = 0.0f;
1176 public float dimpleEnd = 1.0f;
1177 public float skew = 0.0f;
1178 public float holeSizeX = 1.0f; // called pathScaleX in pbs
1179 public float holeSizeY = 0.25f;
1180 public float taperX = 0.0f;
1181 public float taperY = 0.0f;
1182 public float radius = 0.0f;
1183 public float revolutions = 1.0f;
1184 public int stepsPerRevolution = 24;
1185
1186 private bool hasProfileCut = false;
1187 private bool hasHollow = false;
1188
1189 public int numPrimFaces = 0;
1190
1191 /// <summary>
1192 /// Human readable string representation of the parameters used to create a mesh.
1193 /// </summary>
1194 /// <returns></returns>
1195 public string ParamsToDisplayString()
1196 {
1197 string s = "";
1198 s += "sides..................: " + this.sides.ToString();
1199 s += "\nhollowSides..........: " + this.hollowSides.ToString();
1200 s += "\nprofileStart.........: " + this.profileStart.ToString();
1201 s += "\nprofileEnd...........: " + this.profileEnd.ToString();
1202 s += "\nhollow...............: " + this.hollow.ToString();
1203 s += "\ntwistBegin...........: " + this.twistBegin.ToString();
1204 s += "\ntwistEnd.............: " + this.twistEnd.ToString();
1205 s += "\ntopShearX............: " + this.topShearX.ToString();
1206 s += "\ntopShearY............: " + this.topShearY.ToString();
1207 s += "\npathCutBegin.........: " + this.pathCutBegin.ToString();
1208 s += "\npathCutEnd...........: " + this.pathCutEnd.ToString();
1209 s += "\ndimpleBegin..........: " + this.dimpleBegin.ToString();
1210 s += "\ndimpleEnd............: " + this.dimpleEnd.ToString();
1211 s += "\nskew.................: " + this.skew.ToString();
1212 s += "\nholeSizeX............: " + this.holeSizeX.ToString();
1213 s += "\nholeSizeY............: " + this.holeSizeY.ToString();
1214 s += "\ntaperX...............: " + this.taperX.ToString();
1215 s += "\ntaperY...............: " + this.taperY.ToString();
1216 s += "\nradius...............: " + this.radius.ToString();
1217 s += "\nrevolutions..........: " + this.revolutions.ToString();
1218 s += "\nstepsPerRevolution...: " + this.stepsPerRevolution.ToString();
1219 s += "\nhasProfileCut........: " + this.hasProfileCut.ToString();
1220 s += "\nhasHollow............: " + this.hasHollow.ToString();
1221
1222 return s;
1223 }
1224
1225 public bool HasProfileCut
1226 {
1227 get { return hasProfileCut; }
1228 set { hasProfileCut = value; }
1229 }
1230
1231 public bool HasHollow
1232 {
1233 get { return hasHollow; }
1234 }
1235
1236
1237 /// <summary>
1238 /// Constructs a PrimMesh object and creates the profile for extrusion.
1239 /// </summary>
1240 /// <param name="sides"></param>
1241 /// <param name="profileStart"></param>
1242 /// <param name="profileEnd"></param>
1243 /// <param name="hollow"></param>
1244 /// <param name="hollowSides"></param>
1245 /// <param name="sphereMode"></param>
1246 public PrimMesh(int sides, float profileStart, float profileEnd, float hollow, int hollowSides)
1247 {
1248 this.coords = new List<Coord>();
1249 this.faces = new List<Face>();
1250
1251 this.sides = sides;
1252 this.profileStart = profileStart;
1253 this.profileEnd = profileEnd;
1254 this.hollow = hollow;
1255 this.hollowSides = hollowSides;
1256
1257 if (sides < 3)
1258 this.sides = 3;
1259 if (hollowSides < 3)
1260 this.hollowSides = 3;
1261 if (profileStart < 0.0f)
1262 this.profileStart = 0.0f;
1263 if (profileEnd > 1.0f)
1264 this.profileEnd = 1.0f;
1265 if (profileEnd < 0.02f)
1266 this.profileEnd = 0.02f;
1267 if (profileStart >= profileEnd)
1268 this.profileStart = profileEnd - 0.02f;
1269 if (hollow > 0.99f)
1270 this.hollow = 0.99f;
1271 if (hollow < 0.0f)
1272 this.hollow = 0.0f;
1273 }
1274
1275 /// <summary>
1276 /// Extrudes a profile along a path.
1277 /// </summary>
1278 public void Extrude(PathType pathType)
1279 {
1280 bool needEndFaces = false;
1281
1282 this.coords = new List<Coord>();
1283 this.faces = new List<Face>();
1284
1285 int steps = 1;
1286
1287 float length = this.pathCutEnd - this.pathCutBegin;
1288
1289 this.hasProfileCut = this.profileEnd - this.profileStart < 0.9999f;
1290
1291 this.hasHollow = (this.hollow > 0.001f);
1292
1293 float twistBegin = this.twistBegin / 360.0f * twoPi;
1294 float twistEnd = this.twistEnd / 360.0f * twoPi;
1295 float twistTotal = twistEnd - twistBegin;
1296 float twistTotalAbs = Math.Abs(twistTotal);
1297 if (twistTotalAbs > 0.01f)
1298 steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number
1299
1300 float hollow = this.hollow;
1301
1302 if (pathType == PathType.Circular)
1303 {
1304 needEndFaces = false;
1305 if (this.pathCutBegin != 0.0f || this.pathCutEnd != 1.0f)
1306 needEndFaces = true;
1307 else if (this.taperX != 0.0f || this.taperY != 0.0f)
1308 needEndFaces = true;
1309 else if (this.skew != 0.0f)
1310 needEndFaces = true;
1311 else if (twistTotal != 0.0f)
1312 needEndFaces = true;
1313 else if (this.radius != 0.0f)
1314 needEndFaces = true;
1315 }
1316 else needEndFaces = true;
1317
1318 // sanity checks
1319 float initialProfileRot = 0.0f;
1320 if (pathType == PathType.Circular)
1321 {
1322 if (this.sides == 3)
1323 {
1324 initialProfileRot = (float)Math.PI;
1325 if (this.hollowSides == 4)
1326 {
1327 if (hollow > 0.7f)
1328 hollow = 0.7f;
1329 hollow *= 0.707f;
1330 }
1331 else hollow *= 0.5f;
1332 }
1333 else if (this.sides == 4)
1334 {
1335 initialProfileRot = 0.25f * (float)Math.PI;
1336 if (this.hollowSides != 4)
1337 hollow *= 0.707f;
1338 }
1339 else if (this.sides > 4)
1340 {
1341 initialProfileRot = (float)Math.PI;
1342 if (this.hollowSides == 4)
1343 {
1344 if (hollow > 0.7f)
1345 hollow = 0.7f;
1346 hollow /= 0.7f;
1347 }
1348 }
1349 }
1350 else
1351 {
1352 if (this.sides == 3)
1353 {
1354 if (this.hollowSides == 4)
1355 {
1356 if (hollow > 0.7f)
1357 hollow = 0.7f;
1358 hollow *= 0.707f;
1359 }
1360 else hollow *= 0.5f;
1361 }
1362 else if (this.sides == 4)
1363 {
1364 initialProfileRot = 1.25f * (float)Math.PI;
1365 if (this.hollowSides != 4)
1366 hollow *= 0.707f;
1367 }
1368 else if (this.sides == 24 && this.hollowSides == 4)
1369 hollow *= 1.414f;
1370 }
1371
1372 Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, this.hasProfileCut,true);
1373 this.errorMessage = profile.errorMessage;
1374
1375 this.numPrimFaces = profile.numPrimFaces;
1376
1377 if (initialProfileRot != 0.0f)
1378 {
1379 profile.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), initialProfileRot));
1380 }
1381
1382 float thisV = 0.0f;
1383 float lastV = 0.0f;
1384
1385 Path path = new Path();
1386 path.twistBegin = twistBegin;
1387 path.twistEnd = twistEnd;
1388 path.topShearX = topShearX;
1389 path.topShearY = topShearY;
1390 path.pathCutBegin = pathCutBegin;
1391 path.pathCutEnd = pathCutEnd;
1392 path.dimpleBegin = dimpleBegin;
1393 path.dimpleEnd = dimpleEnd;
1394 path.skew = skew;
1395 path.holeSizeX = holeSizeX;
1396 path.holeSizeY = holeSizeY;
1397 path.taperX = taperX;
1398 path.taperY = taperY;
1399 path.radius = radius;
1400 path.revolutions = revolutions;
1401 path.stepsPerRevolution = stepsPerRevolution;
1402
1403 path.Create(pathType, steps);
1404
1405 int lastNode = path.pathNodes.Count -1;
1406
1407 for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++)
1408 {
1409 PathNode node = path.pathNodes[nodeIndex];
1410 Profile newLayer = profile.Copy();
1411
1412 newLayer.Scale(node.xScale, node.yScale);
1413 newLayer.AddRot(node.rotation);
1414 newLayer.AddPos(node.position);
1415
1416 if (needEndFaces && nodeIndex == 0)
1417 {
1418 newLayer.FlipNormals();
1419 } // if (nodeIndex == 0)
1420
1421 // append this layer
1422
1423 int coordsLen = this.coords.Count;
1424 newLayer.AddValue2FaceVertexIndices(coordsLen);
1425
1426 this.coords.AddRange(newLayer.coords);
1427
1428 if (needEndFaces)
1429 {
1430 if (nodeIndex == 0)
1431 this.faces.AddRange(newLayer.faces);
1432 else if (nodeIndex == lastNode)
1433 {
1434 if (node.xScale > 1e-6 && node.yScale > 1e-6)
1435 this.faces.AddRange(newLayer.faces);
1436 }
1437 }
1438
1439 // fill faces between layers
1440
1441 int numVerts = newLayer.coords.Count;
1442 Face newFace1 = new Face();
1443 Face newFace2 = new Face();
1444
1445 thisV = 1.0f - node.percentOfPath;
1446
1447 if (nodeIndex > 0)
1448 {
1449 int startVert = coordsLen;
1450 int endVert = this.coords.Count;
1451 if (!this.hasProfileCut)
1452 {
1453 int i = startVert;
1454 for (int l = 0; l < profile.numOuterVerts - 1; l++)
1455 {
1456 newFace1.v1 = i;
1457 newFace1.v2 = i - numVerts;
1458 newFace1.v3 = i + 1;
1459 this.faces.Add(newFace1);
1460
1461 newFace2.v1 = i + 1;
1462 newFace2.v2 = i - numVerts;
1463 newFace2.v3 = i + 1 - numVerts;
1464 this.faces.Add(newFace2);
1465 i++;
1466 }
1467
1468 newFace1.v1 = i;
1469 newFace1.v2 = i - numVerts;
1470 newFace1.v3 = startVert;
1471 this.faces.Add(newFace1);
1472
1473 newFace2.v1 = startVert;
1474 newFace2.v2 = i - numVerts;
1475 newFace2.v3 = startVert - numVerts;
1476 this.faces.Add(newFace2);
1477
1478 if (this.hasHollow)
1479 {
1480 startVert = ++i;
1481 for (int l = 0; l < profile.numHollowVerts - 1; l++)
1482 {
1483 newFace1.v1 = i;
1484 newFace1.v2 = i - numVerts;
1485 newFace1.v3 = i + 1;
1486 this.faces.Add(newFace1);
1487
1488 newFace2.v1 = i + 1;
1489 newFace2.v2 = i - numVerts;
1490 newFace2.v3 = i + 1 - numVerts;
1491 this.faces.Add(newFace2);
1492 i++;
1493 }
1494
1495 newFace1.v1 = i;
1496 newFace1.v2 = i - numVerts;
1497 newFace1.v3 = startVert;
1498 this.faces.Add(newFace1);
1499
1500 newFace2.v1 = startVert;
1501 newFace2.v2 = i - numVerts;
1502 newFace2.v3 = startVert - numVerts;
1503 this.faces.Add(newFace2);
1504 }
1505
1506
1507 }
1508 else
1509 {
1510 for (int i = startVert; i < endVert; i++)
1511 {
1512 int iNext = i + 1;
1513 if (i == endVert - 1)
1514 iNext = startVert;
1515
1516 newFace1.v1 = i;
1517 newFace1.v2 = i - numVerts;
1518 newFace1.v3 = iNext;
1519 this.faces.Add(newFace1);
1520
1521 newFace2.v1 = iNext;
1522 newFace2.v2 = i - numVerts;
1523 newFace2.v3 = iNext - numVerts;
1524 this.faces.Add(newFace2);
1525
1526 }
1527 }
1528 }
1529
1530 lastV = thisV;
1531
1532 } // for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++)
1533
1534 }
1535
1536
1537 /// <summary>
1538 /// DEPRICATED - use Extrude(PathType.Linear) instead
1539 /// Extrudes a profile along a straight line path. Used for prim types box, cylinder, and prism.
1540 /// </summary>
1541 ///
1542 public void ExtrudeLinear()
1543 {
1544 this.Extrude(PathType.Linear);
1545 }
1546
1547
1548 /// <summary>
1549 /// DEPRICATED - use Extrude(PathType.Circular) instead
1550 /// Extrude a profile into a circular path prim mesh. Used for prim types torus, tube, and ring.
1551 /// </summary>
1552 ///
1553 public void ExtrudeCircular()
1554 {
1555 this.Extrude(PathType.Circular);
1556 }
1557
1558
1559 private Coord SurfaceNormal(Coord c1, Coord c2, Coord c3)
1560 {
1561 Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z);
1562 Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z);
1563
1564 Coord normal = Coord.Cross(edge1, edge2);
1565
1566 normal.Normalize();
1567
1568 return normal;
1569 }
1570
1571 private Coord SurfaceNormal(Face face)
1572 {
1573 return SurfaceNormal(this.coords[face.v1], this.coords[face.v2], this.coords[face.v3]);
1574 }
1575
1576 /// <summary>
1577 /// Calculate the surface normal for a face in the list of faces
1578 /// </summary>
1579 /// <param name="faceIndex"></param>
1580 /// <returns></returns>
1581 public Coord SurfaceNormal(int faceIndex)
1582 {
1583 int numFaces = this.faces.Count;
1584 if (faceIndex < 0 || faceIndex >= numFaces)
1585 throw new Exception("faceIndex out of range");
1586
1587 return SurfaceNormal(this.faces[faceIndex]);
1588 }
1589
1590 /// <summary>
1591 /// Duplicates a PrimMesh object. All object properties are copied by value, including lists.
1592 /// </summary>
1593 /// <returns></returns>
1594 public PrimMesh Copy()
1595 {
1596 PrimMesh copy = new PrimMesh(this.sides, this.profileStart, this.profileEnd, this.hollow, this.hollowSides);
1597 copy.twistBegin = this.twistBegin;
1598 copy.twistEnd = this.twistEnd;
1599 copy.topShearX = this.topShearX;
1600 copy.topShearY = this.topShearY;
1601 copy.pathCutBegin = this.pathCutBegin;
1602 copy.pathCutEnd = this.pathCutEnd;
1603 copy.dimpleBegin = this.dimpleBegin;
1604 copy.dimpleEnd = this.dimpleEnd;
1605 copy.skew = this.skew;
1606 copy.holeSizeX = this.holeSizeX;
1607 copy.holeSizeY = this.holeSizeY;
1608 copy.taperX = this.taperX;
1609 copy.taperY = this.taperY;
1610 copy.radius = this.radius;
1611 copy.revolutions = this.revolutions;
1612 copy.stepsPerRevolution = this.stepsPerRevolution;
1613
1614 copy.numPrimFaces = this.numPrimFaces;
1615 copy.errorMessage = this.errorMessage;
1616
1617 copy.coords = new List<Coord>(this.coords);
1618 copy.faces = new List<Face>(this.faces);
1619
1620 return copy;
1621 }
1622
1623 /// <summary>
1624 /// Adds a value to each XYZ vertex coordinate in the mesh
1625 /// </summary>
1626 /// <param name="x"></param>
1627 /// <param name="y"></param>
1628 /// <param name="z"></param>
1629 public void AddPos(float x, float y, float z)
1630 {
1631 int i;
1632 int numVerts = this.coords.Count;
1633 Coord vert;
1634
1635 for (i = 0; i < numVerts; i++)
1636 {
1637 vert = this.coords[i];
1638 vert.X += x;
1639 vert.Y += y;
1640 vert.Z += z;
1641 this.coords[i] = vert;
1642 }
1643 }
1644
1645 /// <summary>
1646 /// Rotates the mesh
1647 /// </summary>
1648 /// <param name="q"></param>
1649 public void AddRot(Quat q)
1650 {
1651 int i;
1652 int numVerts = this.coords.Count;
1653
1654 for (i = 0; i < numVerts; i++)
1655 this.coords[i] *= q;
1656 }
1657
1658#if VERTEX_INDEXER
1659 public VertexIndexer GetVertexIndexer()
1660 {
1661 return null;
1662 }
1663#endif
1664
1665 /// <summary>
1666 /// Scales the mesh
1667 /// </summary>
1668 /// <param name="x"></param>
1669 /// <param name="y"></param>
1670 /// <param name="z"></param>
1671 public void Scale(float x, float y, float z)
1672 {
1673 int i;
1674 int numVerts = this.coords.Count;
1675 //Coord vert;
1676
1677 Coord m = new Coord(x, y, z);
1678 for (i = 0; i < numVerts; i++)
1679 this.coords[i] *= m;
1680 }
1681
1682 /// <summary>
1683 /// Dumps the mesh to a Blender compatible "Raw" format file
1684 /// </summary>
1685 /// <param name="path"></param>
1686 /// <param name="name"></param>
1687 /// <param name="title"></param>
1688 public void DumpRaw(String path, String name, String title)
1689 {
1690 if (path == null)
1691 return;
1692 String fileName = name + "_" + title + ".raw";
1693 String completePath = System.IO.Path.Combine(path, fileName);
1694 StreamWriter sw = new StreamWriter(completePath);
1695
1696 for (int i = 0; i < this.faces.Count; i++)
1697 {
1698 string s = this.coords[this.faces[i].v1].ToString();
1699 s += " " + this.coords[this.faces[i].v2].ToString();
1700 s += " " + this.coords[this.faces[i].v3].ToString();
1701
1702 sw.WriteLine(s);
1703 }
1704
1705 sw.Close();
1706 }
1707 }
1708}
diff --git a/OpenSim/Region/PhysicsModules/ubOdeMeshing/Properties/AssemblyInfo.cs b/OpenSim/Region/PhysicsModules/ubOdeMeshing/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..91df7ab
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/ubOdeMeshing/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
1using System.Reflection;
2using System.Runtime.CompilerServices;
3using System.Runtime.InteropServices;
4using Mono.Addins;
5
6// General Information about an assembly is controlled through the following
7// set of attributes. Change these attribute values to modify the information
8// associated with an assembly.
9[assembly: AssemblyTitle("OpenSim.Region.PhysicsModule.ubODEMeshing")]
10[assembly: AssemblyDescription("Mesher for ubODE")]
11[assembly: AssemblyConfiguration("")]
12[assembly: AssemblyCompany("http://opensimulator.org")]
13[assembly: AssemblyProduct("OpenSim")]
14[assembly: AssemblyCopyright("OpenSimulator developers")]
15[assembly: AssemblyTrademark("")]
16[assembly: AssemblyCulture("")]
17
18// Setting ComVisible to false makes the types in this assembly not visible
19// to COM components. If you need to access a type in this assembly from
20// COM, set the ComVisible attribute to true on that type.
21[assembly: ComVisible(false)]
22
23// The following GUID is for the ID of the typelib if this project is exposed to COM
24[assembly: Guid("4b7e35c2-a9dd-4b10-b778-eb417f4f6884")]
25
26// Version information for an assembly consists of the following four values:
27//
28// Major Version
29// Minor Version
30// Build Number
31// Revision
32//
33[assembly: AssemblyVersion("0.8.2.*")]
34
35[assembly: Addin("OpenSim.Region.PhysicsModule.ubOdeMeshing", OpenSim.VersionInfo.VersionNumber)]
36[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
diff --git a/OpenSim/Region/PhysicsModules/ubOdeMeshing/SculptMap.cs b/OpenSim/Region/PhysicsModules/ubOdeMeshing/SculptMap.cs
new file mode 100644
index 0000000..1c75db6
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/ubOdeMeshing/SculptMap.cs
@@ -0,0 +1,244 @@
1/*
2 * Copyright (c) Contributors
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Text;
31
32using System.Drawing;
33using System.Drawing.Imaging;
34
35namespace PrimMesher
36{
37 public class SculptMap
38 {
39 public int width;
40 public int height;
41 public byte[] redBytes;
42 public byte[] greenBytes;
43 public byte[] blueBytes;
44
45 public SculptMap()
46 {
47 }
48
49 public SculptMap(Bitmap bm, int lod)
50 {
51 int bmW = bm.Width;
52 int bmH = bm.Height;
53
54 if (bmW == 0 || bmH == 0)
55 throw new Exception("SculptMap: bitmap has no data");
56
57 int numLodPixels = lod * lod; // (32 * 2)^2 = 64^2 pixels for default sculpt map image
58
59 bool needsScaling = false;
60 bool smallMap = false;
61
62 width = bmW;
63 height = bmH;
64
65 while (width * height > numLodPixels * 4)
66 {
67 width >>= 1;
68 height >>= 1;
69 needsScaling = true;
70 }
71
72 try
73 {
74 if (needsScaling)
75 bm = ScaleImage(bm, width, height);
76 }
77
78 catch (Exception e)
79 {
80 throw new Exception("Exception in ScaleImage(): e: " + e.ToString());
81 }
82
83 if (width * height > numLodPixels)
84 {
85 smallMap = false;
86 width >>= 1;
87 height >>= 1;
88 }
89 else
90 smallMap = true;
91
92 int numBytes = (width + 1) * (height + 1);
93 redBytes = new byte[numBytes];
94 greenBytes = new byte[numBytes];
95 blueBytes = new byte[numBytes];
96
97 int byteNdx = 0;
98 Color c;
99
100 try
101 {
102 for (int y = 0; y <= height; y++)
103 {
104 for (int x = 0; x < width; x++)
105 {
106 if (smallMap)
107 c = bm.GetPixel(x, y < height ? y : y - 1);
108 else
109 c = bm.GetPixel(x * 2, y < height ? y * 2 : y * 2 - 1);
110
111 redBytes[byteNdx] = c.R;
112 greenBytes[byteNdx] = c.G;
113 blueBytes[byteNdx] = c.B;
114
115 ++byteNdx;
116 }
117
118 if (smallMap)
119 c = bm.GetPixel(width - 1, y < height ? y : y - 1);
120 else
121 c = bm.GetPixel(width * 2 - 1, y < height ? y * 2 : y * 2 - 1);
122
123 redBytes[byteNdx] = c.R;
124 greenBytes[byteNdx] = c.G;
125 blueBytes[byteNdx] = c.B;
126
127 ++byteNdx;
128 }
129 }
130 catch (Exception e)
131 {
132 throw new Exception("Caught exception processing byte arrays in SculptMap(): e: " + e.ToString());
133 }
134
135 width++;
136 height++;
137 }
138
139 public List<List<Coord>> ToRows(bool mirror)
140 {
141 int numRows = height;
142 int numCols = width;
143
144 List<List<Coord>> rows = new List<List<Coord>>(numRows);
145
146 float pixScale = 1.0f / 255;
147
148 int rowNdx, colNdx;
149 int smNdx = 0;
150
151 for (rowNdx = 0; rowNdx < numRows; rowNdx++)
152 {
153 List<Coord> row = new List<Coord>(numCols);
154 for (colNdx = 0; colNdx < numCols; colNdx++)
155 {
156
157 if (mirror)
158 row.Add(new Coord(-((float)redBytes[smNdx] * pixScale - 0.5f), ((float)greenBytes[smNdx] * pixScale - 0.5f), (float)blueBytes[smNdx] * pixScale - 0.5f));
159 else
160 row.Add(new Coord((float)redBytes[smNdx] * pixScale - 0.5f, (float)greenBytes[smNdx] * pixScale - 0.5f, (float)blueBytes[smNdx] * pixScale - 0.5f));
161
162 ++smNdx;
163 }
164 rows.Add(row);
165 }
166 return rows;
167 }
168
169 private Bitmap ScaleImage(Bitmap srcImage, int destWidth, int destHeight)
170 {
171
172 Bitmap scaledImage = new Bitmap(destWidth, destHeight, PixelFormat.Format24bppRgb);
173
174 Color c;
175
176
177 // will let last step to be eventually diferent, as seems to be in sl
178
179 float xscale = (float)srcImage.Width / (float)destWidth;
180 float yscale = (float)srcImage.Height / (float)destHeight;
181
182 int lastsx = srcImage.Width - 1;
183 int lastsy = srcImage.Height - 1;
184 int lastdx = destWidth - 1;
185 int lastdy = destHeight - 1;
186
187 float sy = 0.5f;
188 float sx;
189
190 for (int y = 0; y < lastdy; y++)
191 {
192 sx = 0.5f;
193 for (int x = 0; x < lastdx; x++)
194 {
195 try
196 {
197 c = srcImage.GetPixel((int)(sx), (int)(sy));
198 scaledImage.SetPixel(x, y, Color.FromArgb(c.R, c.G, c.B));
199 }
200 catch (IndexOutOfRangeException)
201 {
202 }
203 sx += xscale;
204 }
205 try
206 {
207 c = srcImage.GetPixel(lastsx, (int)(sy));
208 scaledImage.SetPixel(lastdx, y, Color.FromArgb(c.R, c.G, c.B));
209 }
210 catch (IndexOutOfRangeException)
211 {
212 }
213
214 sy += yscale;
215 }
216
217 sx = 0.5f;
218 for (int x = 0; x < lastdx; x++)
219 {
220 try
221 {
222 c = srcImage.GetPixel((int)(sx), lastsy);
223 scaledImage.SetPixel(x, lastdy, Color.FromArgb(c.R, c.G, c.B));
224 }
225 catch (IndexOutOfRangeException)
226 {
227 }
228
229 sx += xscale;
230 }
231 try
232 {
233 c = srcImage.GetPixel(lastsx, lastsy);
234 scaledImage.SetPixel(lastdx, lastdy, Color.FromArgb(c.R, c.G, c.B));
235 }
236 catch (IndexOutOfRangeException)
237 {
238 }
239
240 srcImage.Dispose();
241 return scaledImage;
242 }
243 }
244} \ No newline at end of file
diff --git a/OpenSim/Region/PhysicsModules/ubOdeMeshing/SculptMesh.cs b/OpenSim/Region/PhysicsModules/ubOdeMeshing/SculptMesh.cs
new file mode 100644
index 0000000..bc1375b
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/ubOdeMeshing/SculptMesh.cs
@@ -0,0 +1,220 @@
1/*
2 * Copyright (c) Contributors
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Text;
31using System.IO;
32
33using System.Drawing;
34using System.Drawing.Imaging;
35
36namespace PrimMesher
37{
38
39 public class SculptMesh
40 {
41 public List<Coord> coords;
42 public List<Face> faces;
43
44 public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 };
45
46
47 public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool mirror, bool invert)
48 {
49 if (mirror)
50 invert = !invert;
51
52 SculptMap smap = new SculptMap(sculptBitmap, lod);
53
54 List<List<Coord>> rows = smap.ToRows(mirror);
55
56 _SculptMesh(rows, sculptType, invert);
57 }
58
59 private void _SculptMesh(List<List<Coord>> rows, SculptType sculptType, bool invert)
60 {
61 coords = new List<Coord>();
62 faces = new List<Face>();
63
64 sculptType = (SculptType)(((int)sculptType) & 0x07);
65
66 int width = rows[0].Count;
67
68 int p1, p2, p3, p4;
69
70 int imageX, imageY;
71
72 if (sculptType != SculptType.plane)
73 {
74 if (rows.Count % 2 == 0)
75 {
76 for (int rowNdx = 0; rowNdx < rows.Count; rowNdx++)
77 rows[rowNdx].Add(rows[rowNdx][0]);
78 }
79 else
80 {
81 int lastIndex = rows[0].Count - 1;
82
83 for (int i = 0; i < rows.Count; i++)
84 rows[i][0] = rows[i][lastIndex];
85 }
86 }
87
88 Coord topPole = rows[0][width / 2];
89 Coord bottomPole = rows[rows.Count - 1][width / 2];
90
91 if (sculptType == SculptType.sphere)
92 {
93 if (rows.Count % 2 == 0)
94 {
95 int count = rows[0].Count;
96 List<Coord> topPoleRow = new List<Coord>(count);
97 List<Coord> bottomPoleRow = new List<Coord>(count);
98
99 for (int i = 0; i < count; i++)
100 {
101 topPoleRow.Add(topPole);
102 bottomPoleRow.Add(bottomPole);
103 }
104 rows.Insert(0, topPoleRow);
105 rows.Add(bottomPoleRow);
106 }
107 else
108 {
109 int count = rows[0].Count;
110
111 List<Coord> topPoleRow = rows[0];
112 List<Coord> bottomPoleRow = rows[rows.Count - 1];
113
114 for (int i = 0; i < count; i++)
115 {
116 topPoleRow[i] = topPole;
117 bottomPoleRow[i] = bottomPole;
118 }
119 }
120 }
121
122 if (sculptType == SculptType.torus)
123 rows.Add(rows[0]);
124
125 int coordsDown = rows.Count;
126 int coordsAcross = rows[0].Count;
127
128 float widthUnit = 1.0f / (coordsAcross - 1);
129 float heightUnit = 1.0f / (coordsDown - 1);
130
131 for (imageY = 0; imageY < coordsDown; imageY++)
132 {
133 int rowOffset = imageY * coordsAcross;
134
135 for (imageX = 0; imageX < coordsAcross; imageX++)
136 {
137 /*
138 * p1-----p2
139 * | \ f2 |
140 * | \ |
141 * | f1 \|
142 * p3-----p4
143 */
144
145 p4 = rowOffset + imageX;
146 p3 = p4 - 1;
147
148 p2 = p4 - coordsAcross;
149 p1 = p3 - coordsAcross;
150
151 this.coords.Add(rows[imageY][imageX]);
152
153 if (imageY > 0 && imageX > 0)
154 {
155 Face f1, f2;
156
157 if (invert)
158 {
159 f1 = new Face(p1, p4, p3);
160 f2 = new Face(p1, p2, p4);
161 }
162 else
163 {
164 f1 = new Face(p1, p3, p4);
165 f2 = new Face(p1, p4, p2);
166 }
167
168 this.faces.Add(f1);
169 this.faces.Add(f2);
170 }
171 }
172 }
173 }
174
175 /// <summary>
176 /// Duplicates a SculptMesh object. All object properties are copied by value, including lists.
177 /// </summary>
178 /// <returns></returns>
179 public SculptMesh Copy()
180 {
181 return new SculptMesh(this);
182 }
183
184 public SculptMesh(SculptMesh sm)
185 {
186 coords = new List<Coord>(sm.coords);
187 faces = new List<Face>(sm.faces);
188 }
189
190 public void Scale(float x, float y, float z)
191 {
192 int i;
193 int numVerts = this.coords.Count;
194
195 Coord m = new Coord(x, y, z);
196 for (i = 0; i < numVerts; i++)
197 this.coords[i] *= m;
198 }
199
200 public void DumpRaw(String path, String name, String title)
201 {
202 if (path == null)
203 return;
204 String fileName = name + "_" + title + ".raw";
205 String completePath = System.IO.Path.Combine(path, fileName);
206 StreamWriter sw = new StreamWriter(completePath);
207
208 for (int i = 0; i < this.faces.Count; i++)
209 {
210 string s = this.coords[this.faces[i].v1].ToString();
211 s += " " + this.coords[this.faces[i].v2].ToString();
212 s += " " + this.coords[this.faces[i].v3].ToString();
213
214 sw.WriteLine(s);
215 }
216
217 sw.Close();
218 }
219 }
220}