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.cs601
-rw-r--r--OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs1458
-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, 4607 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..da8f623
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/ubOdeMeshing/Mesh.cs
@@ -0,0 +1,601 @@
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 result.m_obb.X = m_obb.X * x;
129 result.m_obb.Y = m_obb.Y * y;
130 result.m_obb.Z = m_obb.Z * z;
131 result.m_obboffset.X = m_obboffset.X * x;
132 result.m_obboffset.Y = m_obboffset.Y * y;
133 result.m_obboffset.Z = m_obboffset.Z * z;
134
135 result.vertices = new float[vertices.Length];
136 int j = 0;
137 for (int i = 0; i < m_vertexCount; i++)
138 {
139 result.vertices[j] = vertices[j] * x;
140 j++;
141 result.vertices[j] = vertices[j] * y;
142 j++;
143 result.vertices[j] = vertices[j] * z;
144 j++;
145 }
146
147 result.indexes = new int[indexes.Length];
148 indexes.CopyTo(result.indexes,0);
149
150 result.pinMemory();
151
152 return result;
153 }
154
155 public Mesh Clone()
156 {
157 Mesh result = new Mesh();
158
159 if (m_bdata != null)
160 {
161 result.m_bdata = new MeshBuildingData();
162 foreach (Triangle t in m_bdata.m_triangles)
163 {
164 result.Add(new Triangle(t.v1.Clone(), t.v2.Clone(), t.v3.Clone()));
165 }
166 result.m_bdata.m_centroid = m_bdata.m_centroid;
167 result.m_bdata.m_centroidDiv = m_bdata.m_centroidDiv;
168 result.m_bdata.m_obbXmin = m_bdata.m_obbXmin;
169 result.m_bdata.m_obbXmax = m_bdata.m_obbXmax;
170 result.m_bdata.m_obbYmin = m_bdata.m_obbYmin;
171 result.m_bdata.m_obbYmax = m_bdata.m_obbYmax;
172 result.m_bdata.m_obbZmin = m_bdata.m_obbZmin;
173 result.m_bdata.m_obbZmax = m_bdata.m_obbZmax;
174 }
175 result.m_obb = m_obb;
176 result.m_obboffset = m_obboffset;
177 return result;
178 }
179
180 public void addVertexLStats(Vertex v)
181 {
182 float x = v.X;
183 float y = v.Y;
184 float z = v.Z;
185
186 m_bdata.m_centroid.X += x;
187 m_bdata.m_centroid.Y += y;
188 m_bdata.m_centroid.Z += z;
189 m_bdata.m_centroidDiv++;
190
191 if (x > m_bdata.m_obbXmax)
192 m_bdata.m_obbXmax = x;
193 else if (x < m_bdata.m_obbXmin)
194 m_bdata.m_obbXmin = x;
195
196 if (y > m_bdata.m_obbYmax)
197 m_bdata.m_obbYmax = y;
198 else if (y < m_bdata.m_obbYmin)
199 m_bdata.m_obbYmin = y;
200
201 if (z > m_bdata.m_obbZmax)
202 m_bdata.m_obbZmax = z;
203 else if (z < m_bdata.m_obbZmin)
204 m_bdata.m_obbZmin = z;
205
206 }
207
208 public void Add(Triangle triangle)
209 {
210 if (m_indicesPtr != IntPtr.Zero || m_verticesPtr != IntPtr.Zero)
211 throw new NotSupportedException("Attempt to Add to a pinned Mesh");
212
213
214 triangle.v1.X = (float)Math.Round(triangle.v1.X, 6);
215 triangle.v1.Y = (float)Math.Round(triangle.v1.Y, 6);
216 triangle.v1.Z = (float)Math.Round(triangle.v1.Z, 6);
217 triangle.v2.X = (float)Math.Round(triangle.v2.X, 6);
218 triangle.v2.Y = (float)Math.Round(triangle.v2.Y, 6);
219 triangle.v2.Z = (float)Math.Round(triangle.v2.Z, 6);
220 triangle.v3.X = (float)Math.Round(triangle.v3.X, 6);
221 triangle.v3.Y = (float)Math.Round(triangle.v3.Y, 6);
222 triangle.v3.Z = (float)Math.Round(triangle.v3.Z, 6);
223
224 if ((triangle.v1.X == triangle.v2.X && triangle.v1.Y == triangle.v2.Y && triangle.v1.Z == triangle.v2.Z)
225 || (triangle.v1.X == triangle.v3.X && triangle.v1.Y == triangle.v3.Y && triangle.v1.Z == triangle.v3.Z)
226 || (triangle.v2.X == triangle.v3.X && triangle.v2.Y == triangle.v3.Y && triangle.v2.Z == triangle.v3.Z)
227 )
228 {
229 return;
230 }
231
232 if (m_bdata.m_vertices.Count == 0)
233 {
234 m_bdata.m_centroidDiv = 0;
235 m_bdata.m_centroid = Vector3.Zero;
236 }
237
238 if (!m_bdata.m_vertices.ContainsKey(triangle.v1))
239 {
240 m_bdata.m_vertices[triangle.v1] = m_bdata.m_vertices.Count;
241 addVertexLStats(triangle.v1);
242 }
243 if (!m_bdata.m_vertices.ContainsKey(triangle.v2))
244 {
245 m_bdata.m_vertices[triangle.v2] = m_bdata.m_vertices.Count;
246 addVertexLStats(triangle.v2);
247 }
248 if (!m_bdata.m_vertices.ContainsKey(triangle.v3))
249 {
250 m_bdata.m_vertices[triangle.v3] = m_bdata.m_vertices.Count;
251 addVertexLStats(triangle.v3);
252 }
253 m_bdata.m_triangles.Add(triangle);
254 }
255
256 public Vector3 GetCentroid()
257 {
258 return m_obboffset;
259
260 }
261
262 public Vector3 GetOBB()
263 {
264 return m_obb;
265 float x, y, z;
266 if (m_bdata.m_centroidDiv > 0)
267 {
268 x = (m_bdata.m_obbXmax - m_bdata.m_obbXmin) * 0.5f;
269 y = (m_bdata.m_obbYmax - m_bdata.m_obbYmin) * 0.5f;
270 z = (m_bdata.m_obbZmax - m_bdata.m_obbZmin) * 0.5f;
271 }
272 else // ??
273 {
274 x = 0.5f;
275 y = 0.5f;
276 z = 0.5f;
277 }
278 return new Vector3(x, y, z);
279 }
280
281 public List<Vector3> getVertexList()
282 {
283 List<Vector3> result = new List<Vector3>();
284 foreach (Vertex v in m_bdata.m_vertices.Keys)
285 {
286 result.Add(new Vector3(v.X, v.Y, v.Z));
287 }
288 return result;
289 }
290
291 public float[] getVertexListAsFloat()
292 {
293 if (m_bdata.m_vertices == null)
294 throw new NotSupportedException();
295 float[] result = new float[m_bdata.m_vertices.Count * 3];
296 foreach (KeyValuePair<Vertex, int> kvp in m_bdata.m_vertices)
297 {
298 Vertex v = kvp.Key;
299 int i = kvp.Value;
300 result[3 * i + 0] = v.X;
301 result[3 * i + 1] = v.Y;
302 result[3 * i + 2] = v.Z;
303 }
304 return result;
305 }
306
307 public float[] getVertexListAsFloatLocked()
308 {
309 return null;
310 }
311
312 public void getVertexListAsPtrToFloatArray(out IntPtr _vertices, out int vertexStride, out int vertexCount)
313 {
314 // A vertex is 3 floats
315 vertexStride = 3 * sizeof(float);
316
317 // If there isn't an unmanaged array allocated yet, do it now
318 if (m_verticesPtr == IntPtr.Zero && m_bdata != null)
319 {
320 vertices = getVertexListAsFloat();
321 // Each vertex is 3 elements (floats)
322 m_vertexCount = vertices.Length / 3;
323 vhandler = GCHandle.Alloc(vertices, GCHandleType.Pinned);
324 m_verticesPtr = vhandler.AddrOfPinnedObject();
325 GC.AddMemoryPressure(Buffer.ByteLength(vertices));
326 }
327 _vertices = m_verticesPtr;
328 vertexCount = m_vertexCount;
329 }
330
331 public int[] getIndexListAsInt()
332 {
333 if (m_bdata.m_triangles == null)
334 throw new NotSupportedException();
335 int[] result = new int[m_bdata.m_triangles.Count * 3];
336 for (int i = 0; i < m_bdata.m_triangles.Count; i++)
337 {
338 Triangle t = m_bdata.m_triangles[i];
339 result[3 * i + 0] = m_bdata.m_vertices[t.v1];
340 result[3 * i + 1] = m_bdata.m_vertices[t.v2];
341 result[3 * i + 2] = m_bdata.m_vertices[t.v3];
342 }
343 return result;
344 }
345
346 /// <summary>
347 /// creates a list of index values that defines triangle faces. THIS METHOD FREES ALL NON-PINNED MESH DATA
348 /// </summary>
349 /// <returns></returns>
350 public int[] getIndexListAsIntLocked()
351 {
352 return null;
353 }
354
355 public void getIndexListAsPtrToIntArray(out IntPtr indices, out int triStride, out int indexCount)
356 {
357 // If there isn't an unmanaged array allocated yet, do it now
358 if (m_indicesPtr == IntPtr.Zero && m_bdata != null)
359 {
360 indexes = getIndexListAsInt();
361 m_indexCount = indexes.Length;
362 ihandler = GCHandle.Alloc(indexes, GCHandleType.Pinned);
363 m_indicesPtr = ihandler.AddrOfPinnedObject();
364 GC.AddMemoryPressure(Buffer.ByteLength(indexes));
365 }
366 // A triangle is 3 ints (indices)
367 triStride = 3 * sizeof(int);
368 indices = m_indicesPtr;
369 indexCount = m_indexCount;
370 }
371
372 public void releasePinned()
373 {
374 if (m_verticesPtr != IntPtr.Zero)
375 {
376 vhandler.Free();
377 vertices = null;
378 m_verticesPtr = IntPtr.Zero;
379 }
380 if (m_indicesPtr != IntPtr.Zero)
381 {
382 ihandler.Free();
383 indexes = null;
384 m_indicesPtr = IntPtr.Zero;
385 }
386 }
387
388 /// <summary>
389 /// frees up the source mesh data to minimize memory - call this method after calling get*Locked() functions
390 /// </summary>
391 public void releaseSourceMeshData()
392 {
393 if (m_bdata != null)
394 {
395 m_bdata.m_triangles = null;
396 m_bdata.m_vertices = null;
397 }
398 }
399
400 public void releaseBuildingMeshData()
401 {
402 if (m_bdata != null)
403 {
404 m_bdata.m_triangles = null;
405 m_bdata.m_vertices = null;
406 m_bdata = null;
407 }
408 }
409
410 public void Append(IMesh newMesh)
411 {
412 if (m_indicesPtr != IntPtr.Zero || m_verticesPtr != IntPtr.Zero)
413 throw new NotSupportedException("Attempt to Append to a pinned Mesh");
414
415 if (!(newMesh is Mesh))
416 return;
417
418 foreach (Triangle t in ((Mesh)newMesh).m_bdata.m_triangles)
419 Add(t);
420 }
421
422 // Do a linear transformation of mesh.
423 public void TransformLinear(float[,] matrix, float[] offset)
424 {
425 if (m_indicesPtr != IntPtr.Zero || m_verticesPtr != IntPtr.Zero)
426 throw new NotSupportedException("Attempt to TransformLinear a pinned Mesh");
427
428 foreach (Vertex v in m_bdata.m_vertices.Keys)
429 {
430 if (v == null)
431 continue;
432 float x, y, z;
433 x = v.X*matrix[0, 0] + v.Y*matrix[1, 0] + v.Z*matrix[2, 0];
434 y = v.X*matrix[0, 1] + v.Y*matrix[1, 1] + v.Z*matrix[2, 1];
435 z = v.X*matrix[0, 2] + v.Y*matrix[1, 2] + v.Z*matrix[2, 2];
436 v.X = x + offset[0];
437 v.Y = y + offset[1];
438 v.Z = z + offset[2];
439 }
440 }
441
442 public void DumpRaw(String path, String name, String title)
443 {
444 if (path == null)
445 return;
446 if (m_bdata == null)
447 return;
448 String fileName = name + "_" + title + ".raw";
449 String completePath = System.IO.Path.Combine(path, fileName);
450 StreamWriter sw = new StreamWriter(completePath);
451 foreach (Triangle t in m_bdata.m_triangles)
452 {
453 String s = t.ToStringRaw();
454 sw.WriteLine(s);
455 }
456 sw.Close();
457 }
458
459 public void TrimExcess()
460 {
461 m_bdata.m_triangles.TrimExcess();
462 }
463
464 public void pinMemory()
465 {
466 m_vertexCount = vertices.Length / 3;
467 vhandler = GCHandle.Alloc(vertices, GCHandleType.Pinned);
468 m_verticesPtr = vhandler.AddrOfPinnedObject();
469 GC.AddMemoryPressure(Buffer.ByteLength(vertices));
470
471 m_indexCount = indexes.Length;
472 ihandler = GCHandle.Alloc(indexes, GCHandleType.Pinned);
473 m_indicesPtr = ihandler.AddrOfPinnedObject();
474 GC.AddMemoryPressure(Buffer.ByteLength(indexes));
475 }
476
477 public void PrepForOde()
478 {
479 // If there isn't an unmanaged array allocated yet, do it now
480 if (m_verticesPtr == IntPtr.Zero)
481 vertices = getVertexListAsFloat();
482
483 // If there isn't an unmanaged array allocated yet, do it now
484 if (m_indicesPtr == IntPtr.Zero)
485 indexes = getIndexListAsInt();
486
487 pinMemory();
488
489 float x, y, z;
490
491 if (m_bdata.m_centroidDiv > 0)
492 {
493 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);
494 x = (m_bdata.m_obbXmax - m_bdata.m_obbXmin) * 0.5f;
495 y = (m_bdata.m_obbYmax - m_bdata.m_obbYmin) * 0.5f;
496 z = (m_bdata.m_obbZmax - m_bdata.m_obbZmin) * 0.5f;
497 }
498
499 else
500 {
501 m_obboffset = Vector3.Zero;
502 x = 0.5f;
503 y = 0.5f;
504 z = 0.5f;
505 }
506 m_obb = new Vector3(x, y, z);
507
508 releaseBuildingMeshData();
509 }
510 public bool ToStream(Stream st)
511 {
512 if (m_indicesPtr == IntPtr.Zero || m_verticesPtr == IntPtr.Zero)
513 return false;
514
515 BinaryWriter bw = new BinaryWriter(st);
516 bool ok = true;
517
518 try
519 {
520
521 bw.Write(m_vertexCount);
522 bw.Write(m_indexCount);
523
524 for (int i = 0; i < 3 * m_vertexCount; i++)
525 bw.Write(vertices[i]);
526 for (int i = 0; i < m_indexCount; i++)
527 bw.Write(indexes[i]);
528 bw.Write(m_obb.X);
529 bw.Write(m_obb.Y);
530 bw.Write(m_obb.Z);
531 bw.Write(m_obboffset.X);
532 bw.Write(m_obboffset.Y);
533 bw.Write(m_obboffset.Z);
534 }
535 catch
536 {
537 ok = false;
538 }
539
540 if (bw != null)
541 {
542 bw.Flush();
543 bw.Close();
544 }
545
546 return ok;
547 }
548
549 public static Mesh FromStream(Stream st, AMeshKey key)
550 {
551 Mesh mesh = new Mesh();
552 mesh.releaseBuildingMeshData();
553
554 BinaryReader br = new BinaryReader(st);
555
556 bool ok = true;
557 try
558 {
559 mesh.m_vertexCount = br.ReadInt32();
560 mesh.m_indexCount = br.ReadInt32();
561
562 int n = 3 * mesh.m_vertexCount;
563 mesh.vertices = new float[n];
564 for (int i = 0; i < n; i++)
565 mesh.vertices[i] = br.ReadSingle();
566
567 mesh.indexes = new int[mesh.m_indexCount];
568 for (int i = 0; i < mesh.m_indexCount; i++)
569 mesh.indexes[i] = br.ReadInt32();
570
571 mesh.m_obb.X = br.ReadSingle();
572 mesh.m_obb.Y = br.ReadSingle();
573 mesh.m_obb.Z = br.ReadSingle();
574
575 mesh.m_obboffset.X = br.ReadSingle();
576 mesh.m_obboffset.Y = br.ReadSingle();
577 mesh.m_obboffset.Z = br.ReadSingle();
578 }
579 catch
580 {
581 ok = false;
582 }
583
584 br.Close();
585
586 if (ok)
587 {
588 mesh.pinMemory();
589
590 mesh.Key = key;
591 mesh.RefCount = 1;
592
593 return mesh;
594 }
595
596 mesh.vertices = null;
597 mesh.indexes = null;
598 return null;
599 }
600 }
601}
diff --git a/OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs b/OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs
new file mode 100644
index 0000000..be76f8f
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/ubOdeMeshing/Meshmerizer.cs
@@ -0,0 +1,1458 @@
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 ComponentAce.Compression.Libs.zlib;
47using System.Runtime.Serialization;
48using System.Runtime.Serialization.Formatters.Binary;
49
50using Mono.Addins;
51
52namespace OpenSim.Region.PhysicsModule.ubODEMeshing
53{
54 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "ubODEMeshmerizer")]
55 public class UbitMeshmerizer : IMesher, INonSharedRegionModule
56 {
57 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
58
59 // Setting baseDir to a path will enable the dumping of raw files
60 // raw files can be imported by blender so a visual inspection of the results can be done
61
62 private bool m_Enabled = false;
63
64 public object diskLock = new object();
65
66 public bool doMeshFileCache = true;
67
68 public string cachePath = "MeshCache";
69 public TimeSpan CacheExpire;
70 public bool doCacheExpire = true;
71
72// const string baseDir = "rawFiles";
73 private const string baseDir = null; //"rawFiles";
74
75 private bool useMeshiesPhysicsMesh = false;
76
77 private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh
78
79 private Dictionary<AMeshKey, Mesh> m_uniqueMeshes = new Dictionary<AMeshKey, Mesh>();
80 private Dictionary<AMeshKey, Mesh> m_uniqueReleasedMeshes = new Dictionary<AMeshKey, Mesh>();
81
82 #region INonSharedRegionModule
83 public string Name
84 {
85 get { return "ubODEMeshmerizer"; }
86 }
87
88 public Type ReplaceableInterface
89 {
90 get { return null; }
91 }
92
93 public void Initialise(IConfigSource config)
94 {
95 IConfig start_config = config.Configs["Startup"];
96
97 string mesher = start_config.GetString("meshing", string.Empty);
98 if (mesher == Name)
99 {
100 float fcache = 48.0f;
101 // float fcache = 0.02f;
102
103 IConfig mesh_config = config.Configs["Mesh"];
104 if (mesh_config != null)
105 {
106 useMeshiesPhysicsMesh = mesh_config.GetBoolean("UseMeshiesPhysicsMesh", useMeshiesPhysicsMesh);
107 if (useMeshiesPhysicsMesh)
108 {
109 doMeshFileCache = mesh_config.GetBoolean("MeshFileCache", doMeshFileCache);
110 cachePath = mesh_config.GetString("MeshFileCachePath", cachePath);
111 fcache = mesh_config.GetFloat("MeshFileCacheExpireHours", fcache);
112 doCacheExpire = mesh_config.GetBoolean("MeshFileCacheDoExpire", doCacheExpire);
113 }
114 else
115 {
116 doMeshFileCache = false;
117 doCacheExpire = false;
118 }
119
120 m_Enabled = true;
121 }
122
123 CacheExpire = TimeSpan.FromHours(fcache);
124
125 }
126 }
127
128 public void Close()
129 {
130 }
131
132 public void AddRegion(Scene scene)
133 {
134 if (!m_Enabled)
135 return;
136
137 scene.RegisterModuleInterface<IMesher>(this);
138 }
139
140 public void RemoveRegion(Scene scene)
141 {
142 if (!m_Enabled)
143 return;
144
145 scene.UnregisterModuleInterface<IMesher>(this);
146 }
147
148 public void RegionLoaded(Scene scene)
149 {
150 if (!m_Enabled)
151 return;
152 }
153
154 #endregion
155
156 /// <summary>
157 /// creates a simple box mesh of the specified size. This mesh is of very low vertex count and may
158 /// be useful as a backup proxy when level of detail is not needed or when more complex meshes fail
159 /// for some reason
160 /// </summary>
161 /// <param name="minX"></param>
162 /// <param name="maxX"></param>
163 /// <param name="minY"></param>
164 /// <param name="maxY"></param>
165 /// <param name="minZ"></param>
166 /// <param name="maxZ"></param>
167 /// <returns></returns>
168 private static Mesh CreateSimpleBoxMesh(float minX, float maxX, float minY, float maxY, float minZ, float maxZ)
169 {
170 Mesh box = new Mesh();
171 List<Vertex> vertices = new List<Vertex>();
172 // bottom
173
174 vertices.Add(new Vertex(minX, maxY, minZ));
175 vertices.Add(new Vertex(maxX, maxY, minZ));
176 vertices.Add(new Vertex(maxX, minY, minZ));
177 vertices.Add(new Vertex(minX, minY, minZ));
178
179 box.Add(new Triangle(vertices[0], vertices[1], vertices[2]));
180 box.Add(new Triangle(vertices[0], vertices[2], vertices[3]));
181
182 // top
183
184 vertices.Add(new Vertex(maxX, maxY, maxZ));
185 vertices.Add(new Vertex(minX, maxY, maxZ));
186 vertices.Add(new Vertex(minX, minY, maxZ));
187 vertices.Add(new Vertex(maxX, minY, maxZ));
188
189 box.Add(new Triangle(vertices[4], vertices[5], vertices[6]));
190 box.Add(new Triangle(vertices[4], vertices[6], vertices[7]));
191
192 // sides
193
194 box.Add(new Triangle(vertices[5], vertices[0], vertices[3]));
195 box.Add(new Triangle(vertices[5], vertices[3], vertices[6]));
196
197 box.Add(new Triangle(vertices[1], vertices[0], vertices[5]));
198 box.Add(new Triangle(vertices[1], vertices[5], vertices[4]));
199
200 box.Add(new Triangle(vertices[7], vertices[1], vertices[4]));
201 box.Add(new Triangle(vertices[7], vertices[2], vertices[1]));
202
203 box.Add(new Triangle(vertices[3], vertices[2], vertices[7]));
204 box.Add(new Triangle(vertices[3], vertices[7], vertices[6]));
205
206 return box;
207 }
208
209 /// <summary>
210 /// Creates a simple bounding box mesh for a complex input mesh
211 /// </summary>
212 /// <param name="meshIn"></param>
213 /// <returns></returns>
214 private static Mesh CreateBoundingBoxMesh(Mesh meshIn)
215 {
216 float minX = float.MaxValue;
217 float maxX = float.MinValue;
218 float minY = float.MaxValue;
219 float maxY = float.MinValue;
220 float minZ = float.MaxValue;
221 float maxZ = float.MinValue;
222
223 foreach (Vector3 v in meshIn.getVertexList())
224 {
225 if (v.X < minX) minX = v.X;
226 if (v.Y < minY) minY = v.Y;
227 if (v.Z < minZ) minZ = v.Z;
228
229 if (v.X > maxX) maxX = v.X;
230 if (v.Y > maxY) maxY = v.Y;
231 if (v.Z > maxZ) maxZ = v.Z;
232 }
233
234 return CreateSimpleBoxMesh(minX, maxX, minY, maxY, minZ, maxZ);
235 }
236
237 private void ReportPrimError(string message, string primName, PrimMesh primMesh)
238 {
239 m_log.Error(message);
240 m_log.Error("\nPrim Name: " + primName);
241 m_log.Error("****** PrimMesh Parameters ******\n" + primMesh.ParamsToDisplayString());
242 }
243
244 /// <summary>
245 /// Add a submesh to an existing list of coords and faces.
246 /// </summary>
247 /// <param name="subMeshData"></param>
248 /// <param name="size">Size of entire object</param>
249 /// <param name="coords"></param>
250 /// <param name="faces"></param>
251 private void AddSubMesh(OSDMap subMeshData, List<Coord> coords, List<Face> faces)
252 {
253 // Console.WriteLine("subMeshMap for {0} - {1}", primName, Util.GetFormattedXml((OSD)subMeshMap));
254
255 // As per http://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format, some Mesh Level
256 // of Detail Blocks (maps) contain just a NoGeometry key to signal there is no
257 // geometry for this submesh.
258 if (subMeshData.ContainsKey("NoGeometry") && ((OSDBoolean)subMeshData["NoGeometry"]))
259 return;
260
261 OpenMetaverse.Vector3 posMax;
262 OpenMetaverse.Vector3 posMin;
263 if (subMeshData.ContainsKey("PositionDomain"))
264 {
265 posMax = ((OSDMap)subMeshData["PositionDomain"])["Max"].AsVector3();
266 posMin = ((OSDMap)subMeshData["PositionDomain"])["Min"].AsVector3();
267 }
268 else
269 {
270 posMax = new Vector3(0.5f, 0.5f, 0.5f);
271 posMin = new Vector3(-0.5f, -0.5f, -0.5f);
272 }
273
274 ushort faceIndexOffset = (ushort)coords.Count;
275
276 byte[] posBytes = subMeshData["Position"].AsBinary();
277 for (int i = 0; i < posBytes.Length; i += 6)
278 {
279 ushort uX = Utils.BytesToUInt16(posBytes, i);
280 ushort uY = Utils.BytesToUInt16(posBytes, i + 2);
281 ushort uZ = Utils.BytesToUInt16(posBytes, i + 4);
282
283 Coord c = new Coord(
284 Utils.UInt16ToFloat(uX, posMin.X, posMax.X),
285 Utils.UInt16ToFloat(uY, posMin.Y, posMax.Y),
286 Utils.UInt16ToFloat(uZ, posMin.Z, posMax.Z));
287
288 coords.Add(c);
289 }
290
291 byte[] triangleBytes = subMeshData["TriangleList"].AsBinary();
292 for (int i = 0; i < triangleBytes.Length; i += 6)
293 {
294 ushort v1 = (ushort)(Utils.BytesToUInt16(triangleBytes, i) + faceIndexOffset);
295 ushort v2 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 2) + faceIndexOffset);
296 ushort v3 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 4) + faceIndexOffset);
297 Face f = new Face(v1, v2, v3);
298 faces.Add(f);
299 }
300 }
301
302 /// <summary>
303 /// Create a physics mesh from data that comes with the prim. The actual data used depends on the prim type.
304 /// </summary>
305 /// <param name="primName"></param>
306 /// <param name="primShape"></param>
307 /// <param name="size"></param>
308 /// <param name="lod"></param>
309 /// <returns></returns>
310 private Mesh CreateMeshFromPrimMesher(string primName, PrimitiveBaseShape primShape, float lod, bool convex)
311 {
312// m_log.DebugFormat(
313// "[MESH]: Creating physics proxy for {0}, shape {1}",
314// primName, (OpenMetaverse.SculptType)primShape.SculptType);
315
316 List<Coord> coords;
317 List<Face> faces;
318
319 if (primShape.SculptEntry)
320 {
321 if (((OpenMetaverse.SculptType)primShape.SculptType) == SculptType.Mesh)
322 {
323 if (!useMeshiesPhysicsMesh)
324 return null;
325
326 if (!GenerateCoordsAndFacesFromPrimMeshData(primName, primShape, out coords, out faces, convex))
327 return null;
328 }
329 else
330 {
331 if (!GenerateCoordsAndFacesFromPrimSculptData(primName, primShape, lod, out coords, out faces))
332 return null;
333 }
334 }
335 else
336 {
337 if (!GenerateCoordsAndFacesFromPrimShapeData(primName, primShape, lod, out coords, out faces))
338 return null;
339 }
340
341 primShape.SculptData = Utils.EmptyBytes;
342
343 int numCoords = coords.Count;
344 int numFaces = faces.Count;
345
346 Mesh mesh = new Mesh();
347 // Add the corresponding triangles to the mesh
348 for (int i = 0; i < numFaces; i++)
349 {
350 Face f = faces[i];
351 mesh.Add(new Triangle(coords[f.v1].X, coords[f.v1].Y, coords[f.v1].Z,
352 coords[f.v2].X, coords[f.v2].Y, coords[f.v2].Z,
353 coords[f.v3].X, coords[f.v3].Y, coords[f.v3].Z));
354 }
355
356 coords.Clear();
357 faces.Clear();
358
359 return mesh;
360 }
361
362 /// <summary>
363 /// Generate the co-ords and faces necessary to construct a mesh from the mesh data the accompanies a prim.
364 /// </summary>
365 /// <param name="primName"></param>
366 /// <param name="primShape"></param>
367 /// <param name="size"></param>
368 /// <param name="coords">Coords are added to this list by the method.</param>
369 /// <param name="faces">Faces are added to this list by the method.</param>
370 /// <returns>true if coords and faces were successfully generated, false if not</returns>
371 private bool GenerateCoordsAndFacesFromPrimMeshData(
372 string primName, PrimitiveBaseShape primShape, out List<Coord> coords, out List<Face> faces, bool convex)
373 {
374// m_log.DebugFormat("[MESH]: experimental mesh proxy generation for {0}", primName);
375
376 bool usemesh = false;
377
378 coords = new List<Coord>();
379 faces = new List<Face>();
380 OSD meshOsd = null;
381
382 if (primShape.SculptData.Length <= 0)
383 {
384// m_log.InfoFormat("[MESH]: asset data for {0} is zero length", primName);
385 return false;
386 }
387
388 long start = 0;
389 using (MemoryStream data = new MemoryStream(primShape.SculptData))
390 {
391 try
392 {
393 OSD osd = OSDParser.DeserializeLLSDBinary(data);
394 if (osd is OSDMap)
395 meshOsd = (OSDMap)osd;
396 else
397 {
398 m_log.Warn("[Mesh}: unable to cast mesh asset to OSDMap");
399 return false;
400 }
401 }
402 catch (Exception e)
403 {
404 m_log.Error("[MESH]: Exception deserializing mesh asset header:" + e.ToString());
405 }
406
407 start = data.Position;
408 }
409
410 if (meshOsd is OSDMap)
411 {
412 OSDMap physicsParms = null;
413 OSDMap map = (OSDMap)meshOsd;
414
415 if (!convex)
416 {
417 if (map.ContainsKey("physics_shape"))
418 physicsParms = (OSDMap)map["physics_shape"]; // old asset format
419 else if (map.ContainsKey("physics_mesh"))
420 physicsParms = (OSDMap)map["physics_mesh"]; // new asset format
421
422 if (physicsParms != null)
423 usemesh = true;
424 }
425
426 if(!usemesh && (map.ContainsKey("physics_convex")))
427 physicsParms = (OSDMap)map["physics_convex"];
428
429
430 if (physicsParms == null)
431 {
432 m_log.Warn("[MESH]: unknown mesh type");
433 return false;
434 }
435
436 int physOffset = physicsParms["offset"].AsInteger() + (int)start;
437 int physSize = physicsParms["size"].AsInteger();
438
439 if (physOffset < 0 || physSize == 0)
440 return false; // no mesh data in asset
441
442 OSD decodedMeshOsd = new OSD();
443 byte[] meshBytes = new byte[physSize];
444 System.Buffer.BlockCopy(primShape.SculptData, physOffset, meshBytes, 0, physSize);
445
446 try
447 {
448 using (MemoryStream inMs = new MemoryStream(meshBytes))
449 {
450 using (MemoryStream outMs = new MemoryStream())
451 {
452 using (ZOutputStream zOut = new ZOutputStream(outMs))
453 {
454 byte[] readBuffer = new byte[2048];
455 int readLen = 0;
456 while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0)
457 {
458 zOut.Write(readBuffer, 0, readLen);
459 }
460 zOut.Flush();
461 outMs.Seek(0, SeekOrigin.Begin);
462
463 byte[] decompressedBuf = outMs.GetBuffer();
464
465 decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
466 }
467 }
468 }
469 }
470 catch (Exception e)
471 {
472 m_log.Error("[MESH]: exception decoding physical mesh: " + e.ToString());
473 return false;
474 }
475
476 if (usemesh)
477 {
478 OSDArray decodedMeshOsdArray = null;
479
480 // physics_shape is an array of OSDMaps, one for each submesh
481 if (decodedMeshOsd is OSDArray)
482 {
483// Console.WriteLine("decodedMeshOsd for {0} - {1}", primName, Util.GetFormattedXml(decodedMeshOsd));
484
485 decodedMeshOsdArray = (OSDArray)decodedMeshOsd;
486 foreach (OSD subMeshOsd in decodedMeshOsdArray)
487 {
488 if (subMeshOsd is OSDMap)
489 AddSubMesh(subMeshOsd as OSDMap, coords, faces);
490 }
491 }
492 }
493 else
494 {
495 OSDMap cmap = (OSDMap)decodedMeshOsd;
496 if (cmap == null)
497 return false;
498
499 byte[] data;
500
501 List<float3> vs = new List<float3>();
502 PHullResult hullr = new PHullResult();
503 float3 f3;
504 Coord c;
505 Face f;
506 Vector3 range;
507 Vector3 min;
508
509 const float invMaxU16 = 1.0f / 65535f;
510 int t1;
511 int t2;
512 int t3;
513 int i;
514 int nverts;
515 int nindexs;
516
517 if (cmap.ContainsKey("Max"))
518 range = cmap["Max"].AsVector3();
519 else
520 range = new Vector3(0.5f, 0.5f, 0.5f);
521
522 if (cmap.ContainsKey("Min"))
523 min = cmap["Min"].AsVector3();
524 else
525 min = new Vector3(-0.5f, -0.5f, -0.5f);
526
527 range = range - min;
528 range *= invMaxU16;
529
530 if (!convex && cmap.ContainsKey("HullList") && cmap.ContainsKey("Positions"))
531 {
532 List<int> hsizes = new List<int>();
533 int totalpoints = 0;
534 data = cmap["HullList"].AsBinary();
535 for (i = 0; i < data.Length; i++)
536 {
537 t1 = data[i];
538 if (t1 == 0)
539 t1 = 256;
540 totalpoints += t1;
541 hsizes.Add(t1);
542 }
543
544 data = cmap["Positions"].AsBinary();
545 int ptr = 0;
546 int vertsoffset = 0;
547
548 if (totalpoints == data.Length / 6) // 2 bytes per coord, 3 coords per point
549 {
550 foreach (int hullsize in hsizes)
551 {
552 for (i = 0; i < hullsize; i++ )
553 {
554 t1 = data[ptr++];
555 t1 += data[ptr++] << 8;
556 t2 = data[ptr++];
557 t2 += data[ptr++] << 8;
558 t3 = data[ptr++];
559 t3 += data[ptr++] << 8;
560
561 f3 = new float3((t1 * range.X + min.X),
562 (t2 * range.Y + min.Y),
563 (t3 * range.Z + min.Z));
564 vs.Add(f3);
565 }
566
567 if(hullsize <3)
568 {
569 vs.Clear();
570 continue;
571 }
572
573 if (hullsize <5)
574 {
575 foreach (float3 point in vs)
576 {
577 c.X = point.x;
578 c.Y = point.y;
579 c.Z = point.z;
580 coords.Add(c);
581 }
582 f = new Face(vertsoffset, vertsoffset + 1, vertsoffset + 2);
583 faces.Add(f);
584
585 if (hullsize == 4)
586 {
587 // not sure about orientation..
588 f = new Face(vertsoffset, vertsoffset + 2, vertsoffset + 3);
589 faces.Add(f);
590 f = new Face(vertsoffset, vertsoffset + 3, vertsoffset + 1);
591 faces.Add(f);
592 f = new Face(vertsoffset + 3, vertsoffset + 2, vertsoffset + 1);
593 faces.Add(f);
594 }
595 vertsoffset += vs.Count;
596 vs.Clear();
597 continue;
598 }
599
600 if (!HullUtils.ComputeHull(vs, ref hullr, 0, 0.0f))
601 {
602 vs.Clear();
603 continue;
604 }
605
606 nverts = hullr.Vertices.Count;
607 nindexs = hullr.Indices.Count;
608
609 if (nindexs % 3 != 0)
610 {
611 vs.Clear();
612 continue;
613 }
614
615 for (i = 0; i < nverts; i++)
616 {
617 c.X = hullr.Vertices[i].x;
618 c.Y = hullr.Vertices[i].y;
619 c.Z = hullr.Vertices[i].z;
620 coords.Add(c);
621 }
622
623 for (i = 0; i < nindexs; i += 3)
624 {
625 t1 = hullr.Indices[i];
626 if (t1 > nverts)
627 break;
628 t2 = hullr.Indices[i + 1];
629 if (t2 > nverts)
630 break;
631 t3 = hullr.Indices[i + 2];
632 if (t3 > nverts)
633 break;
634 f = new Face(vertsoffset + t1, vertsoffset + t2, vertsoffset + t3);
635 faces.Add(f);
636 }
637 vertsoffset += nverts;
638 vs.Clear();
639 }
640 }
641 if (coords.Count > 0 && faces.Count > 0)
642 return true;
643 }
644
645 vs.Clear();
646
647 if (cmap.ContainsKey("BoundingVerts"))
648 {
649 data = cmap["BoundingVerts"].AsBinary();
650
651 for (i = 0; i < data.Length; )
652 {
653 t1 = data[i++];
654 t1 += data[i++] << 8;
655 t2 = data[i++];
656 t2 += data[i++] << 8;
657 t3 = data[i++];
658 t3 += data[i++] << 8;
659
660 f3 = new float3((t1 * range.X + min.X),
661 (t2 * range.Y + min.Y),
662 (t3 * range.Z + min.Z));
663 vs.Add(f3);
664 }
665
666 if (vs.Count < 3)
667 {
668 vs.Clear();
669 return false;
670 }
671
672 if (vs.Count < 5)
673 {
674 foreach (float3 point in vs)
675 {
676 c.X = point.x;
677 c.Y = point.y;
678 c.Z = point.z;
679 coords.Add(c);
680 }
681 f = new Face(0, 1, 2);
682 faces.Add(f);
683
684 if (vs.Count == 4)
685 {
686 f = new Face(0, 2, 3);
687 faces.Add(f);
688 f = new Face(0, 3, 1);
689 faces.Add(f);
690 f = new Face( 3, 2, 1);
691 faces.Add(f);
692 }
693 vs.Clear();
694 return true;
695 }
696
697 if (!HullUtils.ComputeHull(vs, ref hullr, 0, 0.0f))
698 return false;
699
700 nverts = hullr.Vertices.Count;
701 nindexs = hullr.Indices.Count;
702
703 if (nindexs % 3 != 0)
704 return false;
705
706 for (i = 0; i < nverts; i++)
707 {
708 c.X = hullr.Vertices[i].x;
709 c.Y = hullr.Vertices[i].y;
710 c.Z = hullr.Vertices[i].z;
711 coords.Add(c);
712 }
713 for (i = 0; i < nindexs; i += 3)
714 {
715 t1 = hullr.Indices[i];
716 if (t1 > nverts)
717 break;
718 t2 = hullr.Indices[i + 1];
719 if (t2 > nverts)
720 break;
721 t3 = hullr.Indices[i + 2];
722 if (t3 > nverts)
723 break;
724 f = new Face(t1, t2, t3);
725 faces.Add(f);
726 }
727
728 if (coords.Count > 0 && faces.Count > 0)
729 return true;
730 }
731 else
732 return false;
733 }
734 }
735
736 return true;
737 }
738
739 /// <summary>
740 /// Generate the co-ords and faces necessary to construct a mesh from the sculpt data the accompanies a prim.
741 /// </summary>
742 /// <param name="primName"></param>
743 /// <param name="primShape"></param>
744 /// <param name="size"></param>
745 /// <param name="lod"></param>
746 /// <param name="coords">Coords are added to this list by the method.</param>
747 /// <param name="faces">Faces are added to this list by the method.</param>
748 /// <returns>true if coords and faces were successfully generated, false if not</returns>
749 private bool GenerateCoordsAndFacesFromPrimSculptData(
750 string primName, PrimitiveBaseShape primShape, float lod, out List<Coord> coords, out List<Face> faces)
751 {
752 coords = new List<Coord>();
753 faces = new List<Face>();
754 PrimMesher.SculptMesh sculptMesh;
755 Image idata = null;
756
757 if (primShape.SculptData == null || primShape.SculptData.Length == 0)
758 return false;
759
760 try
761 {
762 OpenMetaverse.Imaging.ManagedImage unusedData;
763 OpenMetaverse.Imaging.OpenJPEG.DecodeToImage(primShape.SculptData, out unusedData, out idata);
764
765 unusedData = null;
766
767 if (idata == null)
768 {
769 // In some cases it seems that the decode can return a null bitmap without throwing
770 // an exception
771 m_log.WarnFormat("[PHYSICS]: OpenJPEG decoded sculpt data for {0} to a null bitmap. Ignoring.", primName);
772 return false;
773 }
774 }
775 catch (DllNotFoundException)
776 {
777 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!");
778 return false;
779 }
780 catch (IndexOutOfRangeException)
781 {
782 m_log.Error("[PHYSICS]: OpenJpeg was unable to decode this. Physics Proxy generation failed");
783 return false;
784 }
785 catch (Exception ex)
786 {
787 m_log.Error("[PHYSICS]: Unable to generate a Sculpty physics proxy. Sculpty texture decode failed: " + ex.Message);
788 return false;
789 }
790
791 PrimMesher.SculptMesh.SculptType sculptType;
792 // remove mirror and invert bits
793 OpenMetaverse.SculptType pbsSculptType = ((OpenMetaverse.SculptType)(primShape.SculptType & 0x3f));
794 switch (pbsSculptType)
795 {
796 case OpenMetaverse.SculptType.Cylinder:
797 sculptType = PrimMesher.SculptMesh.SculptType.cylinder;
798 break;
799 case OpenMetaverse.SculptType.Plane:
800 sculptType = PrimMesher.SculptMesh.SculptType.plane;
801 break;
802 case OpenMetaverse.SculptType.Torus:
803 sculptType = PrimMesher.SculptMesh.SculptType.torus;
804 break;
805 case OpenMetaverse.SculptType.Sphere:
806 sculptType = PrimMesher.SculptMesh.SculptType.sphere;
807 break;
808 default:
809 sculptType = PrimMesher.SculptMesh.SculptType.plane;
810 break;
811 }
812
813 bool mirror = ((primShape.SculptType & 128) != 0);
814 bool invert = ((primShape.SculptType & 64) != 0);
815
816 sculptMesh = new PrimMesher.SculptMesh((Bitmap)idata, sculptType, (int)lod, mirror, invert);
817
818 idata.Dispose();
819
820// sculptMesh.DumpRaw(baseDir, primName, "primMesh");
821
822 coords = sculptMesh.coords;
823 faces = sculptMesh.faces;
824
825 return true;
826 }
827
828 /// <summary>
829 /// Generate the co-ords and faces necessary to construct a mesh from the shape data the accompanies a prim.
830 /// </summary>
831 /// <param name="primName"></param>
832 /// <param name="primShape"></param>
833 /// <param name="size"></param>
834 /// <param name="coords">Coords are added to this list by the method.</param>
835 /// <param name="faces">Faces are added to this list by the method.</param>
836 /// <returns>true if coords and faces were successfully generated, false if not</returns>
837 private bool GenerateCoordsAndFacesFromPrimShapeData(
838 string primName, PrimitiveBaseShape primShape, float lod, out List<Coord> coords, out List<Face> faces)
839 {
840 PrimMesh primMesh;
841 coords = new List<Coord>();
842 faces = new List<Face>();
843
844 float pathShearX = primShape.PathShearX < 128 ? (float)primShape.PathShearX * 0.01f : (float)(primShape.PathShearX - 256) * 0.01f;
845 float pathShearY = primShape.PathShearY < 128 ? (float)primShape.PathShearY * 0.01f : (float)(primShape.PathShearY - 256) * 0.01f;
846 float pathBegin = (float)primShape.PathBegin * 2.0e-5f;
847 float pathEnd = 1.0f - (float)primShape.PathEnd * 2.0e-5f;
848 float pathScaleX = (float)(primShape.PathScaleX - 100) * 0.01f;
849 float pathScaleY = (float)(primShape.PathScaleY - 100) * 0.01f;
850
851 float profileBegin = (float)primShape.ProfileBegin * 2.0e-5f;
852 float profileEnd = 1.0f - (float)primShape.ProfileEnd * 2.0e-5f;
853
854 if (profileBegin < 0.0f)
855 profileBegin = 0.0f;
856
857 if (profileEnd < 0.02f)
858 profileEnd = 0.02f;
859 else if (profileEnd > 1.0f)
860 profileEnd = 1.0f;
861
862 if (profileBegin >= profileEnd)
863 profileBegin = profileEnd - 0.02f;
864
865 float profileHollow = (float)primShape.ProfileHollow * 2.0e-5f;
866 if (profileHollow > 0.95f)
867 profileHollow = 0.95f;
868
869 int sides = 4;
870 LevelOfDetail iLOD = (LevelOfDetail)lod;
871 byte profshape = (byte)(primShape.ProfileCurve & 0x07);
872
873 if (profshape == (byte)ProfileShape.EquilateralTriangle
874 || profshape == (byte)ProfileShape.IsometricTriangle
875 || profshape == (byte)ProfileShape.RightTriangle)
876 sides = 3;
877 else if (profshape == (byte)ProfileShape.Circle)
878 {
879 switch (iLOD)
880 {
881 case LevelOfDetail.High: sides = 24; break;
882 case LevelOfDetail.Medium: sides = 12; break;
883 case LevelOfDetail.Low: sides = 6; break;
884 case LevelOfDetail.VeryLow: sides = 3; break;
885 default: sides = 24; break;
886 }
887 }
888 else if (profshape == (byte)ProfileShape.HalfCircle)
889 { // half circle, prim is a sphere
890 switch (iLOD)
891 {
892 case LevelOfDetail.High: sides = 24; break;
893 case LevelOfDetail.Medium: sides = 12; break;
894 case LevelOfDetail.Low: sides = 6; break;
895 case LevelOfDetail.VeryLow: sides = 3; break;
896 default: sides = 24; break;
897 }
898
899 profileBegin = 0.5f * profileBegin + 0.5f;
900 profileEnd = 0.5f * profileEnd + 0.5f;
901 }
902
903 int hollowSides = sides;
904 if (primShape.HollowShape == HollowShape.Circle)
905 {
906 switch (iLOD)
907 {
908 case LevelOfDetail.High: hollowSides = 24; break;
909 case LevelOfDetail.Medium: hollowSides = 12; break;
910 case LevelOfDetail.Low: hollowSides = 6; break;
911 case LevelOfDetail.VeryLow: hollowSides = 3; break;
912 default: hollowSides = 24; break;
913 }
914 }
915 else if (primShape.HollowShape == HollowShape.Square)
916 hollowSides = 4;
917 else if (primShape.HollowShape == HollowShape.Triangle)
918 {
919 if (profshape == (byte)ProfileShape.HalfCircle)
920 hollowSides = 6;
921 else
922 hollowSides = 3;
923 }
924
925 primMesh = new PrimMesh(sides, profileBegin, profileEnd, profileHollow, hollowSides);
926
927 if (primMesh.errorMessage != null)
928 if (primMesh.errorMessage.Length > 0)
929 m_log.Error("[ERROR] " + primMesh.errorMessage);
930
931 primMesh.topShearX = pathShearX;
932 primMesh.topShearY = pathShearY;
933 primMesh.pathCutBegin = pathBegin;
934 primMesh.pathCutEnd = pathEnd;
935
936 if (primShape.PathCurve == (byte)Extrusion.Straight || primShape.PathCurve == (byte) Extrusion.Flexible)
937 {
938 primMesh.twistBegin = (primShape.PathTwistBegin * 18) / 10;
939 primMesh.twistEnd = (primShape.PathTwist * 18) / 10;
940 primMesh.taperX = pathScaleX;
941 primMesh.taperY = pathScaleY;
942
943#if SPAM
944 m_log.Debug("****** PrimMesh Parameters (Linear) ******\n" + primMesh.ParamsToDisplayString());
945#endif
946 try
947 {
948 primMesh.ExtrudeLinear();
949 }
950 catch (Exception ex)
951 {
952 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
953 return false;
954 }
955 }
956 else
957 {
958 primMesh.holeSizeX = (200 - primShape.PathScaleX) * 0.01f;
959 primMesh.holeSizeY = (200 - primShape.PathScaleY) * 0.01f;
960 primMesh.radius = 0.01f * primShape.PathRadiusOffset;
961 primMesh.revolutions = 1.0f + 0.015f * primShape.PathRevolutions;
962 primMesh.skew = 0.01f * primShape.PathSkew;
963 primMesh.twistBegin = (primShape.PathTwistBegin * 36) / 10;
964 primMesh.twistEnd = (primShape.PathTwist * 36) / 10;
965 primMesh.taperX = primShape.PathTaperX * 0.01f;
966 primMesh.taperY = primShape.PathTaperY * 0.01f;
967
968#if SPAM
969 m_log.Debug("****** PrimMesh Parameters (Circular) ******\n" + primMesh.ParamsToDisplayString());
970#endif
971 try
972 {
973 primMesh.ExtrudeCircular();
974 }
975 catch (Exception ex)
976 {
977 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
978 return false;
979 }
980 }
981
982// primMesh.DumpRaw(baseDir, primName, "primMesh");
983
984 coords = primMesh.coords;
985 faces = primMesh.faces;
986
987 return true;
988 }
989
990 public AMeshKey GetMeshUniqueKey(PrimitiveBaseShape primShape, Vector3 size, byte lod, bool convex)
991 {
992 AMeshKey key = new AMeshKey();
993 Byte[] someBytes;
994
995 key.hashB = 5181;
996 key.hashC = 5181;
997 ulong hash = 5381;
998
999 if (primShape.SculptEntry)
1000 {
1001 key.uuid = primShape.SculptTexture;
1002 key.hashC = mdjb2(key.hashC, primShape.SculptType);
1003 key.hashC = mdjb2(key.hashC, primShape.PCode);
1004 }
1005 else
1006 {
1007 hash = mdjb2(hash, primShape.PathCurve);
1008 hash = mdjb2(hash, (byte)primShape.HollowShape);
1009 hash = mdjb2(hash, (byte)primShape.ProfileShape);
1010 hash = mdjb2(hash, primShape.PathBegin);
1011 hash = mdjb2(hash, primShape.PathEnd);
1012 hash = mdjb2(hash, primShape.PathScaleX);
1013 hash = mdjb2(hash, primShape.PathScaleY);
1014 hash = mdjb2(hash, primShape.PathShearX);
1015 key.hashA = hash;
1016 hash = key.hashB;
1017 hash = mdjb2(hash, primShape.PathShearY);
1018 hash = mdjb2(hash, (byte)primShape.PathTwist);
1019 hash = mdjb2(hash, (byte)primShape.PathTwistBegin);
1020 hash = mdjb2(hash, (byte)primShape.PathRadiusOffset);
1021 hash = mdjb2(hash, (byte)primShape.PathTaperX);
1022 hash = mdjb2(hash, (byte)primShape.PathTaperY);
1023 hash = mdjb2(hash, primShape.PathRevolutions);
1024 hash = mdjb2(hash, (byte)primShape.PathSkew);
1025 hash = mdjb2(hash, primShape.ProfileBegin);
1026 hash = mdjb2(hash, primShape.ProfileEnd);
1027 hash = mdjb2(hash, primShape.ProfileHollow);
1028 hash = mdjb2(hash, primShape.PCode);
1029 key.hashB = hash;
1030 }
1031
1032 hash = key.hashC;
1033
1034 hash = mdjb2(hash, lod);
1035
1036 if (size == m_MeshUnitSize)
1037 {
1038 hash = hash << 8;
1039 hash |= 8;
1040 }
1041 else
1042 {
1043 someBytes = size.GetBytes();
1044 for (int i = 0; i < someBytes.Length; i++)
1045 hash = mdjb2(hash, someBytes[i]);
1046 hash = hash << 8;
1047 }
1048
1049 if (convex)
1050 hash |= 4;
1051
1052 if (primShape.SculptEntry)
1053 {
1054 hash |= 1;
1055 if (primShape.SculptType == (byte)SculptType.Mesh)
1056 hash |= 2;
1057 }
1058
1059 key.hashC = hash;
1060
1061 return key;
1062 }
1063
1064 private ulong mdjb2(ulong hash, byte c)
1065 {
1066 return ((hash << 5) + hash) + (ulong)c;
1067 }
1068
1069 private ulong mdjb2(ulong hash, ushort c)
1070 {
1071 hash = ((hash << 5) + hash) + (ulong)((byte)c);
1072 return ((hash << 5) + hash) + (ulong)(c >> 8);
1073 }
1074
1075 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
1076 {
1077 return CreateMesh(primName, primShape, size, lod, false,false,false);
1078 }
1079
1080 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
1081 {
1082 return CreateMesh(primName, primShape, size, lod, false,false,false);
1083 }
1084
1085 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache, bool convex, bool forOde)
1086 {
1087 return CreateMesh(primName, primShape, size, lod, false, false, false);
1088 }
1089
1090 public IMesh GetMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex)
1091 {
1092 Mesh mesh = null;
1093
1094 if (size.X < 0.01f) size.X = 0.01f;
1095 if (size.Y < 0.01f) size.Y = 0.01f;
1096 if (size.Z < 0.01f) size.Z = 0.01f;
1097
1098 AMeshKey key = GetMeshUniqueKey(primShape, size, (byte)lod, convex);
1099 lock (m_uniqueMeshes)
1100 {
1101 m_uniqueMeshes.TryGetValue(key, out mesh);
1102
1103 if (mesh != null)
1104 {
1105 mesh.RefCount++;
1106 return mesh;
1107 }
1108
1109 // try to find a identical mesh on meshs recently released
1110 lock (m_uniqueReleasedMeshes)
1111 {
1112 m_uniqueReleasedMeshes.TryGetValue(key, out mesh);
1113 if (mesh != null)
1114 {
1115 m_uniqueReleasedMeshes.Remove(key);
1116 try
1117 {
1118 m_uniqueMeshes.Add(key, mesh);
1119 }
1120 catch { }
1121 mesh.RefCount = 1;
1122 return mesh;
1123 }
1124 }
1125 }
1126 return null;
1127 }
1128
1129 private static Vector3 m_MeshUnitSize = new Vector3(1.0f, 1.0f, 1.0f);
1130
1131 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex, bool forOde)
1132 {
1133#if SPAM
1134 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName);
1135#endif
1136
1137 Mesh mesh = null;
1138
1139 if (size.X < 0.01f) size.X = 0.01f;
1140 if (size.Y < 0.01f) size.Y = 0.01f;
1141 if (size.Z < 0.01f) size.Z = 0.01f;
1142
1143 // try to find a identical mesh on meshs in use
1144
1145 AMeshKey key = GetMeshUniqueKey(primShape,size,(byte)lod, convex);
1146
1147 lock (m_uniqueMeshes)
1148 {
1149 m_uniqueMeshes.TryGetValue(key, out mesh);
1150
1151 if (mesh != null)
1152 {
1153 mesh.RefCount++;
1154 return mesh;
1155 }
1156
1157 // try to find a identical mesh on meshs recently released
1158 lock (m_uniqueReleasedMeshes)
1159 {
1160 m_uniqueReleasedMeshes.TryGetValue(key, out mesh);
1161 if (mesh != null)
1162 {
1163 m_uniqueReleasedMeshes.Remove(key);
1164 try
1165 {
1166 m_uniqueMeshes.Add(key, mesh);
1167 }
1168 catch { }
1169 mesh.RefCount = 1;
1170 return mesh;
1171 }
1172 }
1173 }
1174
1175 Mesh UnitMesh = null;
1176 AMeshKey unitKey = GetMeshUniqueKey(primShape, m_MeshUnitSize, (byte)lod, convex);
1177
1178 lock (m_uniqueReleasedMeshes)
1179 {
1180 m_uniqueReleasedMeshes.TryGetValue(unitKey, out UnitMesh);
1181 if (UnitMesh != null)
1182 {
1183 UnitMesh.RefCount = 1;
1184 }
1185 }
1186
1187 if (UnitMesh == null && primShape.SculptEntry && doMeshFileCache)
1188 UnitMesh = GetFromFileCache(unitKey);
1189
1190 if (UnitMesh == null)
1191 {
1192 UnitMesh = CreateMeshFromPrimMesher(primName, primShape, lod, convex);
1193
1194 if (UnitMesh == null)
1195 return null;
1196
1197 UnitMesh.DumpRaw(baseDir, unitKey.ToString(), "Z");
1198
1199 if (forOde)
1200 {
1201 // force pinned mem allocation
1202 UnitMesh.PrepForOde();
1203 }
1204 else
1205 UnitMesh.TrimExcess();
1206
1207 UnitMesh.Key = unitKey;
1208 UnitMesh.RefCount = 1;
1209
1210 if (doMeshFileCache && primShape.SculptEntry)
1211 StoreToFileCache(unitKey, UnitMesh);
1212
1213 lock (m_uniqueReleasedMeshes)
1214 {
1215 try
1216 {
1217 m_uniqueReleasedMeshes.Add(unitKey, UnitMesh);
1218 }
1219 catch { }
1220 }
1221 }
1222
1223 mesh = UnitMesh.Scale(size);
1224 mesh.Key = key;
1225 mesh.RefCount = 1;
1226 lock (m_uniqueMeshes)
1227 {
1228 try
1229 {
1230 m_uniqueMeshes.Add(key, mesh);
1231 }
1232 catch { }
1233 }
1234
1235 return mesh;
1236 }
1237
1238 public void ReleaseMesh(IMesh imesh)
1239 {
1240 if (imesh == null)
1241 return;
1242
1243 Mesh mesh = (Mesh)imesh;
1244
1245 lock (m_uniqueMeshes)
1246 {
1247 int curRefCount = mesh.RefCount;
1248 curRefCount--;
1249
1250 if (curRefCount > 0)
1251 {
1252 mesh.RefCount = curRefCount;
1253 return;
1254 }
1255
1256 mesh.RefCount = 0;
1257 m_uniqueMeshes.Remove(mesh.Key);
1258 lock (m_uniqueReleasedMeshes)
1259 {
1260 try
1261 {
1262 m_uniqueReleasedMeshes.Add(mesh.Key, mesh);
1263 }
1264 catch { }
1265 }
1266 }
1267 }
1268
1269 public void ExpireReleaseMeshs()
1270 {
1271 if (m_uniqueReleasedMeshes.Count == 0)
1272 return;
1273
1274 List<Mesh> meshstodelete = new List<Mesh>();
1275 int refcntr;
1276
1277 lock (m_uniqueReleasedMeshes)
1278 {
1279 foreach (Mesh m in m_uniqueReleasedMeshes.Values)
1280 {
1281 refcntr = m.RefCount;
1282 refcntr--;
1283 if (refcntr > -6)
1284 m.RefCount = refcntr;
1285 else
1286 meshstodelete.Add(m);
1287 }
1288
1289 foreach (Mesh m in meshstodelete)
1290 {
1291 m_uniqueReleasedMeshes.Remove(m.Key);
1292 m.releaseBuildingMeshData();
1293 m.releasePinned();
1294 }
1295 }
1296 }
1297
1298 public void FileNames(AMeshKey key, out string dir,out string fullFileName)
1299 {
1300 string id = key.ToString();
1301 string init = id.Substring(0, 1);
1302 dir = System.IO.Path.Combine(cachePath, init);
1303 fullFileName = System.IO.Path.Combine(dir, id);
1304 }
1305
1306 public string FullFileName(AMeshKey key)
1307 {
1308 string id = key.ToString();
1309 string init = id.Substring(0,1);
1310 id = System.IO.Path.Combine(init, id);
1311 id = System.IO.Path.Combine(cachePath, id);
1312 return id;
1313 }
1314
1315 private Mesh GetFromFileCache(AMeshKey key)
1316 {
1317 Mesh mesh = null;
1318 string filename = FullFileName(key);
1319 bool ok = true;
1320
1321 lock (diskLock)
1322 {
1323 if (File.Exists(filename))
1324 {
1325 FileStream stream = null;
1326 try
1327 {
1328 stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
1329 BinaryFormatter bformatter = new BinaryFormatter();
1330
1331 mesh = Mesh.FromStream(stream, key);
1332
1333 }
1334 catch (Exception e)
1335 {
1336 ok = false;
1337 m_log.ErrorFormat(
1338 "[MESH CACHE]: Failed to get file {0}. Exception {1} {2}",
1339 filename, e.Message, e.StackTrace);
1340 }
1341
1342 if (stream != null)
1343 stream.Close();
1344
1345 if (mesh == null || !ok)
1346 File.Delete(filename);
1347 else
1348 File.SetLastAccessTimeUtc(filename, DateTime.UtcNow);
1349 }
1350 }
1351
1352 return mesh;
1353 }
1354
1355 private void StoreToFileCache(AMeshKey key, Mesh mesh)
1356 {
1357 Stream stream = null;
1358 bool ok = false;
1359
1360 // Make sure the target cache directory exists
1361 string dir = String.Empty;
1362 string filename = String.Empty;
1363
1364 FileNames(key, out dir, out filename);
1365
1366 lock (diskLock)
1367 {
1368 try
1369 {
1370 if (!Directory.Exists(dir))
1371 {
1372 Directory.CreateDirectory(dir);
1373 }
1374
1375 stream = File.Open(filename, FileMode.Create);
1376 ok = mesh.ToStream(stream);
1377 }
1378 catch (IOException e)
1379 {
1380 m_log.ErrorFormat(
1381 "[MESH CACHE]: Failed to write file {0}. Exception {1} {2}.",
1382 filename, e.Message, e.StackTrace);
1383 ok = false;
1384 }
1385
1386 if (stream != null)
1387 stream.Close();
1388
1389 if (File.Exists(filename))
1390 {
1391 if (ok)
1392 File.SetLastAccessTimeUtc(filename, DateTime.UtcNow);
1393 else
1394 File.Delete(filename);
1395 }
1396 }
1397 }
1398
1399 public void ExpireFileCache()
1400 {
1401 if (!doCacheExpire)
1402 return;
1403
1404 string controlfile = System.IO.Path.Combine(cachePath, "cntr");
1405
1406 lock (diskLock)
1407 {
1408 try
1409 {
1410 if (File.Exists(controlfile))
1411 {
1412 int ndeleted = 0;
1413 int totalfiles = 0;
1414 int ndirs = 0;
1415 DateTime OlderTime = File.GetLastAccessTimeUtc(controlfile) - CacheExpire;
1416 File.SetLastAccessTimeUtc(controlfile, DateTime.UtcNow);
1417
1418 foreach (string dir in Directory.GetDirectories(cachePath))
1419 {
1420 try
1421 {
1422 foreach (string file in Directory.GetFiles(dir))
1423 {
1424 try
1425 {
1426 if (File.GetLastAccessTimeUtc(file) < OlderTime)
1427 {
1428 File.Delete(file);
1429 ndeleted++;
1430 }
1431 }
1432 catch { }
1433 totalfiles++;
1434 }
1435 }
1436 catch { }
1437 ndirs++;
1438 }
1439
1440 if (ndeleted == 0)
1441 m_log.InfoFormat("[MESH CACHE]: {0} Files in {1} cache folders, no expires",
1442 totalfiles,ndirs);
1443 else
1444 m_log.InfoFormat("[MESH CACHE]: {0} Files in {1} cache folders, expired {2} files accessed before {3}",
1445 totalfiles,ndirs, ndeleted, OlderTime.ToString());
1446 }
1447 else
1448 {
1449 m_log.Info("[MESH CACHE]: Expire delayed to next startup");
1450 FileStream fs = File.Create(controlfile,4096,FileOptions.WriteThrough);
1451 fs.Close();
1452 }
1453 }
1454 catch { }
1455 }
1456 }
1457 }
1458}
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..6881e26
--- /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.UbitMeshing")]
10[assembly: AssemblyDescription("")]
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.UbitMeshing", 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}