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