aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/UbitMeshing
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/Physics/UbitMeshing/HelperTypes.cs340
-rw-r--r--OpenSim/Region/Physics/UbitMeshing/Mesh.cs614
-rw-r--r--OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs1410
-rw-r--r--OpenSim/Region/Physics/UbitMeshing/PrimMesher.cs2324
-rw-r--r--OpenSim/Region/Physics/UbitMeshing/SculptMap.cs244
-rw-r--r--OpenSim/Region/Physics/UbitMeshing/SculptMesh.cs220
6 files changed, 5152 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/UbitMeshing/HelperTypes.cs b/OpenSim/Region/Physics/UbitMeshing/HelperTypes.cs
new file mode 100644
index 0000000..2938257
--- /dev/null
+++ b/OpenSim/Region/Physics/UbitMeshing/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.Physics.Manager;
34using OpenSim.Region.Physics.Meshing;
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 = 3;
260
261 String s1 = X.ToString("N2", nfi) + " " + Y.ToString("N2", nfi) + " " + Z.ToString("N2", 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/Physics/UbitMeshing/Mesh.cs b/OpenSim/Region/Physics/UbitMeshing/Mesh.cs
new file mode 100644
index 0000000..b67422f
--- /dev/null
+++ b/OpenSim/Region/Physics/UbitMeshing/Mesh.cs
@@ -0,0 +1,614 @@
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.Physics.Manager;
33using PrimMesher;
34using OpenMetaverse;
35using System.Runtime.Serialization;
36using System.Runtime.Serialization.Formatters.Binary;
37
38namespace OpenSim.Region.Physics.Meshing
39{
40 public class MeshBuildingData
41 {
42 public Dictionary<Vertex, int> m_vertices;
43 public List<Triangle> m_triangles;
44 public float m_obbXmin;
45 public float m_obbXmax;
46 public float m_obbYmin;
47 public float m_obbYmax;
48 public float m_obbZmin;
49 public float m_obbZmax;
50 public Vector3 m_centroid;
51 public int m_centroidDiv;
52 }
53
54 [Serializable()]
55 public class Mesh : IMesh
56 {
57 float[] vertices;
58 int[] indexes;
59 Vector3 m_obb;
60 Vector3 m_obboffset;
61 [NonSerialized()]
62 MeshBuildingData m_bdata;
63 [NonSerialized()]
64 GCHandle vhandler;
65 [NonSerialized()]
66 GCHandle ihandler;
67 [NonSerialized()]
68 IntPtr m_verticesPtr = IntPtr.Zero;
69 [NonSerialized()]
70 IntPtr m_indicesPtr = IntPtr.Zero;
71 [NonSerialized()]
72 int m_vertexCount = 0;
73 [NonSerialized()]
74 int m_indexCount = 0;
75
76 public int RefCount { get; set; }
77 public AMeshKey Key { get; set; }
78
79 private class vertexcomp : IEqualityComparer<Vertex>
80 {
81 public bool Equals(Vertex v1, Vertex v2)
82 {
83 if (v1.X == v2.X && v1.Y == v2.Y && v1.Z == v2.Z)
84 return true;
85 else
86 return false;
87 }
88 public int GetHashCode(Vertex v)
89 {
90 int a = v.X.GetHashCode();
91 int b = v.Y.GetHashCode();
92 int c = v.Z.GetHashCode();
93 return (a << 16) ^ (b << 8) ^ c;
94 }
95 }
96
97 public Mesh()
98 {
99 vertexcomp vcomp = new vertexcomp();
100
101 m_bdata = new MeshBuildingData();
102 m_bdata.m_vertices = new Dictionary<Vertex, int>(vcomp);
103 m_bdata.m_triangles = new List<Triangle>();
104 m_bdata.m_centroid = Vector3.Zero;
105 m_bdata.m_centroidDiv = 0;
106 m_bdata.m_obbXmin = float.MaxValue;
107 m_bdata.m_obbXmax = float.MinValue;
108 m_bdata.m_obbYmin = float.MaxValue;
109 m_bdata.m_obbYmax = float.MinValue;
110 m_bdata.m_obbZmin = float.MaxValue;
111 m_bdata.m_obbZmax = float.MinValue;
112 m_obb = new Vector3(0.5f, 0.5f, 0.5f);
113 m_obboffset = Vector3.Zero;
114 }
115
116
117 public Mesh Scale(Vector3 scale)
118 {
119 if (m_verticesPtr == null || m_indicesPtr == null)
120 return null;
121
122 Mesh result = new Mesh();
123
124 float x = scale.X;
125 float y = scale.Y;
126 float z = scale.Z;
127
128 result.m_obb.X = m_obb.X * x;
129 result.m_obb.Y = m_obb.Y * y;
130 result.m_obb.Z = m_obb.Z * z;
131 result.m_obboffset.X = m_obboffset.X * x;
132 result.m_obboffset.Y = m_obboffset.Y * y;
133 result.m_obboffset.Z = m_obboffset.Z * z;
134
135 result.vertices = new float[vertices.Length];
136 int j = 0;
137 for (int i = 0; i < m_vertexCount; i++)
138 {
139 result.vertices[j] = vertices[j] * x;
140 j++;
141 result.vertices[j] = vertices[j] * y;
142 j++;
143 result.vertices[j] = vertices[j] * z;
144 j++;
145 }
146
147 result.indexes = new int[indexes.Length];
148 indexes.CopyTo(result.indexes,0);
149
150 result.pinMemory();
151
152 return result;
153 }
154
155 public Mesh Clone()
156 {
157 Mesh result = new Mesh();
158
159 if (m_bdata != null)
160 {
161 result.m_bdata = new MeshBuildingData();
162 foreach (Triangle t in m_bdata.m_triangles)
163 {
164 result.Add(new Triangle(t.v1.Clone(), t.v2.Clone(), t.v3.Clone()));
165 }
166 result.m_bdata.m_centroid = m_bdata.m_centroid;
167 result.m_bdata.m_centroidDiv = m_bdata.m_centroidDiv;
168 result.m_bdata.m_obbXmin = m_bdata.m_obbXmin;
169 result.m_bdata.m_obbXmax = m_bdata.m_obbXmax;
170 result.m_bdata.m_obbYmin = m_bdata.m_obbYmin;
171 result.m_bdata.m_obbYmax = m_bdata.m_obbYmax;
172 result.m_bdata.m_obbZmin = m_bdata.m_obbZmin;
173 result.m_bdata.m_obbZmax = m_bdata.m_obbZmax;
174 }
175 result.m_obb = m_obb;
176 result.m_obboffset = m_obboffset;
177 return result;
178 }
179
180 public void addVertexLStats(Vertex v)
181 {
182 float x = v.X;
183 float y = v.Y;
184 float z = v.Z;
185
186 m_bdata.m_centroid.X += x;
187 m_bdata.m_centroid.Y += y;
188 m_bdata.m_centroid.Z += z;
189 m_bdata.m_centroidDiv++;
190
191 if (x > m_bdata.m_obbXmax)
192 m_bdata.m_obbXmax = x;
193 else if (x < m_bdata.m_obbXmin)
194 m_bdata.m_obbXmin = x;
195
196 if (y > m_bdata.m_obbYmax)
197 m_bdata.m_obbYmax = y;
198 else if (y < m_bdata.m_obbYmin)
199 m_bdata.m_obbYmin = y;
200
201 if (z > m_bdata.m_obbZmax)
202 m_bdata.m_obbZmax = z;
203 else if (z < m_bdata.m_obbZmin)
204 m_bdata.m_obbZmin = z;
205
206 }
207
208 private float fRound(float f)
209 {
210 int i;
211 if (f == 0f)
212 return f;
213 else if (f > 0f)
214 i = (int)(1e5f * f + 0.5f);
215 else
216 i = (int)(1e5f * f - 0.5f);
217
218 return ((float)i * 1e-5f);
219 }
220
221 public void Add(Triangle triangle)
222 {
223 if (m_indicesPtr != IntPtr.Zero || m_verticesPtr != IntPtr.Zero)
224 throw new NotSupportedException("Attempt to Add to a pinned Mesh");
225
226 // round down
227 triangle.v1.X = fRound(triangle.v1.X);
228 triangle.v1.Y = fRound(triangle.v1.Y);
229 triangle.v1.Z = fRound(triangle.v1.Z);
230 triangle.v2.X = fRound(triangle.v2.X);
231 triangle.v2.Y = fRound(triangle.v2.Y);
232 triangle.v2.Z = fRound(triangle.v2.Z);
233 triangle.v3.X = fRound(triangle.v3.X);
234 triangle.v3.Y = fRound(triangle.v3.Y);
235 triangle.v3.Z = fRound(triangle.v3.Z);
236
237 if ((triangle.v1.X == triangle.v2.X && triangle.v1.Y == triangle.v2.Y && triangle.v1.Z == triangle.v2.Z)
238 || (triangle.v1.X == triangle.v3.X && triangle.v1.Y == triangle.v3.Y && triangle.v1.Z == triangle.v3.Z)
239 || (triangle.v2.X == triangle.v3.X && triangle.v2.Y == triangle.v3.Y && triangle.v2.Z == triangle.v3.Z)
240 )
241 {
242 return;
243 }
244
245 if (m_bdata.m_vertices.Count == 0)
246 {
247 m_bdata.m_centroidDiv = 0;
248 m_bdata.m_centroid = Vector3.Zero;
249 }
250
251 if (!m_bdata.m_vertices.ContainsKey(triangle.v1))
252 {
253 m_bdata.m_vertices[triangle.v1] = m_bdata.m_vertices.Count;
254 addVertexLStats(triangle.v1);
255 }
256 if (!m_bdata.m_vertices.ContainsKey(triangle.v2))
257 {
258 m_bdata.m_vertices[triangle.v2] = m_bdata.m_vertices.Count;
259 addVertexLStats(triangle.v2);
260 }
261 if (!m_bdata.m_vertices.ContainsKey(triangle.v3))
262 {
263 m_bdata.m_vertices[triangle.v3] = m_bdata.m_vertices.Count;
264 addVertexLStats(triangle.v3);
265 }
266 m_bdata.m_triangles.Add(triangle);
267 }
268
269 public Vector3 GetCentroid()
270 {
271 return m_obboffset;
272
273 }
274
275 public Vector3 GetOBB()
276 {
277 return m_obb;
278 float x, y, z;
279 if (m_bdata.m_centroidDiv > 0)
280 {
281 x = (m_bdata.m_obbXmax - m_bdata.m_obbXmin) * 0.5f;
282 y = (m_bdata.m_obbYmax - m_bdata.m_obbYmin) * 0.5f;
283 z = (m_bdata.m_obbZmax - m_bdata.m_obbZmin) * 0.5f;
284 }
285 else // ??
286 {
287 x = 0.5f;
288 y = 0.5f;
289 z = 0.5f;
290 }
291 return new Vector3(x, y, z);
292 }
293
294 public List<Vector3> getVertexList()
295 {
296 List<Vector3> result = new List<Vector3>();
297 foreach (Vertex v in m_bdata.m_vertices.Keys)
298 {
299 result.Add(new Vector3(v.X, v.Y, v.Z));
300 }
301 return result;
302 }
303
304 private float[] getVertexListAsFloat()
305 {
306 if (m_bdata.m_vertices == null)
307 throw new NotSupportedException();
308 float[] result = new float[m_bdata.m_vertices.Count * 3];
309 foreach (KeyValuePair<Vertex, int> kvp in m_bdata.m_vertices)
310 {
311 Vertex v = kvp.Key;
312 int i = kvp.Value;
313 result[3 * i + 0] = v.X;
314 result[3 * i + 1] = v.Y;
315 result[3 * i + 2] = v.Z;
316 }
317 return result;
318 }
319
320 public float[] getVertexListAsFloatLocked()
321 {
322 return null;
323 }
324
325 public void getVertexListAsPtrToFloatArray(out IntPtr _vertices, out int vertexStride, out int vertexCount)
326 {
327 // A vertex is 3 floats
328 vertexStride = 3 * sizeof(float);
329
330 // If there isn't an unmanaged array allocated yet, do it now
331 if (m_verticesPtr == IntPtr.Zero && m_bdata != null)
332 {
333 vertices = getVertexListAsFloat();
334 // Each vertex is 3 elements (floats)
335 m_vertexCount = vertices.Length / 3;
336 vhandler = GCHandle.Alloc(vertices, GCHandleType.Pinned);
337 m_verticesPtr = vhandler.AddrOfPinnedObject();
338 GC.AddMemoryPressure(Buffer.ByteLength(vertices));
339 }
340 _vertices = m_verticesPtr;
341 vertexCount = m_vertexCount;
342 }
343
344 public int[] getIndexListAsInt()
345 {
346 if (m_bdata.m_triangles == null)
347 throw new NotSupportedException();
348 int[] result = new int[m_bdata.m_triangles.Count * 3];
349 for (int i = 0; i < m_bdata.m_triangles.Count; i++)
350 {
351 Triangle t = m_bdata.m_triangles[i];
352 result[3 * i + 0] = m_bdata.m_vertices[t.v1];
353 result[3 * i + 1] = m_bdata.m_vertices[t.v2];
354 result[3 * i + 2] = m_bdata.m_vertices[t.v3];
355 }
356 return result;
357 }
358
359 /// <summary>
360 /// creates a list of index values that defines triangle faces. THIS METHOD FREES ALL NON-PINNED MESH DATA
361 /// </summary>
362 /// <returns></returns>
363 public int[] getIndexListAsIntLocked()
364 {
365 return null;
366 }
367
368 public void getIndexListAsPtrToIntArray(out IntPtr indices, out int triStride, out int indexCount)
369 {
370 // If there isn't an unmanaged array allocated yet, do it now
371 if (m_indicesPtr == IntPtr.Zero && m_bdata != null)
372 {
373 indexes = getIndexListAsInt();
374 m_indexCount = indexes.Length;
375 ihandler = GCHandle.Alloc(indexes, GCHandleType.Pinned);
376 m_indicesPtr = ihandler.AddrOfPinnedObject();
377 GC.AddMemoryPressure(Buffer.ByteLength(indexes));
378 }
379 // A triangle is 3 ints (indices)
380 triStride = 3 * sizeof(int);
381 indices = m_indicesPtr;
382 indexCount = m_indexCount;
383 }
384
385 public void releasePinned()
386 {
387 if (m_verticesPtr != IntPtr.Zero)
388 {
389 vhandler.Free();
390 vertices = null;
391 m_verticesPtr = IntPtr.Zero;
392 }
393 if (m_indicesPtr != IntPtr.Zero)
394 {
395 ihandler.Free();
396 indexes = null;
397 m_indicesPtr = IntPtr.Zero;
398 }
399 }
400
401 /// <summary>
402 /// frees up the source mesh data to minimize memory - call this method after calling get*Locked() functions
403 /// </summary>
404 public void releaseSourceMeshData()
405 {
406 if (m_bdata != null)
407 {
408 m_bdata.m_triangles = null;
409 m_bdata.m_vertices = null;
410 }
411 }
412
413 public void releaseBuildingMeshData()
414 {
415 if (m_bdata != null)
416 {
417 m_bdata.m_triangles = null;
418 m_bdata.m_vertices = null;
419 m_bdata = null;
420 }
421 }
422
423 public void Append(IMesh newMesh)
424 {
425 if (m_indicesPtr != IntPtr.Zero || m_verticesPtr != IntPtr.Zero)
426 throw new NotSupportedException("Attempt to Append to a pinned Mesh");
427
428 if (!(newMesh is Mesh))
429 return;
430
431 foreach (Triangle t in ((Mesh)newMesh).m_bdata.m_triangles)
432 Add(t);
433 }
434
435 // Do a linear transformation of mesh.
436 public void TransformLinear(float[,] matrix, float[] offset)
437 {
438 if (m_indicesPtr != IntPtr.Zero || m_verticesPtr != IntPtr.Zero)
439 throw new NotSupportedException("Attempt to TransformLinear a pinned Mesh");
440
441 foreach (Vertex v in m_bdata.m_vertices.Keys)
442 {
443 if (v == null)
444 continue;
445 float x, y, z;
446 x = v.X*matrix[0, 0] + v.Y*matrix[1, 0] + v.Z*matrix[2, 0];
447 y = v.X*matrix[0, 1] + v.Y*matrix[1, 1] + v.Z*matrix[2, 1];
448 z = v.X*matrix[0, 2] + v.Y*matrix[1, 2] + v.Z*matrix[2, 2];
449 v.X = x + offset[0];
450 v.Y = y + offset[1];
451 v.Z = z + offset[2];
452 }
453 }
454
455 public void DumpRaw(String path, String name, String title)
456 {
457 if (path == null)
458 return;
459 if (m_bdata == null)
460 return;
461 String fileName = name + "_" + title + ".raw";
462 String completePath = System.IO.Path.Combine(path, fileName);
463 StreamWriter sw = new StreamWriter(completePath);
464 foreach (Triangle t in m_bdata.m_triangles)
465 {
466 String s = t.ToStringRaw();
467 sw.WriteLine(s);
468 }
469 sw.Close();
470 }
471
472 public void TrimExcess()
473 {
474 m_bdata.m_triangles.TrimExcess();
475 }
476
477 public void pinMemory()
478 {
479 m_vertexCount = vertices.Length / 3;
480 vhandler = GCHandle.Alloc(vertices, GCHandleType.Pinned);
481 m_verticesPtr = vhandler.AddrOfPinnedObject();
482 GC.AddMemoryPressure(Buffer.ByteLength(vertices));
483
484 m_indexCount = indexes.Length;
485 ihandler = GCHandle.Alloc(indexes, GCHandleType.Pinned);
486 m_indicesPtr = ihandler.AddrOfPinnedObject();
487 GC.AddMemoryPressure(Buffer.ByteLength(indexes));
488 }
489
490 public void PrepForOde()
491 {
492 // If there isn't an unmanaged array allocated yet, do it now
493 if (m_verticesPtr == IntPtr.Zero)
494 vertices = getVertexListAsFloat();
495
496 // If there isn't an unmanaged array allocated yet, do it now
497 if (m_indicesPtr == IntPtr.Zero)
498 indexes = getIndexListAsInt();
499
500 pinMemory();
501
502 float x, y, z;
503
504 if (m_bdata.m_centroidDiv > 0)
505 {
506 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);
507 x = (m_bdata.m_obbXmax - m_bdata.m_obbXmin) * 0.5f;
508 y = (m_bdata.m_obbYmax - m_bdata.m_obbYmin) * 0.5f;
509 z = (m_bdata.m_obbZmax - m_bdata.m_obbZmin) * 0.5f;
510 }
511
512 else
513 {
514 m_obboffset = Vector3.Zero;
515 x = 0.5f;
516 y = 0.5f;
517 z = 0.5f;
518 }
519 m_obb = new Vector3(x, y, z);
520
521 releaseBuildingMeshData();
522 }
523 public bool ToStream(Stream st)
524 {
525 if (m_indicesPtr == IntPtr.Zero || m_verticesPtr == IntPtr.Zero)
526 return false;
527
528 BinaryWriter bw = new BinaryWriter(st);
529 bool ok = true;
530
531 try
532 {
533
534 bw.Write(m_vertexCount);
535 bw.Write(m_indexCount);
536
537 for (int i = 0; i < 3 * m_vertexCount; i++)
538 bw.Write(vertices[i]);
539 for (int i = 0; i < m_indexCount; i++)
540 bw.Write(indexes[i]);
541 bw.Write(m_obb.X);
542 bw.Write(m_obb.Y);
543 bw.Write(m_obb.Z);
544 bw.Write(m_obboffset.X);
545 bw.Write(m_obboffset.Y);
546 bw.Write(m_obboffset.Z);
547 }
548 catch
549 {
550 ok = false;
551 }
552
553 if (bw != null)
554 {
555 bw.Flush();
556 bw.Close();
557 }
558
559 return ok;
560 }
561
562 public static Mesh FromStream(Stream st, AMeshKey key)
563 {
564 Mesh mesh = new Mesh();
565 mesh.releaseBuildingMeshData();
566
567 BinaryReader br = new BinaryReader(st);
568
569 bool ok = true;
570 try
571 {
572 mesh.m_vertexCount = br.ReadInt32();
573 mesh.m_indexCount = br.ReadInt32();
574
575 int n = 3 * mesh.m_vertexCount;
576 mesh.vertices = new float[n];
577 for (int i = 0; i < n; i++)
578 mesh.vertices[i] = br.ReadSingle();
579
580 mesh.indexes = new int[mesh.m_indexCount];
581 for (int i = 0; i < mesh.m_indexCount; i++)
582 mesh.indexes[i] = br.ReadInt32();
583
584 mesh.m_obb.X = br.ReadSingle();
585 mesh.m_obb.Y = br.ReadSingle();
586 mesh.m_obb.Z = br.ReadSingle();
587
588 mesh.m_obboffset.X = br.ReadSingle();
589 mesh.m_obboffset.Y = br.ReadSingle();
590 mesh.m_obboffset.Z = br.ReadSingle();
591 }
592 catch
593 {
594 ok = false;
595 }
596
597 br.Close();
598
599 if (ok)
600 {
601 mesh.pinMemory();
602
603 mesh.Key = key;
604 mesh.RefCount = 1;
605
606 return mesh;
607 }
608
609 mesh.vertices = null;
610 mesh.indexes = null;
611 return null;
612 }
613 }
614}
diff --git a/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs b/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs
new file mode 100644
index 0000000..00cbfbd
--- /dev/null
+++ b/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs
@@ -0,0 +1,1410 @@
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.Physics.Manager;
33using OpenMetaverse;
34using OpenMetaverse.StructuredData;
35using System.Drawing;
36using System.Drawing.Imaging;
37using System.IO.Compression;
38using PrimMesher;
39using log4net;
40using Nini.Config;
41using System.Reflection;
42using System.IO;
43using ComponentAce.Compression.Libs.zlib;
44using OpenSim.Region.Physics.ConvexDecompositionDotNet;
45using System.Runtime.Serialization;
46using System.Runtime.Serialization.Formatters.Binary;
47
48namespace OpenSim.Region.Physics.Meshing
49{
50 public class MeshmerizerPlugin : IMeshingPlugin
51 {
52 public MeshmerizerPlugin()
53 {
54 }
55
56 public string GetName()
57 {
58 return "UbitMeshmerizer";
59 }
60
61 public IMesher GetMesher(IConfigSource config)
62 {
63 return new Meshmerizer(config);
64 }
65 }
66
67 public class Meshmerizer : IMesher
68 {
69 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
70
71 // Setting baseDir to a path will enable the dumping of raw files
72 // raw files can be imported by blender so a visual inspection of the results can be done
73
74 public object diskLock = new object();
75
76 public bool doMeshFileCache = true;
77
78 public string cachePath = "MeshCache";
79 public TimeSpan CacheExpire;
80 public bool doCacheExpire = true;
81
82// const string baseDir = "rawFiles";
83 private const string baseDir = null; //"rawFiles";
84
85 private bool useMeshiesPhysicsMesh = false;
86
87 private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh
88
89 private Dictionary<AMeshKey, Mesh> m_uniqueMeshes = new Dictionary<AMeshKey, Mesh>();
90 private Dictionary<AMeshKey, Mesh> m_uniqueReleasedMeshes = new Dictionary<AMeshKey, Mesh>();
91
92 public Meshmerizer(IConfigSource config)
93 {
94 IConfig start_config = config.Configs["Startup"];
95 IConfig mesh_config = config.Configs["Mesh"];
96
97
98 float fcache = 48.0f;
99// float fcache = 0.02f;
100
101 if(mesh_config != null)
102 {
103 useMeshiesPhysicsMesh = mesh_config.GetBoolean("UseMeshiesPhysicsMesh", useMeshiesPhysicsMesh);
104 if (useMeshiesPhysicsMesh)
105 {
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 else
112 {
113 doMeshFileCache = false;
114 doCacheExpire = false;
115 }
116 }
117
118 CacheExpire = TimeSpan.FromHours(fcache);
119
120 }
121
122 /// <summary>
123 /// creates a simple box mesh of the specified size. This mesh is of very low vertex count and may
124 /// be useful as a backup proxy when level of detail is not needed or when more complex meshes fail
125 /// for some reason
126 /// </summary>
127 /// <param name="minX"></param>
128 /// <param name="maxX"></param>
129 /// <param name="minY"></param>
130 /// <param name="maxY"></param>
131 /// <param name="minZ"></param>
132 /// <param name="maxZ"></param>
133 /// <returns></returns>
134 private static Mesh CreateSimpleBoxMesh(float minX, float maxX, float minY, float maxY, float minZ, float maxZ)
135 {
136 Mesh box = new Mesh();
137 List<Vertex> vertices = new List<Vertex>();
138 // bottom
139
140 vertices.Add(new Vertex(minX, maxY, minZ));
141 vertices.Add(new Vertex(maxX, maxY, minZ));
142 vertices.Add(new Vertex(maxX, minY, minZ));
143 vertices.Add(new Vertex(minX, minY, minZ));
144
145 box.Add(new Triangle(vertices[0], vertices[1], vertices[2]));
146 box.Add(new Triangle(vertices[0], vertices[2], vertices[3]));
147
148 // top
149
150 vertices.Add(new Vertex(maxX, maxY, maxZ));
151 vertices.Add(new Vertex(minX, maxY, maxZ));
152 vertices.Add(new Vertex(minX, minY, maxZ));
153 vertices.Add(new Vertex(maxX, minY, maxZ));
154
155 box.Add(new Triangle(vertices[4], vertices[5], vertices[6]));
156 box.Add(new Triangle(vertices[4], vertices[6], vertices[7]));
157
158 // sides
159
160 box.Add(new Triangle(vertices[5], vertices[0], vertices[3]));
161 box.Add(new Triangle(vertices[5], vertices[3], vertices[6]));
162
163 box.Add(new Triangle(vertices[1], vertices[0], vertices[5]));
164 box.Add(new Triangle(vertices[1], vertices[5], vertices[4]));
165
166 box.Add(new Triangle(vertices[7], vertices[1], vertices[4]));
167 box.Add(new Triangle(vertices[7], vertices[2], vertices[1]));
168
169 box.Add(new Triangle(vertices[3], vertices[2], vertices[7]));
170 box.Add(new Triangle(vertices[3], vertices[7], vertices[6]));
171
172 return box;
173 }
174
175 /// <summary>
176 /// Creates a simple bounding box mesh for a complex input mesh
177 /// </summary>
178 /// <param name="meshIn"></param>
179 /// <returns></returns>
180 private static Mesh CreateBoundingBoxMesh(Mesh meshIn)
181 {
182 float minX = float.MaxValue;
183 float maxX = float.MinValue;
184 float minY = float.MaxValue;
185 float maxY = float.MinValue;
186 float minZ = float.MaxValue;
187 float maxZ = float.MinValue;
188
189 foreach (Vector3 v in meshIn.getVertexList())
190 {
191 if (v.X < minX) minX = v.X;
192 if (v.Y < minY) minY = v.Y;
193 if (v.Z < minZ) minZ = v.Z;
194
195 if (v.X > maxX) maxX = v.X;
196 if (v.Y > maxY) maxY = v.Y;
197 if (v.Z > maxZ) maxZ = v.Z;
198 }
199
200 return CreateSimpleBoxMesh(minX, maxX, minY, maxY, minZ, maxZ);
201 }
202
203 private void ReportPrimError(string message, string primName, PrimMesh primMesh)
204 {
205 m_log.Error(message);
206 m_log.Error("\nPrim Name: " + primName);
207 m_log.Error("****** PrimMesh Parameters ******\n" + primMesh.ParamsToDisplayString());
208 }
209
210 /// <summary>
211 /// Add a submesh to an existing list of coords and faces.
212 /// </summary>
213 /// <param name="subMeshData"></param>
214 /// <param name="size">Size of entire object</param>
215 /// <param name="coords"></param>
216 /// <param name="faces"></param>
217 private void AddSubMesh(OSDMap subMeshData, List<Coord> coords, List<Face> faces)
218 {
219 // Console.WriteLine("subMeshMap for {0} - {1}", primName, Util.GetFormattedXml((OSD)subMeshMap));
220
221 // As per http://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format, some Mesh Level
222 // of Detail Blocks (maps) contain just a NoGeometry key to signal there is no
223 // geometry for this submesh.
224 if (subMeshData.ContainsKey("NoGeometry") && ((OSDBoolean)subMeshData["NoGeometry"]))
225 return;
226
227 OpenMetaverse.Vector3 posMax;
228 OpenMetaverse.Vector3 posMin;
229 if (subMeshData.ContainsKey("PositionDomain"))
230 {
231 posMax = ((OSDMap)subMeshData["PositionDomain"])["Max"].AsVector3();
232 posMin = ((OSDMap)subMeshData["PositionDomain"])["Min"].AsVector3();
233 }
234 else
235 {
236 posMax = new Vector3(0.5f, 0.5f, 0.5f);
237 posMin = new Vector3(-0.5f, -0.5f, -0.5f);
238 }
239
240 ushort faceIndexOffset = (ushort)coords.Count;
241
242 byte[] posBytes = subMeshData["Position"].AsBinary();
243 for (int i = 0; i < posBytes.Length; i += 6)
244 {
245 ushort uX = Utils.BytesToUInt16(posBytes, i);
246 ushort uY = Utils.BytesToUInt16(posBytes, i + 2);
247 ushort uZ = Utils.BytesToUInt16(posBytes, i + 4);
248
249 Coord c = new Coord(
250 Utils.UInt16ToFloat(uX, posMin.X, posMax.X),
251 Utils.UInt16ToFloat(uY, posMin.Y, posMax.Y),
252 Utils.UInt16ToFloat(uZ, posMin.Z, posMax.Z));
253
254 coords.Add(c);
255 }
256
257 byte[] triangleBytes = subMeshData["TriangleList"].AsBinary();
258 for (int i = 0; i < triangleBytes.Length; i += 6)
259 {
260 ushort v1 = (ushort)(Utils.BytesToUInt16(triangleBytes, i) + faceIndexOffset);
261 ushort v2 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 2) + faceIndexOffset);
262 ushort v3 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 4) + faceIndexOffset);
263 Face f = new Face(v1, v2, v3);
264 faces.Add(f);
265 }
266 }
267
268 /// <summary>
269 /// Create a physics mesh from data that comes with the prim. The actual data used depends on the prim type.
270 /// </summary>
271 /// <param name="primName"></param>
272 /// <param name="primShape"></param>
273 /// <param name="size"></param>
274 /// <param name="lod"></param>
275 /// <returns></returns>
276 private Mesh CreateMeshFromPrimMesher(string primName, PrimitiveBaseShape primShape, float lod, bool convex)
277 {
278// m_log.DebugFormat(
279// "[MESH]: Creating physics proxy for {0}, shape {1}",
280// primName, (OpenMetaverse.SculptType)primShape.SculptType);
281
282 List<Coord> coords;
283 List<Face> faces;
284
285 if (primShape.SculptEntry)
286 {
287 if (((OpenMetaverse.SculptType)primShape.SculptType) == SculptType.Mesh)
288 {
289 if (!useMeshiesPhysicsMesh)
290 return null;
291
292 if (!GenerateCoordsAndFacesFromPrimMeshData(primName, primShape, out coords, out faces, convex))
293 return null;
294 }
295 else
296 {
297 if (!GenerateCoordsAndFacesFromPrimSculptData(primName, primShape, lod, out coords, out faces))
298 return null;
299 }
300 }
301 else
302 {
303 if (!GenerateCoordsAndFacesFromPrimShapeData(primName, primShape, lod, out coords, out faces))
304 return null;
305 }
306
307 primShape.SculptData = Utils.EmptyBytes;
308
309 int numCoords = coords.Count;
310 int numFaces = faces.Count;
311
312 Mesh mesh = new Mesh();
313 // Add the corresponding triangles to the mesh
314 for (int i = 0; i < numFaces; i++)
315 {
316 Face f = faces[i];
317 mesh.Add(new Triangle(coords[f.v1].X, coords[f.v1].Y, coords[f.v1].Z,
318 coords[f.v2].X, coords[f.v2].Y, coords[f.v2].Z,
319 coords[f.v3].X, coords[f.v3].Y, coords[f.v3].Z));
320 }
321
322 coords.Clear();
323 faces.Clear();
324
325 return mesh;
326 }
327
328 /// <summary>
329 /// Generate the co-ords and faces necessary to construct a mesh from the mesh data the accompanies a prim.
330 /// </summary>
331 /// <param name="primName"></param>
332 /// <param name="primShape"></param>
333 /// <param name="size"></param>
334 /// <param name="coords">Coords are added to this list by the method.</param>
335 /// <param name="faces">Faces are added to this list by the method.</param>
336 /// <returns>true if coords and faces were successfully generated, false if not</returns>
337 private bool GenerateCoordsAndFacesFromPrimMeshData(
338 string primName, PrimitiveBaseShape primShape, out List<Coord> coords, out List<Face> faces, bool convex)
339 {
340// m_log.DebugFormat("[MESH]: experimental mesh proxy generation for {0}", primName);
341
342 bool usemesh = false;
343
344 coords = new List<Coord>();
345 faces = new List<Face>();
346 OSD meshOsd = null;
347
348 if (primShape.SculptData.Length <= 0)
349 {
350// m_log.InfoFormat("[MESH]: asset data for {0} is zero length", primName);
351 return false;
352 }
353
354 long start = 0;
355 using (MemoryStream data = new MemoryStream(primShape.SculptData))
356 {
357 try
358 {
359 OSD osd = OSDParser.DeserializeLLSDBinary(data);
360 if (osd is OSDMap)
361 meshOsd = (OSDMap)osd;
362 else
363 {
364 m_log.Warn("[Mesh}: unable to cast mesh asset to OSDMap");
365 return false;
366 }
367 }
368 catch (Exception e)
369 {
370 m_log.Error("[MESH]: Exception deserializing mesh asset header:" + e.ToString());
371 }
372
373 start = data.Position;
374 }
375
376 if (meshOsd is OSDMap)
377 {
378 OSDMap physicsParms = null;
379 OSDMap map = (OSDMap)meshOsd;
380
381 if (!convex)
382 {
383 if (map.ContainsKey("physics_shape"))
384 physicsParms = (OSDMap)map["physics_shape"]; // old asset format
385 else if (map.ContainsKey("physics_mesh"))
386 physicsParms = (OSDMap)map["physics_mesh"]; // new asset format
387
388 if (physicsParms != null)
389 usemesh = true;
390 }
391
392 if(!usemesh && (map.ContainsKey("physics_convex")))
393 physicsParms = (OSDMap)map["physics_convex"];
394
395
396 if (physicsParms == null)
397 {
398 m_log.Warn("[MESH]: unknown mesh type");
399 return false;
400 }
401
402 int physOffset = physicsParms["offset"].AsInteger() + (int)start;
403 int physSize = physicsParms["size"].AsInteger();
404
405 if (physOffset < 0 || physSize == 0)
406 return false; // no mesh data in asset
407
408 OSD decodedMeshOsd = new OSD();
409 byte[] meshBytes = new byte[physSize];
410 System.Buffer.BlockCopy(primShape.SculptData, physOffset, meshBytes, 0, physSize);
411
412 try
413 {
414 using (MemoryStream inMs = new MemoryStream(meshBytes))
415 {
416 using (MemoryStream outMs = new MemoryStream())
417 {
418 using (ZOutputStream zOut = new ZOutputStream(outMs))
419 {
420 byte[] readBuffer = new byte[2048];
421 int readLen = 0;
422 while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0)
423 {
424 zOut.Write(readBuffer, 0, readLen);
425 }
426 zOut.Flush();
427 outMs.Seek(0, SeekOrigin.Begin);
428
429 byte[] decompressedBuf = outMs.GetBuffer();
430
431 decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
432 }
433 }
434 }
435 }
436 catch (Exception e)
437 {
438 m_log.Error("[MESH]: exception decoding physical mesh: " + e.ToString());
439 return false;
440 }
441
442 if (usemesh)
443 {
444 OSDArray decodedMeshOsdArray = null;
445
446 // physics_shape is an array of OSDMaps, one for each submesh
447 if (decodedMeshOsd is OSDArray)
448 {
449// Console.WriteLine("decodedMeshOsd for {0} - {1}", primName, Util.GetFormattedXml(decodedMeshOsd));
450
451 decodedMeshOsdArray = (OSDArray)decodedMeshOsd;
452 foreach (OSD subMeshOsd in decodedMeshOsdArray)
453 {
454 if (subMeshOsd is OSDMap)
455 AddSubMesh(subMeshOsd as OSDMap, coords, faces);
456 }
457 }
458 }
459 else
460 {
461 OSDMap cmap = (OSDMap)decodedMeshOsd;
462 if (cmap == null)
463 return false;
464
465 byte[] data;
466
467 List<float3> vs = new List<float3>();
468 PHullResult hullr = new PHullResult();
469 float3 f3;
470 Coord c;
471 Face f;
472 Vector3 range;
473 Vector3 min;
474
475 const float invMaxU16 = 1.0f / 65535f;
476 int t1;
477 int t2;
478 int t3;
479 int i;
480 int nverts;
481 int nindexs;
482
483 if (cmap.ContainsKey("Max"))
484 range = cmap["Max"].AsVector3();
485 else
486 range = new Vector3(0.5f, 0.5f, 0.5f);
487
488 if (cmap.ContainsKey("Min"))
489 min = cmap["Min"].AsVector3();
490 else
491 min = new Vector3(-0.5f, -0.5f, -0.5f);
492
493 range = range - min;
494 range *= invMaxU16;
495
496 if (!convex && cmap.ContainsKey("HullList") && cmap.ContainsKey("Positions"))
497 {
498 List<int> hsizes = new List<int>();
499 int totalpoints = 0;
500 data = cmap["HullList"].AsBinary();
501 for (i = 0; i < data.Length; i++)
502 {
503 t1 = data[i];
504 if (t1 == 0)
505 t1 = 256;
506 totalpoints += t1;
507 hsizes.Add(t1);
508 }
509
510 data = cmap["Positions"].AsBinary();
511 int ptr = 0;
512 int vertsoffset = 0;
513
514 if (totalpoints == data.Length / 6) // 2 bytes per coord, 3 coords per point
515 {
516 foreach (int hullsize in hsizes)
517 {
518 for (i = 0; i < hullsize; i++ )
519 {
520 t1 = data[ptr++];
521 t1 += data[ptr++] << 8;
522 t2 = data[ptr++];
523 t2 += data[ptr++] << 8;
524 t3 = data[ptr++];
525 t3 += data[ptr++] << 8;
526
527 f3 = new float3((t1 * range.X + min.X),
528 (t2 * range.Y + min.Y),
529 (t3 * range.Z + min.Z));
530 vs.Add(f3);
531 }
532
533 if(hullsize <3)
534 {
535 vs.Clear();
536 continue;
537 }
538
539 if (hullsize <5)
540 {
541 foreach (float3 point in vs)
542 {
543 c.X = point.x;
544 c.Y = point.y;
545 c.Z = point.z;
546 coords.Add(c);
547 }
548 f = new Face(vertsoffset, vertsoffset + 1, vertsoffset + 2);
549 faces.Add(f);
550
551 if (hullsize == 4)
552 {
553 // not sure about orientation..
554 f = new Face(vertsoffset, vertsoffset + 2, vertsoffset + 3);
555 faces.Add(f);
556 f = new Face(vertsoffset, vertsoffset + 3, vertsoffset + 1);
557 faces.Add(f);
558 f = new Face(vertsoffset + 3, vertsoffset + 2, vertsoffset + 1);
559 faces.Add(f);
560 }
561 vertsoffset += vs.Count;
562 vs.Clear();
563 continue;
564 }
565
566 if (!HullUtils.ComputeHull(vs, ref hullr, 0, 0.0f))
567 {
568 vs.Clear();
569 continue;
570 }
571
572 nverts = hullr.Vertices.Count;
573 nindexs = hullr.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 = hullr.Vertices[i].x;
584 c.Y = hullr.Vertices[i].y;
585 c.Z = hullr.Vertices[i].z;
586 coords.Add(c);
587 }
588
589 for (i = 0; i < nindexs; i += 3)
590 {
591 t1 = hullr.Indices[i];
592 if (t1 > nverts)
593 break;
594 t2 = hullr.Indices[i + 1];
595 if (t2 > nverts)
596 break;
597 t3 = hullr.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
611 vs.Clear();
612
613 if (cmap.ContainsKey("BoundingVerts"))
614 {
615 data = cmap["BoundingVerts"].AsBinary();
616
617 for (i = 0; i < data.Length; )
618 {
619 t1 = data[i++];
620 t1 += data[i++] << 8;
621 t2 = data[i++];
622 t2 += data[i++] << 8;
623 t3 = data[i++];
624 t3 += data[i++] << 8;
625
626 f3 = new float3((t1 * range.X + min.X),
627 (t2 * range.Y + min.Y),
628 (t3 * range.Z + min.Z));
629 vs.Add(f3);
630 }
631
632 if (vs.Count < 3)
633 {
634 vs.Clear();
635 return false;
636 }
637
638 if (vs.Count < 5)
639 {
640 foreach (float3 point in vs)
641 {
642 c.X = point.x;
643 c.Y = point.y;
644 c.Z = point.z;
645 coords.Add(c);
646 }
647 f = new Face(0, 1, 2);
648 faces.Add(f);
649
650 if (vs.Count == 4)
651 {
652 f = new Face(0, 2, 3);
653 faces.Add(f);
654 f = new Face(0, 3, 1);
655 faces.Add(f);
656 f = new Face( 3, 2, 1);
657 faces.Add(f);
658 }
659 vs.Clear();
660 return true;
661 }
662
663 if (!HullUtils.ComputeHull(vs, ref hullr, 0, 0.0f))
664 return false;
665
666 nverts = hullr.Vertices.Count;
667 nindexs = hullr.Indices.Count;
668
669 if (nindexs % 3 != 0)
670 return false;
671
672 for (i = 0; i < nverts; i++)
673 {
674 c.X = hullr.Vertices[i].x;
675 c.Y = hullr.Vertices[i].y;
676 c.Z = hullr.Vertices[i].z;
677 coords.Add(c);
678 }
679 for (i = 0; i < nindexs; i += 3)
680 {
681 t1 = hullr.Indices[i];
682 if (t1 > nverts)
683 break;
684 t2 = hullr.Indices[i + 1];
685 if (t2 > nverts)
686 break;
687 t3 = hullr.Indices[i + 2];
688 if (t3 > nverts)
689 break;
690 f = new Face(t1, t2, t3);
691 faces.Add(f);
692 }
693
694 if (coords.Count > 0 && faces.Count > 0)
695 return true;
696 }
697 else
698 return false;
699 }
700 }
701
702 return true;
703 }
704
705 /// <summary>
706 /// Generate the co-ords and faces necessary to construct a mesh from the sculpt data the accompanies a prim.
707 /// </summary>
708 /// <param name="primName"></param>
709 /// <param name="primShape"></param>
710 /// <param name="size"></param>
711 /// <param name="lod"></param>
712 /// <param name="coords">Coords are added to this list by the method.</param>
713 /// <param name="faces">Faces are added to this list by the method.</param>
714 /// <returns>true if coords and faces were successfully generated, false if not</returns>
715 private bool GenerateCoordsAndFacesFromPrimSculptData(
716 string primName, PrimitiveBaseShape primShape, float lod, out List<Coord> coords, out List<Face> faces)
717 {
718 coords = new List<Coord>();
719 faces = new List<Face>();
720 PrimMesher.SculptMesh sculptMesh;
721 Image idata = null;
722
723 if (primShape.SculptData == null || primShape.SculptData.Length == 0)
724 return false;
725
726 try
727 {
728 OpenMetaverse.Imaging.ManagedImage unusedData;
729 OpenMetaverse.Imaging.OpenJPEG.DecodeToImage(primShape.SculptData, out unusedData, out idata);
730
731 unusedData = null;
732
733 if (idata == null)
734 {
735 // In some cases it seems that the decode can return a null bitmap without throwing
736 // an exception
737 m_log.WarnFormat("[PHYSICS]: OpenJPEG decoded sculpt data for {0} to a null bitmap. Ignoring.", primName);
738 return false;
739 }
740 }
741 catch (DllNotFoundException)
742 {
743 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!");
744 return false;
745 }
746 catch (IndexOutOfRangeException)
747 {
748 m_log.Error("[PHYSICS]: OpenJpeg was unable to decode this. Physics Proxy generation failed");
749 return false;
750 }
751 catch (Exception ex)
752 {
753 m_log.Error("[PHYSICS]: Unable to generate a Sculpty physics proxy. Sculpty texture decode failed: " + ex.Message);
754 return false;
755 }
756
757 PrimMesher.SculptMesh.SculptType sculptType;
758 // remove mirror and invert bits
759 OpenMetaverse.SculptType pbsSculptType = ((OpenMetaverse.SculptType)(primShape.SculptType & 0x3f));
760 switch (pbsSculptType)
761 {
762 case OpenMetaverse.SculptType.Cylinder:
763 sculptType = PrimMesher.SculptMesh.SculptType.cylinder;
764 break;
765 case OpenMetaverse.SculptType.Plane:
766 sculptType = PrimMesher.SculptMesh.SculptType.plane;
767 break;
768 case OpenMetaverse.SculptType.Torus:
769 sculptType = PrimMesher.SculptMesh.SculptType.torus;
770 break;
771 case OpenMetaverse.SculptType.Sphere:
772 sculptType = PrimMesher.SculptMesh.SculptType.sphere;
773 break;
774 default:
775 sculptType = PrimMesher.SculptMesh.SculptType.plane;
776 break;
777 }
778
779 bool mirror = ((primShape.SculptType & 128) != 0);
780 bool invert = ((primShape.SculptType & 64) != 0);
781
782 sculptMesh = new PrimMesher.SculptMesh((Bitmap)idata, sculptType, (int)lod, mirror, invert);
783
784 idata.Dispose();
785
786// sculptMesh.DumpRaw(baseDir, primName, "primMesh");
787
788 coords = sculptMesh.coords;
789 faces = sculptMesh.faces;
790
791 return true;
792 }
793
794 /// <summary>
795 /// Generate the co-ords and faces necessary to construct a mesh from the shape data the accompanies a prim.
796 /// </summary>
797 /// <param name="primName"></param>
798 /// <param name="primShape"></param>
799 /// <param name="size"></param>
800 /// <param name="coords">Coords are added to this list by the method.</param>
801 /// <param name="faces">Faces are added to this list by the method.</param>
802 /// <returns>true if coords and faces were successfully generated, false if not</returns>
803 private bool GenerateCoordsAndFacesFromPrimShapeData(
804 string primName, PrimitiveBaseShape primShape, float lod, out List<Coord> coords, out List<Face> faces)
805 {
806 PrimMesh primMesh;
807 coords = new List<Coord>();
808 faces = new List<Face>();
809
810 float pathShearX = primShape.PathShearX < 128 ? (float)primShape.PathShearX * 0.01f : (float)(primShape.PathShearX - 256) * 0.01f;
811 float pathShearY = primShape.PathShearY < 128 ? (float)primShape.PathShearY * 0.01f : (float)(primShape.PathShearY - 256) * 0.01f;
812 float pathBegin = (float)primShape.PathBegin * 2.0e-5f;
813 float pathEnd = 1.0f - (float)primShape.PathEnd * 2.0e-5f;
814 float pathScaleX = (float)(primShape.PathScaleX - 100) * 0.01f;
815 float pathScaleY = (float)(primShape.PathScaleY - 100) * 0.01f;
816
817 float profileBegin = (float)primShape.ProfileBegin * 2.0e-5f;
818 float profileEnd = 1.0f - (float)primShape.ProfileEnd * 2.0e-5f;
819 float profileHollow = (float)primShape.ProfileHollow * 2.0e-5f;
820 if (profileHollow > 0.95f)
821 profileHollow = 0.95f;
822
823 int sides = 4;
824 LevelOfDetail iLOD = (LevelOfDetail)lod;
825 if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
826 sides = 3;
827 else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
828 {
829 switch (iLOD)
830 {
831 case LevelOfDetail.High: sides = 24; break;
832 case LevelOfDetail.Medium: sides = 12; break;
833 case LevelOfDetail.Low: sides = 6; break;
834 case LevelOfDetail.VeryLow: sides = 3; break;
835 default: sides = 24; break;
836 }
837 }
838 else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
839 { // half circle, prim is a sphere
840 switch (iLOD)
841 {
842 case LevelOfDetail.High: sides = 24; break;
843 case LevelOfDetail.Medium: sides = 12; break;
844 case LevelOfDetail.Low: sides = 6; break;
845 case LevelOfDetail.VeryLow: sides = 3; break;
846 default: sides = 24; break;
847 }
848
849 profileBegin = 0.5f * profileBegin + 0.5f;
850 profileEnd = 0.5f * profileEnd + 0.5f;
851 }
852
853 int hollowSides = sides;
854 if (primShape.HollowShape == HollowShape.Circle)
855 {
856 switch (iLOD)
857 {
858 case LevelOfDetail.High: hollowSides = 24; break;
859 case LevelOfDetail.Medium: hollowSides = 12; break;
860 case LevelOfDetail.Low: hollowSides = 6; break;
861 case LevelOfDetail.VeryLow: hollowSides = 3; break;
862 default: hollowSides = 24; break;
863 }
864 }
865 else if (primShape.HollowShape == HollowShape.Square)
866 hollowSides = 4;
867 else if (primShape.HollowShape == HollowShape.Triangle)
868 hollowSides = 3;
869
870 primMesh = new PrimMesh(sides, profileBegin, profileEnd, profileHollow, hollowSides);
871
872 if (primMesh.errorMessage != null)
873 if (primMesh.errorMessage.Length > 0)
874 m_log.Error("[ERROR] " + primMesh.errorMessage);
875
876 primMesh.topShearX = pathShearX;
877 primMesh.topShearY = pathShearY;
878 primMesh.pathCutBegin = pathBegin;
879 primMesh.pathCutEnd = pathEnd;
880
881 if (primShape.PathCurve == (byte)Extrusion.Straight || primShape.PathCurve == (byte) Extrusion.Flexible)
882 {
883 primMesh.twistBegin = primShape.PathTwistBegin * 18 / 10;
884 primMesh.twistEnd = primShape.PathTwist * 18 / 10;
885 primMesh.taperX = pathScaleX;
886 primMesh.taperY = pathScaleY;
887
888 if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f)
889 {
890 ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh);
891 if (profileBegin < 0.0f) profileBegin = 0.0f;
892 if (profileEnd > 1.0f) profileEnd = 1.0f;
893 }
894#if SPAM
895 m_log.Debug("****** PrimMesh Parameters (Linear) ******\n" + primMesh.ParamsToDisplayString());
896#endif
897 try
898 {
899 primMesh.ExtrudeLinear();
900 }
901 catch (Exception ex)
902 {
903 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
904 return false;
905 }
906 }
907 else
908 {
909 primMesh.holeSizeX = (200 - primShape.PathScaleX) * 0.01f;
910 primMesh.holeSizeY = (200 - primShape.PathScaleY) * 0.01f;
911 primMesh.radius = 0.01f * primShape.PathRadiusOffset;
912 primMesh.revolutions = 1.0f + 0.015f * primShape.PathRevolutions;
913 primMesh.skew = 0.01f * primShape.PathSkew;
914 primMesh.twistBegin = primShape.PathTwistBegin * 36 / 10;
915 primMesh.twistEnd = primShape.PathTwist * 36 / 10;
916 primMesh.taperX = primShape.PathTaperX * 0.01f;
917 primMesh.taperY = primShape.PathTaperY * 0.01f;
918
919 if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f)
920 {
921 ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh);
922 if (profileBegin < 0.0f) profileBegin = 0.0f;
923 if (profileEnd > 1.0f) profileEnd = 1.0f;
924 }
925#if SPAM
926 m_log.Debug("****** PrimMesh Parameters (Circular) ******\n" + primMesh.ParamsToDisplayString());
927#endif
928 try
929 {
930 primMesh.ExtrudeCircular();
931 }
932 catch (Exception ex)
933 {
934 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
935 return false;
936 }
937 }
938
939// primMesh.DumpRaw(baseDir, primName, "primMesh");
940
941 coords = primMesh.coords;
942 faces = primMesh.faces;
943
944 return true;
945 }
946
947 public AMeshKey GetMeshUniqueKey(PrimitiveBaseShape primShape, Vector3 size, byte lod, bool convex)
948 {
949 AMeshKey key = new AMeshKey();
950 Byte[] someBytes;
951
952 key.hashB = 5181;
953 key.hashC = 5181;
954 ulong hash = 5381;
955
956 if (primShape.SculptEntry)
957 {
958 key.uuid = primShape.SculptTexture;
959 key.hashC = mdjb2(key.hashC, primShape.SculptType);
960 key.hashC = mdjb2(key.hashC, primShape.PCode);
961 }
962 else
963 {
964 hash = mdjb2(hash, primShape.PathCurve);
965 hash = mdjb2(hash, (byte)primShape.HollowShape);
966 hash = mdjb2(hash, (byte)primShape.ProfileShape);
967 hash = mdjb2(hash, primShape.PathBegin);
968 hash = mdjb2(hash, primShape.PathEnd);
969 hash = mdjb2(hash, primShape.PathScaleX);
970 hash = mdjb2(hash, primShape.PathScaleY);
971 hash = mdjb2(hash, primShape.PathShearX);
972 key.hashA = hash;
973 hash = key.hashB;
974 hash = mdjb2(hash, primShape.PathShearY);
975 hash = mdjb2(hash, (byte)primShape.PathTwist);
976 hash = mdjb2(hash, (byte)primShape.PathTwistBegin);
977 hash = mdjb2(hash, (byte)primShape.PathRadiusOffset);
978 hash = mdjb2(hash, (byte)primShape.PathTaperX);
979 hash = mdjb2(hash, (byte)primShape.PathTaperY);
980 hash = mdjb2(hash, primShape.PathRevolutions);
981 hash = mdjb2(hash, (byte)primShape.PathSkew);
982 hash = mdjb2(hash, primShape.ProfileBegin);
983 hash = mdjb2(hash, primShape.ProfileEnd);
984 hash = mdjb2(hash, primShape.ProfileHollow);
985 hash = mdjb2(hash, primShape.PCode);
986 key.hashB = hash;
987 }
988
989 hash = key.hashC;
990
991 hash = mdjb2(hash, lod);
992
993 if (size == m_MeshUnitSize)
994 {
995 hash = hash << 8;
996 hash |= 8;
997 }
998 else
999 {
1000 someBytes = size.GetBytes();
1001 for (int i = 0; i < someBytes.Length; i++)
1002 hash = mdjb2(hash, someBytes[i]);
1003 hash = hash << 8;
1004 }
1005
1006 if (convex)
1007 hash |= 4;
1008
1009 if (primShape.SculptEntry)
1010 {
1011 hash |= 1;
1012 if (primShape.SculptType == (byte)SculptType.Mesh)
1013 hash |= 2;
1014 }
1015
1016 key.hashC = hash;
1017
1018 return key;
1019 }
1020
1021 private ulong mdjb2(ulong hash, byte c)
1022 {
1023 return ((hash << 5) + hash) + (ulong)c;
1024 }
1025
1026 private ulong mdjb2(ulong hash, ushort c)
1027 {
1028 hash = ((hash << 5) + hash) + (ulong)((byte)c);
1029 return ((hash << 5) + hash) + (ulong)(c >> 8);
1030 }
1031
1032 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
1033 {
1034 return CreateMesh(primName, primShape, size, lod, false,false,false,false);
1035 }
1036
1037 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
1038 {
1039 return CreateMesh(primName, primShape, size, lod, false,false,false,false);
1040 }
1041
1042 public IMesh GetMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex)
1043 {
1044 Mesh mesh = null;
1045
1046 if (size.X < 0.01f) size.X = 0.01f;
1047 if (size.Y < 0.01f) size.Y = 0.01f;
1048 if (size.Z < 0.01f) size.Z = 0.01f;
1049
1050 AMeshKey key = GetMeshUniqueKey(primShape, size, (byte)lod, convex);
1051 lock (m_uniqueMeshes)
1052 {
1053 m_uniqueMeshes.TryGetValue(key, out mesh);
1054
1055 if (mesh != null)
1056 {
1057 mesh.RefCount++;
1058 return mesh;
1059 }
1060
1061 // try to find a identical mesh on meshs recently released
1062 lock (m_uniqueReleasedMeshes)
1063 {
1064 m_uniqueReleasedMeshes.TryGetValue(key, out mesh);
1065 if (mesh != null)
1066 {
1067 m_uniqueReleasedMeshes.Remove(key);
1068 try
1069 {
1070 m_uniqueMeshes.Add(key, mesh);
1071 }
1072 catch { }
1073 mesh.RefCount = 1;
1074 return mesh;
1075 }
1076 }
1077 }
1078 return null;
1079 }
1080
1081 private static Vector3 m_MeshUnitSize = new Vector3(1.0f, 1.0f, 1.0f);
1082
1083 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache, bool convex, bool forOde)
1084 {
1085#if SPAM
1086 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName);
1087#endif
1088
1089 Mesh mesh = null;
1090
1091 if (size.X < 0.01f) size.X = 0.01f;
1092 if (size.Y < 0.01f) size.Y = 0.01f;
1093 if (size.Z < 0.01f) size.Z = 0.01f;
1094
1095 // try to find a identical mesh on meshs in use
1096
1097 AMeshKey key = GetMeshUniqueKey(primShape,size,(byte)lod, convex);
1098
1099 lock (m_uniqueMeshes)
1100 {
1101 m_uniqueMeshes.TryGetValue(key, out mesh);
1102
1103 if (mesh != null)
1104 {
1105 mesh.RefCount++;
1106 return mesh;
1107 }
1108
1109 // try to find a identical mesh on meshs recently released
1110 lock (m_uniqueReleasedMeshes)
1111 {
1112 m_uniqueReleasedMeshes.TryGetValue(key, out mesh);
1113 if (mesh != null)
1114 {
1115 m_uniqueReleasedMeshes.Remove(key);
1116 try
1117 {
1118 m_uniqueMeshes.Add(key, mesh);
1119 }
1120 catch { }
1121 mesh.RefCount = 1;
1122 return mesh;
1123 }
1124 }
1125 }
1126
1127 Mesh UnitMesh = null;
1128 AMeshKey unitKey = GetMeshUniqueKey(primShape, m_MeshUnitSize, (byte)lod, convex);
1129
1130 lock (m_uniqueReleasedMeshes)
1131 {
1132 m_uniqueReleasedMeshes.TryGetValue(unitKey, out UnitMesh);
1133 if (UnitMesh != null)
1134 {
1135 UnitMesh.RefCount = 1;
1136 }
1137 }
1138
1139 if (UnitMesh == null && primShape.SculptEntry && doMeshFileCache)
1140 UnitMesh = GetFromFileCache(unitKey);
1141
1142 if (UnitMesh == null)
1143 {
1144 UnitMesh = CreateMeshFromPrimMesher(primName, primShape, lod, convex);
1145
1146 if (UnitMesh == null)
1147 return null;
1148
1149 UnitMesh.DumpRaw(baseDir, unitKey.ToString(), "Z");
1150
1151 if (forOde)
1152 {
1153 // force pinned mem allocation
1154 UnitMesh.PrepForOde();
1155 }
1156 else
1157 UnitMesh.TrimExcess();
1158
1159 UnitMesh.Key = unitKey;
1160 UnitMesh.RefCount = 1;
1161
1162 if (doMeshFileCache && primShape.SculptEntry)
1163 StoreToFileCache(unitKey, UnitMesh);
1164
1165 lock (m_uniqueReleasedMeshes)
1166 {
1167 try
1168 {
1169 m_uniqueReleasedMeshes.Add(unitKey, UnitMesh);
1170 }
1171 catch { }
1172 }
1173 }
1174
1175 mesh = UnitMesh.Scale(size);
1176 mesh.Key = key;
1177 mesh.RefCount = 1;
1178 lock (m_uniqueMeshes)
1179 {
1180 try
1181 {
1182 m_uniqueMeshes.Add(key, mesh);
1183 }
1184 catch { }
1185 }
1186
1187 return mesh;
1188 }
1189
1190 public void ReleaseMesh(IMesh imesh)
1191 {
1192 if (imesh == null)
1193 return;
1194
1195 Mesh mesh = (Mesh)imesh;
1196
1197 lock (m_uniqueMeshes)
1198 {
1199 int curRefCount = mesh.RefCount;
1200 curRefCount--;
1201
1202 if (curRefCount > 0)
1203 {
1204 mesh.RefCount = curRefCount;
1205 return;
1206 }
1207
1208 mesh.RefCount = 0;
1209 m_uniqueMeshes.Remove(mesh.Key);
1210 lock (m_uniqueReleasedMeshes)
1211 {
1212 try
1213 {
1214 m_uniqueReleasedMeshes.Add(mesh.Key, mesh);
1215 }
1216 catch { }
1217 }
1218 }
1219 }
1220
1221 public void ExpireReleaseMeshs()
1222 {
1223 if (m_uniqueReleasedMeshes.Count == 0)
1224 return;
1225
1226 List<Mesh> meshstodelete = new List<Mesh>();
1227 int refcntr;
1228
1229 lock (m_uniqueReleasedMeshes)
1230 {
1231 foreach (Mesh m in m_uniqueReleasedMeshes.Values)
1232 {
1233 refcntr = m.RefCount;
1234 refcntr--;
1235 if (refcntr > -6)
1236 m.RefCount = refcntr;
1237 else
1238 meshstodelete.Add(m);
1239 }
1240
1241 foreach (Mesh m in meshstodelete)
1242 {
1243 m_uniqueReleasedMeshes.Remove(m.Key);
1244 m.releaseBuildingMeshData();
1245 m.releasePinned();
1246 }
1247 }
1248 }
1249
1250 public void FileNames(AMeshKey key, out string dir,out string fullFileName)
1251 {
1252 string id = key.ToString();
1253 string init = id.Substring(0, 1);
1254 dir = System.IO.Path.Combine(cachePath, init);
1255 fullFileName = System.IO.Path.Combine(dir, id);
1256 }
1257
1258 public string FullFileName(AMeshKey key)
1259 {
1260 string id = key.ToString();
1261 string init = id.Substring(0,1);
1262 id = System.IO.Path.Combine(init, id);
1263 id = System.IO.Path.Combine(cachePath, id);
1264 return id;
1265 }
1266
1267 private Mesh GetFromFileCache(AMeshKey key)
1268 {
1269 Mesh mesh = null;
1270 string filename = FullFileName(key);
1271 bool ok = true;
1272
1273 lock (diskLock)
1274 {
1275 if (File.Exists(filename))
1276 {
1277 FileStream stream = null;
1278 try
1279 {
1280 stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
1281 BinaryFormatter bformatter = new BinaryFormatter();
1282
1283 mesh = Mesh.FromStream(stream, key);
1284
1285 }
1286 catch (Exception e)
1287 {
1288 ok = false;
1289 m_log.ErrorFormat(
1290 "[MESH CACHE]: Failed to get file {0}. Exception {1} {2}",
1291 filename, e.Message, e.StackTrace);
1292 }
1293
1294 if (stream != null)
1295 stream.Close();
1296
1297 if (mesh == null || !ok)
1298 File.Delete(filename);
1299 else
1300 File.SetLastAccessTimeUtc(filename, DateTime.UtcNow);
1301 }
1302 }
1303
1304 return mesh;
1305 }
1306
1307 private void StoreToFileCache(AMeshKey key, Mesh mesh)
1308 {
1309 Stream stream = null;
1310 bool ok = false;
1311
1312 // Make sure the target cache directory exists
1313 string dir = String.Empty;
1314 string filename = String.Empty;
1315
1316 FileNames(key, out dir, out filename);
1317
1318 lock (diskLock)
1319 {
1320 try
1321 {
1322 if (!Directory.Exists(dir))
1323 {
1324 Directory.CreateDirectory(dir);
1325 }
1326
1327 stream = File.Open(filename, FileMode.Create);
1328 ok = mesh.ToStream(stream);
1329 }
1330 catch (IOException e)
1331 {
1332 m_log.ErrorFormat(
1333 "[MESH CACHE]: Failed to write file {0}. Exception {1} {2}.",
1334 filename, e.Message, e.StackTrace);
1335 ok = false;
1336 }
1337
1338 if (stream != null)
1339 stream.Close();
1340
1341 if (File.Exists(filename))
1342 {
1343 if (ok)
1344 File.SetLastAccessTimeUtc(filename, DateTime.UtcNow);
1345 else
1346 File.Delete(filename);
1347 }
1348 }
1349 }
1350
1351 public void ExpireFileCache()
1352 {
1353 if (!doCacheExpire)
1354 return;
1355
1356 string controlfile = System.IO.Path.Combine(cachePath, "cntr");
1357
1358 lock (diskLock)
1359 {
1360 try
1361 {
1362 if (File.Exists(controlfile))
1363 {
1364 int ndeleted = 0;
1365 int totalfiles = 0;
1366 int ndirs = 0;
1367 DateTime OlderTime = File.GetLastAccessTimeUtc(controlfile) - CacheExpire;
1368 File.SetLastAccessTimeUtc(controlfile, DateTime.UtcNow);
1369
1370 foreach (string dir in Directory.GetDirectories(cachePath))
1371 {
1372 try
1373 {
1374 foreach (string file in Directory.GetFiles(dir))
1375 {
1376 try
1377 {
1378 if (File.GetLastAccessTimeUtc(file) < OlderTime)
1379 {
1380 File.Delete(file);
1381 ndeleted++;
1382 }
1383 }
1384 catch { }
1385 totalfiles++;
1386 }
1387 }
1388 catch { }
1389 ndirs++;
1390 }
1391
1392 if (ndeleted == 0)
1393 m_log.InfoFormat("[MESH CACHE]: {0} Files in {1} cache folders, no expires",
1394 totalfiles,ndirs);
1395 else
1396 m_log.InfoFormat("[MESH CACHE]: {0} Files in {1} cache folders, expired {2} files accessed before {3}",
1397 totalfiles,ndirs, ndeleted, OlderTime.ToString());
1398 }
1399 else
1400 {
1401 m_log.Info("[MESH CACHE]: Expire delayed to next startup");
1402 FileStream fs = File.Create(controlfile,4096,FileOptions.WriteThrough);
1403 fs.Close();
1404 }
1405 }
1406 catch { }
1407 }
1408 }
1409 }
1410}
diff --git a/OpenSim/Region/Physics/UbitMeshing/PrimMesher.cs b/OpenSim/Region/Physics/UbitMeshing/PrimMesher.cs
new file mode 100644
index 0000000..4049ee1
--- /dev/null
+++ b/OpenSim/Region/Physics/UbitMeshing/PrimMesher.cs
@@ -0,0 +1,2324 @@
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 UVCoord
229 {
230 public float U;
231 public float V;
232
233
234 public UVCoord(float u, float v)
235 {
236 this.U = u;
237 this.V = v;
238 }
239
240 public UVCoord Flip()
241 {
242 this.U = 1.0f - this.U;
243 this.V = 1.0f - this.V;
244 return this;
245 }
246 }
247
248 public struct Face
249 {
250 public int primFace;
251
252 // vertices
253 public int v1;
254 public int v2;
255 public int v3;
256
257 //normals
258 public int n1;
259 public int n2;
260 public int n3;
261
262 // uvs
263 public int uv1;
264 public int uv2;
265 public int uv3;
266
267 public Face(int v1, int v2, int v3)
268 {
269 primFace = 0;
270
271 this.v1 = v1;
272 this.v2 = v2;
273 this.v3 = v3;
274
275 this.n1 = 0;
276 this.n2 = 0;
277 this.n3 = 0;
278
279 this.uv1 = 0;
280 this.uv2 = 0;
281 this.uv3 = 0;
282
283 }
284
285 public Face(int v1, int v2, int v3, int n1, int n2, int n3)
286 {
287 primFace = 0;
288
289 this.v1 = v1;
290 this.v2 = v2;
291 this.v3 = v3;
292
293 this.n1 = n1;
294 this.n2 = n2;
295 this.n3 = n3;
296
297 this.uv1 = 0;
298 this.uv2 = 0;
299 this.uv3 = 0;
300 }
301
302 public Coord SurfaceNormal(List<Coord> coordList)
303 {
304 Coord c1 = coordList[this.v1];
305 Coord c2 = coordList[this.v2];
306 Coord c3 = coordList[this.v3];
307
308 Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z);
309 Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z);
310
311 return Coord.Cross(edge1, edge2).Normalize();
312 }
313 }
314
315 public struct ViewerFace
316 {
317 public int primFaceNumber;
318
319 public Coord v1;
320 public Coord v2;
321 public Coord v3;
322
323 public int coordIndex1;
324 public int coordIndex2;
325 public int coordIndex3;
326
327 public Coord n1;
328 public Coord n2;
329 public Coord n3;
330
331 public UVCoord uv1;
332 public UVCoord uv2;
333 public UVCoord uv3;
334
335 public ViewerFace(int primFaceNumber)
336 {
337 this.primFaceNumber = primFaceNumber;
338
339 this.v1 = new Coord();
340 this.v2 = new Coord();
341 this.v3 = new Coord();
342
343 this.coordIndex1 = this.coordIndex2 = this.coordIndex3 = -1; // -1 means not assigned yet
344
345 this.n1 = new Coord();
346 this.n2 = new Coord();
347 this.n3 = new Coord();
348
349 this.uv1 = new UVCoord();
350 this.uv2 = new UVCoord();
351 this.uv3 = new UVCoord();
352 }
353
354 public void Scale(float x, float y, float z)
355 {
356 this.v1.X *= x;
357 this.v1.Y *= y;
358 this.v1.Z *= z;
359
360 this.v2.X *= x;
361 this.v2.Y *= y;
362 this.v2.Z *= z;
363
364 this.v3.X *= x;
365 this.v3.Y *= y;
366 this.v3.Z *= z;
367 }
368
369 public void AddPos(float x, float y, float z)
370 {
371 this.v1.X += x;
372 this.v2.X += x;
373 this.v3.X += x;
374
375 this.v1.Y += y;
376 this.v2.Y += y;
377 this.v3.Y += y;
378
379 this.v1.Z += z;
380 this.v2.Z += z;
381 this.v3.Z += z;
382 }
383
384 public void AddRot(Quat q)
385 {
386 this.v1 *= q;
387 this.v2 *= q;
388 this.v3 *= q;
389
390 this.n1 *= q;
391 this.n2 *= q;
392 this.n3 *= q;
393 }
394
395 public void CalcSurfaceNormal()
396 {
397
398 Coord edge1 = new Coord(this.v2.X - this.v1.X, this.v2.Y - this.v1.Y, this.v2.Z - this.v1.Z);
399 Coord edge2 = new Coord(this.v3.X - this.v1.X, this.v3.Y - this.v1.Y, this.v3.Z - this.v1.Z);
400
401 this.n1 = this.n2 = this.n3 = Coord.Cross(edge1, edge2).Normalize();
402 }
403 }
404
405 internal struct Angle
406 {
407 internal float angle;
408 internal float X;
409 internal float Y;
410
411 internal Angle(float angle, float x, float y)
412 {
413 this.angle = angle;
414 this.X = x;
415 this.Y = y;
416 }
417 }
418
419 internal class AngleList
420 {
421 private float iX, iY; // intersection point
422
423 private static Angle[] angles3 =
424 {
425 new Angle(0.0f, 1.0f, 0.0f),
426 new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
427 new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
428 new Angle(1.0f, 1.0f, 0.0f)
429 };
430
431 private static Coord[] normals3 =
432 {
433 new Coord(0.25f, 0.4330127019f, 0.0f).Normalize(),
434 new Coord(-0.5f, 0.0f, 0.0f).Normalize(),
435 new Coord(0.25f, -0.4330127019f, 0.0f).Normalize(),
436 new Coord(0.25f, 0.4330127019f, 0.0f).Normalize()
437 };
438
439 private static Angle[] angles4 =
440 {
441 new Angle(0.0f, 1.0f, 0.0f),
442 new Angle(0.25f, 0.0f, 1.0f),
443 new Angle(0.5f, -1.0f, 0.0f),
444 new Angle(0.75f, 0.0f, -1.0f),
445 new Angle(1.0f, 1.0f, 0.0f)
446 };
447
448 private static Coord[] normals4 =
449 {
450 new Coord(0.5f, 0.5f, 0.0f).Normalize(),
451 new Coord(-0.5f, 0.5f, 0.0f).Normalize(),
452 new Coord(-0.5f, -0.5f, 0.0f).Normalize(),
453 new Coord(0.5f, -0.5f, 0.0f).Normalize(),
454 new Coord(0.5f, 0.5f, 0.0f).Normalize()
455 };
456
457 private static Angle[] angles24 =
458 {
459 new Angle(0.0f, 1.0f, 0.0f),
460 new Angle(0.041666666666666664f, 0.96592582628906831f, 0.25881904510252074f),
461 new Angle(0.083333333333333329f, 0.86602540378443871f, 0.5f),
462 new Angle(0.125f, 0.70710678118654757f, 0.70710678118654746f),
463 new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f),
464 new Angle(0.20833333333333331f, 0.25881904510252096f, 0.9659258262890682f),
465 new Angle(0.25f, 0.0f, 1.0f),
466 new Angle(0.29166666666666663f, -0.25881904510252063f, 0.96592582628906831f),
467 new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
468 new Angle(0.375f, -0.70710678118654746f, 0.70710678118654757f),
469 new Angle(0.41666666666666663f, -0.86602540378443849f, 0.5f),
470 new Angle(0.45833333333333331f, -0.9659258262890682f, 0.25881904510252102f),
471 new Angle(0.5f, -1.0f, 0.0f),
472 new Angle(0.54166666666666663f, -0.96592582628906842f, -0.25881904510252035f),
473 new Angle(0.58333333333333326f, -0.86602540378443882f, -0.5f),
474 new Angle(0.62499999999999989f, -0.70710678118654791f, -0.70710678118654713f),
475 new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
476 new Angle(0.70833333333333326f, -0.25881904510252152f, -0.96592582628906809f),
477 new Angle(0.75f, 0.0f, -1.0f),
478 new Angle(0.79166666666666663f, 0.2588190451025203f, -0.96592582628906842f),
479 new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f),
480 new Angle(0.875f, 0.70710678118654735f, -0.70710678118654768f),
481 new Angle(0.91666666666666663f, 0.86602540378443837f, -0.5f),
482 new Angle(0.95833333333333326f, 0.96592582628906809f, -0.25881904510252157f),
483 new Angle(1.0f, 1.0f, 0.0f)
484 };
485
486 private Angle interpolatePoints(float newPoint, Angle p1, Angle p2)
487 {
488 float m = (newPoint - p1.angle) / (p2.angle - p1.angle);
489 return new Angle(newPoint, p1.X + m * (p2.X - p1.X), p1.Y + m * (p2.Y - p1.Y));
490 }
491
492 private void intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
493 { // ref: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
494 double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
495 double uaNumerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
496
497 if (denom != 0.0)
498 {
499 double ua = uaNumerator / denom;
500 iX = (float)(x1 + ua * (x2 - x1));
501 iY = (float)(y1 + ua * (y2 - y1));
502 }
503 }
504
505 internal List<Angle> angles;
506 internal List<Coord> normals;
507
508 internal void makeAngles(int sides, float startAngle, float stopAngle)
509 {
510 angles = new List<Angle>();
511 normals = new List<Coord>();
512
513 double twoPi = System.Math.PI * 2.0;
514 float twoPiInv = 1.0f / (float)twoPi;
515
516 if (sides < 1)
517 throw new Exception("number of sides not greater than zero");
518 if (stopAngle <= startAngle)
519 throw new Exception("stopAngle not greater than startAngle");
520
521 if ((sides == 3 || sides == 4 || sides == 24))
522 {
523 startAngle *= twoPiInv;
524 stopAngle *= twoPiInv;
525
526 Angle[] sourceAngles;
527 if (sides == 3)
528 sourceAngles = angles3;
529 else if (sides == 4)
530 sourceAngles = angles4;
531 else sourceAngles = angles24;
532
533 int startAngleIndex = (int)(startAngle * sides);
534 int endAngleIndex = sourceAngles.Length - 1;
535 if (stopAngle < 1.0f)
536 endAngleIndex = (int)(stopAngle * sides) + 1;
537 if (endAngleIndex == startAngleIndex)
538 endAngleIndex++;
539
540 for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex + 1; angleIndex++)
541 {
542 angles.Add(sourceAngles[angleIndex]);
543 if (sides == 3)
544 normals.Add(normals3[angleIndex]);
545 else if (sides == 4)
546 normals.Add(normals4[angleIndex]);
547 }
548
549 if (startAngle > 0.0f)
550 angles[0] = interpolatePoints(startAngle, angles[0], angles[1]);
551
552 if (stopAngle < 1.0f)
553 {
554 int lastAngleIndex = angles.Count - 1;
555 angles[lastAngleIndex] = interpolatePoints(stopAngle, angles[lastAngleIndex - 1], angles[lastAngleIndex]);
556 }
557 }
558 else
559 {
560 double stepSize = twoPi / sides;
561
562 int startStep = (int)(startAngle / stepSize);
563 double angle = stepSize * startStep;
564 int step = startStep;
565 double stopAngleTest = stopAngle;
566 if (stopAngle < twoPi)
567 {
568 stopAngleTest = stepSize * ((int)(stopAngle / stepSize) + 1);
569 if (stopAngleTest < stopAngle)
570 stopAngleTest += stepSize;
571 if (stopAngleTest > twoPi)
572 stopAngleTest = twoPi;
573 }
574
575 while (angle <= stopAngleTest)
576 {
577 Angle newAngle;
578 newAngle.angle = (float)angle;
579 newAngle.X = (float)System.Math.Cos(angle);
580 newAngle.Y = (float)System.Math.Sin(angle);
581 angles.Add(newAngle);
582 step += 1;
583 angle = stepSize * step;
584 }
585
586 if (startAngle > angles[0].angle)
587 {
588 Angle newAngle;
589 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));
590 newAngle.angle = startAngle;
591 newAngle.X = iX;
592 newAngle.Y = iY;
593 angles[0] = newAngle;
594 }
595
596 int index = angles.Count - 1;
597 if (stopAngle < angles[index].angle)
598 {
599 Angle newAngle;
600 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));
601 newAngle.angle = stopAngle;
602 newAngle.X = iX;
603 newAngle.Y = iY;
604 angles[index] = newAngle;
605 }
606 }
607 }
608 }
609
610 /// <summary>
611 /// generates a profile for extrusion
612 /// </summary>
613 public class Profile
614 {
615 private const float twoPi = 2.0f * (float)Math.PI;
616
617 public string errorMessage = null;
618
619 public List<Coord> coords;
620 public List<Face> faces;
621 public List<Coord> vertexNormals;
622 public List<float> us;
623 public List<UVCoord> faceUVs;
624 public List<int> faceNumbers;
625
626 // use these for making individual meshes for each prim face
627 public List<int> outerCoordIndices = null;
628 public List<int> hollowCoordIndices = null;
629 public List<int> cut1CoordIndices = null;
630 public List<int> cut2CoordIndices = null;
631
632 public Coord faceNormal = new Coord(0.0f, 0.0f, 1.0f);
633 public Coord cutNormal1 = new Coord();
634 public Coord cutNormal2 = new Coord();
635
636 public int numOuterVerts = 0;
637 public int numHollowVerts = 0;
638
639 public int outerFaceNumber = -1;
640 public int hollowFaceNumber = -1;
641
642 public bool calcVertexNormals = false;
643 public int bottomFaceNumber = 0;
644 public int numPrimFaces = 0;
645
646 public Profile()
647 {
648 this.coords = new List<Coord>();
649 this.faces = new List<Face>();
650 this.vertexNormals = new List<Coord>();
651 this.us = new List<float>();
652 this.faceUVs = new List<UVCoord>();
653 this.faceNumbers = new List<int>();
654 }
655
656 public Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals)
657 {
658 this.calcVertexNormals = calcVertexNormals;
659 this.coords = new List<Coord>();
660 this.faces = new List<Face>();
661 this.vertexNormals = new List<Coord>();
662 this.us = new List<float>();
663 this.faceUVs = new List<UVCoord>();
664 this.faceNumbers = new List<int>();
665
666 Coord center = new Coord(0.0f, 0.0f, 0.0f);
667
668 List<Coord> hollowCoords = new List<Coord>();
669 List<Coord> hollowNormals = new List<Coord>();
670 List<float> hollowUs = new List<float>();
671
672 if (calcVertexNormals)
673 {
674 this.outerCoordIndices = new List<int>();
675 this.hollowCoordIndices = new List<int>();
676 this.cut1CoordIndices = new List<int>();
677 this.cut2CoordIndices = new List<int>();
678 }
679
680 bool hasHollow = (hollow > 0.0f);
681
682 bool hasProfileCut = (profileStart > 0.0f || profileEnd < 1.0f);
683
684 AngleList angles = new AngleList();
685 AngleList hollowAngles = new AngleList();
686
687 float xScale = 0.5f;
688 float yScale = 0.5f;
689 if (sides == 4) // corners of a square are sqrt(2) from center
690 {
691 xScale = 0.707107f;
692 yScale = 0.707107f;
693 }
694
695 float startAngle = profileStart * twoPi;
696 float stopAngle = profileEnd * twoPi;
697
698 try { angles.makeAngles(sides, startAngle, stopAngle); }
699 catch (Exception ex)
700 {
701
702 errorMessage = "makeAngles failed: Exception: " + ex.ToString()
703 + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString();
704
705 return;
706 }
707
708 this.numOuterVerts = angles.angles.Count;
709
710 // flag to create as few triangles as possible for 3 or 4 side profile
711 bool simpleFace = (sides < 5 && !hasHollow && !hasProfileCut);
712
713 if (hasHollow)
714 {
715 if (sides == hollowSides)
716 hollowAngles = angles;
717 else
718 {
719 try { hollowAngles.makeAngles(hollowSides, startAngle, stopAngle); }
720 catch (Exception ex)
721 {
722 errorMessage = "makeAngles failed: Exception: " + ex.ToString()
723 + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString();
724
725 return;
726 }
727 }
728 this.numHollowVerts = hollowAngles.angles.Count;
729 }
730 else if (!simpleFace)
731 {
732 this.coords.Add(center);
733 if (this.calcVertexNormals)
734 this.vertexNormals.Add(new Coord(0.0f, 0.0f, 1.0f));
735 this.us.Add(0.0f);
736 }
737
738 float z = 0.0f;
739
740 Angle angle;
741 Coord newVert = new Coord();
742 if (hasHollow && hollowSides != sides)
743 {
744 int numHollowAngles = hollowAngles.angles.Count;
745 for (int i = 0; i < numHollowAngles; i++)
746 {
747 angle = hollowAngles.angles[i];
748 newVert.X = hollow * xScale * angle.X;
749 newVert.Y = hollow * yScale * angle.Y;
750 newVert.Z = z;
751
752 hollowCoords.Add(newVert);
753 if (this.calcVertexNormals)
754 {
755 if (hollowSides < 5)
756 hollowNormals.Add(hollowAngles.normals[i].Invert());
757 else
758 hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f));
759
760 if (hollowSides == 4)
761 hollowUs.Add(angle.angle * hollow * 0.707107f);
762 else
763 hollowUs.Add(angle.angle * hollow);
764 }
765 }
766 }
767
768 int index = 0;
769 int numAngles = angles.angles.Count;
770
771 for (int i = 0; i < numAngles; i++)
772 {
773 angle = angles.angles[i];
774 newVert.X = angle.X * xScale;
775 newVert.Y = angle.Y * yScale;
776 newVert.Z = z;
777 this.coords.Add(newVert);
778 if (this.calcVertexNormals)
779 {
780 this.outerCoordIndices.Add(this.coords.Count - 1);
781
782 if (sides < 5)
783 {
784 this.vertexNormals.Add(angles.normals[i]);
785 float u = angle.angle;
786 this.us.Add(u);
787 }
788 else
789 {
790 this.vertexNormals.Add(new Coord(angle.X, angle.Y, 0.0f));
791 this.us.Add(angle.angle);
792 }
793 }
794
795 if (hasHollow)
796 {
797 if (hollowSides == sides)
798 {
799 newVert.X *= hollow;
800 newVert.Y *= hollow;
801 newVert.Z = z;
802 hollowCoords.Add(newVert);
803 if (this.calcVertexNormals)
804 {
805 if (sides < 5)
806 {
807 hollowNormals.Add(angles.normals[i].Invert());
808 }
809
810 else
811 hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f));
812
813 hollowUs.Add(angle.angle * hollow);
814 }
815 }
816 }
817 else if (!simpleFace && createFaces && angle.angle > 0.0001f)
818 {
819 Face newFace = new Face();
820 newFace.v1 = 0;
821 newFace.v2 = index;
822 newFace.v3 = index + 1;
823
824 this.faces.Add(newFace);
825 }
826 index += 1;
827 }
828
829 if (hasHollow)
830 {
831 hollowCoords.Reverse();
832 if (this.calcVertexNormals)
833 {
834 hollowNormals.Reverse();
835 hollowUs.Reverse();
836 }
837
838 if (createFaces)
839 {
840 int numTotalVerts = this.numOuterVerts + this.numHollowVerts;
841
842 if (this.numOuterVerts == this.numHollowVerts)
843 {
844 Face newFace = new Face();
845
846 for (int coordIndex = 0; coordIndex < this.numOuterVerts - 1; coordIndex++)
847 {
848 newFace.v1 = coordIndex;
849 newFace.v2 = coordIndex + 1;
850 newFace.v3 = numTotalVerts - coordIndex - 1;
851 this.faces.Add(newFace);
852
853 newFace.v1 = coordIndex + 1;
854 newFace.v2 = numTotalVerts - coordIndex - 2;
855 newFace.v3 = numTotalVerts - coordIndex - 1;
856 this.faces.Add(newFace);
857 }
858 }
859 else
860 {
861 if (this.numOuterVerts < this.numHollowVerts)
862 {
863 Face newFace = new Face();
864 int j = 0; // j is the index for outer vertices
865 int maxJ = this.numOuterVerts - 1;
866 for (int i = 0; i < this.numHollowVerts; i++) // i is the index for inner vertices
867 {
868 if (j < maxJ)
869 if (angles.angles[j + 1].angle - hollowAngles.angles[i].angle < hollowAngles.angles[i].angle - angles.angles[j].angle + 0.000001f)
870 {
871 newFace.v1 = numTotalVerts - i - 1;
872 newFace.v2 = j;
873 newFace.v3 = j + 1;
874
875 this.faces.Add(newFace);
876 j += 1;
877 }
878
879 newFace.v1 = j;
880 newFace.v2 = numTotalVerts - i - 2;
881 newFace.v3 = numTotalVerts - i - 1;
882
883 this.faces.Add(newFace);
884 }
885 }
886 else // numHollowVerts < numOuterVerts
887 {
888 Face newFace = new Face();
889 int j = 0; // j is the index for inner vertices
890 int maxJ = this.numHollowVerts - 1;
891 for (int i = 0; i < this.numOuterVerts; i++)
892 {
893 if (j < maxJ)
894 if (hollowAngles.angles[j + 1].angle - angles.angles[i].angle < angles.angles[i].angle - hollowAngles.angles[j].angle + 0.000001f)
895 {
896 newFace.v1 = i;
897 newFace.v2 = numTotalVerts - j - 2;
898 newFace.v3 = numTotalVerts - j - 1;
899
900 this.faces.Add(newFace);
901 j += 1;
902 }
903
904 newFace.v1 = numTotalVerts - j - 1;
905 newFace.v2 = i;
906 newFace.v3 = i + 1;
907
908 this.faces.Add(newFace);
909 }
910 }
911 }
912 }
913
914 if (calcVertexNormals)
915 {
916 foreach (Coord hc in hollowCoords)
917 {
918 this.coords.Add(hc);
919 hollowCoordIndices.Add(this.coords.Count - 1);
920 }
921 }
922 else
923 this.coords.AddRange(hollowCoords);
924
925 if (this.calcVertexNormals)
926 {
927 this.vertexNormals.AddRange(hollowNormals);
928 this.us.AddRange(hollowUs);
929
930 }
931 }
932
933 if (simpleFace && createFaces)
934 {
935 if (sides == 3)
936 this.faces.Add(new Face(0, 1, 2));
937 else if (sides == 4)
938 {
939 this.faces.Add(new Face(0, 1, 2));
940 this.faces.Add(new Face(0, 2, 3));
941 }
942 }
943
944 if (calcVertexNormals && hasProfileCut)
945 {
946 int lastOuterVertIndex = this.numOuterVerts - 1;
947
948 if (hasHollow)
949 {
950 this.cut1CoordIndices.Add(0);
951 this.cut1CoordIndices.Add(this.coords.Count - 1);
952
953 this.cut2CoordIndices.Add(lastOuterVertIndex + 1);
954 this.cut2CoordIndices.Add(lastOuterVertIndex);
955
956 this.cutNormal1.X = this.coords[0].Y - this.coords[this.coords.Count - 1].Y;
957 this.cutNormal1.Y = -(this.coords[0].X - this.coords[this.coords.Count - 1].X);
958
959 this.cutNormal2.X = this.coords[lastOuterVertIndex + 1].Y - this.coords[lastOuterVertIndex].Y;
960 this.cutNormal2.Y = -(this.coords[lastOuterVertIndex + 1].X - this.coords[lastOuterVertIndex].X);
961 }
962
963 else
964 {
965 this.cut1CoordIndices.Add(0);
966 this.cut1CoordIndices.Add(1);
967
968 this.cut2CoordIndices.Add(lastOuterVertIndex);
969 this.cut2CoordIndices.Add(0);
970
971 this.cutNormal1.X = this.vertexNormals[1].Y;
972 this.cutNormal1.Y = -this.vertexNormals[1].X;
973
974 this.cutNormal2.X = -this.vertexNormals[this.vertexNormals.Count - 2].Y;
975 this.cutNormal2.Y = this.vertexNormals[this.vertexNormals.Count - 2].X;
976
977 }
978 this.cutNormal1.Normalize();
979 this.cutNormal2.Normalize();
980 }
981
982 this.MakeFaceUVs();
983
984 hollowCoords = null;
985 hollowNormals = null;
986 hollowUs = null;
987
988 if (calcVertexNormals)
989 { // calculate prim face numbers
990
991 // face number order is top, outer, hollow, bottom, start cut, end cut
992 // I know it's ugly but so is the whole concept of prim face numbers
993
994 int faceNum = 1; // start with outer faces
995 this.outerFaceNumber = faceNum;
996
997 int startVert = hasProfileCut && !hasHollow ? 1 : 0;
998 if (startVert > 0)
999 this.faceNumbers.Add(-1);
1000 for (int i = 0; i < this.numOuterVerts - 1; i++)
1001 this.faceNumbers.Add(sides < 5 && i <= sides ? faceNum++ : faceNum);
1002
1003 this.faceNumbers.Add(hasProfileCut ? -1 : faceNum++);
1004
1005 if (sides > 4 && (hasHollow || hasProfileCut))
1006 faceNum++;
1007
1008 if (sides < 5 && (hasHollow || hasProfileCut) && this.numOuterVerts < sides)
1009 faceNum++;
1010
1011 if (hasHollow)
1012 {
1013 for (int i = 0; i < this.numHollowVerts; i++)
1014 this.faceNumbers.Add(faceNum);
1015
1016 this.hollowFaceNumber = faceNum++;
1017 }
1018
1019 this.bottomFaceNumber = faceNum++;
1020
1021 if (hasHollow && hasProfileCut)
1022 this.faceNumbers.Add(faceNum++);
1023
1024 for (int i = 0; i < this.faceNumbers.Count; i++)
1025 if (this.faceNumbers[i] == -1)
1026 this.faceNumbers[i] = faceNum++;
1027
1028 this.numPrimFaces = faceNum;
1029 }
1030
1031 }
1032
1033 public void MakeFaceUVs()
1034 {
1035 this.faceUVs = new List<UVCoord>();
1036 foreach (Coord c in this.coords)
1037 this.faceUVs.Add(new UVCoord(1.0f - (0.5f + c.X), 1.0f - (0.5f - c.Y)));
1038 }
1039
1040 public Profile Copy()
1041 {
1042 return this.Copy(true);
1043 }
1044
1045 public Profile Copy(bool needFaces)
1046 {
1047 Profile copy = new Profile();
1048
1049 copy.coords.AddRange(this.coords);
1050 copy.faceUVs.AddRange(this.faceUVs);
1051
1052 if (needFaces)
1053 copy.faces.AddRange(this.faces);
1054 if ((copy.calcVertexNormals = this.calcVertexNormals) == true)
1055 {
1056 copy.vertexNormals.AddRange(this.vertexNormals);
1057 copy.faceNormal = this.faceNormal;
1058 copy.cutNormal1 = this.cutNormal1;
1059 copy.cutNormal2 = this.cutNormal2;
1060 copy.us.AddRange(this.us);
1061 copy.faceNumbers.AddRange(this.faceNumbers);
1062
1063 copy.cut1CoordIndices = new List<int>(this.cut1CoordIndices);
1064 copy.cut2CoordIndices = new List<int>(this.cut2CoordIndices);
1065 copy.hollowCoordIndices = new List<int>(this.hollowCoordIndices);
1066 copy.outerCoordIndices = new List<int>(this.outerCoordIndices);
1067 }
1068 copy.numOuterVerts = this.numOuterVerts;
1069 copy.numHollowVerts = this.numHollowVerts;
1070
1071 return copy;
1072 }
1073
1074 public void AddPos(Coord v)
1075 {
1076 this.AddPos(v.X, v.Y, v.Z);
1077 }
1078
1079 public void AddPos(float x, float y, float z)
1080 {
1081 int i;
1082 int numVerts = this.coords.Count;
1083 Coord vert;
1084
1085 for (i = 0; i < numVerts; i++)
1086 {
1087 vert = this.coords[i];
1088 vert.X += x;
1089 vert.Y += y;
1090 vert.Z += z;
1091 this.coords[i] = vert;
1092 }
1093 }
1094
1095 public void AddRot(Quat q)
1096 {
1097 int i;
1098 int numVerts = this.coords.Count;
1099
1100 for (i = 0; i < numVerts; i++)
1101 this.coords[i] *= q;
1102
1103 if (this.calcVertexNormals)
1104 {
1105 int numNormals = this.vertexNormals.Count;
1106 for (i = 0; i < numNormals; i++)
1107 this.vertexNormals[i] *= q;
1108
1109 this.faceNormal *= q;
1110 this.cutNormal1 *= q;
1111 this.cutNormal2 *= q;
1112
1113 }
1114 }
1115
1116 public void Scale(float x, float y)
1117 {
1118 int i;
1119 int numVerts = this.coords.Count;
1120 Coord vert;
1121
1122 for (i = 0; i < numVerts; i++)
1123 {
1124 vert = this.coords[i];
1125 vert.X *= x;
1126 vert.Y *= y;
1127 this.coords[i] = vert;
1128 }
1129 }
1130
1131 /// <summary>
1132 /// Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex normals of radial vertices
1133 /// </summary>
1134 public void FlipNormals()
1135 {
1136 int i;
1137 int numFaces = this.faces.Count;
1138 Face tmpFace;
1139 int tmp;
1140
1141 for (i = 0; i < numFaces; i++)
1142 {
1143 tmpFace = this.faces[i];
1144 tmp = tmpFace.v3;
1145 tmpFace.v3 = tmpFace.v1;
1146 tmpFace.v1 = tmp;
1147 this.faces[i] = tmpFace;
1148 }
1149
1150 if (this.calcVertexNormals)
1151 {
1152 int normalCount = this.vertexNormals.Count;
1153 if (normalCount > 0)
1154 {
1155 Coord n = this.vertexNormals[normalCount - 1];
1156 n.Z = -n.Z;
1157 this.vertexNormals[normalCount - 1] = n;
1158 }
1159 }
1160
1161 this.faceNormal.X = -this.faceNormal.X;
1162 this.faceNormal.Y = -this.faceNormal.Y;
1163 this.faceNormal.Z = -this.faceNormal.Z;
1164
1165 int numfaceUVs = this.faceUVs.Count;
1166 for (i = 0; i < numfaceUVs; i++)
1167 {
1168 UVCoord uv = this.faceUVs[i];
1169 uv.V = 1.0f - uv.V;
1170 this.faceUVs[i] = uv;
1171 }
1172 }
1173
1174 public void AddValue2FaceVertexIndices(int num)
1175 {
1176 int numFaces = this.faces.Count;
1177 Face tmpFace;
1178 for (int i = 0; i < numFaces; i++)
1179 {
1180 tmpFace = this.faces[i];
1181 tmpFace.v1 += num;
1182 tmpFace.v2 += num;
1183 tmpFace.v3 += num;
1184
1185 this.faces[i] = tmpFace;
1186 }
1187 }
1188
1189 public void AddValue2FaceNormalIndices(int num)
1190 {
1191 if (this.calcVertexNormals)
1192 {
1193 int numFaces = this.faces.Count;
1194 Face tmpFace;
1195 for (int i = 0; i < numFaces; i++)
1196 {
1197 tmpFace = this.faces[i];
1198 tmpFace.n1 += num;
1199 tmpFace.n2 += num;
1200 tmpFace.n3 += num;
1201
1202 this.faces[i] = tmpFace;
1203 }
1204 }
1205 }
1206
1207 public void DumpRaw(String path, String name, String title)
1208 {
1209 if (path == null)
1210 return;
1211 String fileName = name + "_" + title + ".raw";
1212 String completePath = System.IO.Path.Combine(path, fileName);
1213 StreamWriter sw = new StreamWriter(completePath);
1214
1215 for (int i = 0; i < this.faces.Count; i++)
1216 {
1217 string s = this.coords[this.faces[i].v1].ToString();
1218 s += " " + this.coords[this.faces[i].v2].ToString();
1219 s += " " + this.coords[this.faces[i].v3].ToString();
1220
1221 sw.WriteLine(s);
1222 }
1223
1224 sw.Close();
1225 }
1226 }
1227
1228 public struct PathNode
1229 {
1230 public Coord position;
1231 public Quat rotation;
1232 public float xScale;
1233 public float yScale;
1234 public float percentOfPath;
1235 }
1236
1237 public enum PathType { Linear = 0, Circular = 1, Flexible = 2 }
1238
1239 public class Path
1240 {
1241 public List<PathNode> pathNodes = new List<PathNode>();
1242
1243 public float twistBegin = 0.0f;
1244 public float twistEnd = 0.0f;
1245 public float topShearX = 0.0f;
1246 public float topShearY = 0.0f;
1247 public float pathCutBegin = 0.0f;
1248 public float pathCutEnd = 1.0f;
1249 public float dimpleBegin = 0.0f;
1250 public float dimpleEnd = 1.0f;
1251 public float skew = 0.0f;
1252 public float holeSizeX = 1.0f; // called pathScaleX in pbs
1253 public float holeSizeY = 0.25f;
1254 public float taperX = 0.0f;
1255 public float taperY = 0.0f;
1256 public float radius = 0.0f;
1257 public float revolutions = 1.0f;
1258 public int stepsPerRevolution = 24;
1259
1260 private const float twoPi = 2.0f * (float)Math.PI;
1261
1262 public void Create(PathType pathType, int steps)
1263 {
1264 if (this.taperX > 0.999f)
1265 this.taperX = 0.999f;
1266 if (this.taperX < -0.999f)
1267 this.taperX = -0.999f;
1268 if (this.taperY > 0.999f)
1269 this.taperY = 0.999f;
1270 if (this.taperY < -0.999f)
1271 this.taperY = -0.999f;
1272
1273 if (pathType == PathType.Linear || pathType == PathType.Flexible)
1274 {
1275 int step = 0;
1276
1277 float length = this.pathCutEnd - this.pathCutBegin;
1278 float twistTotal = twistEnd - twistBegin;
1279 float twistTotalAbs = Math.Abs(twistTotal);
1280 if (twistTotalAbs > 0.01f)
1281 steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number
1282
1283 float start = -0.5f;
1284 float stepSize = length / (float)steps;
1285 float percentOfPathMultiplier = stepSize * 0.999999f;
1286 float xOffset = this.topShearX * this.pathCutBegin;
1287 float yOffset = this.topShearY * this.pathCutBegin;
1288 float zOffset = start;
1289 float xOffsetStepIncrement = this.topShearX * length / steps;
1290 float yOffsetStepIncrement = this.topShearY * length / steps;
1291
1292 float percentOfPath = this.pathCutBegin;
1293 zOffset += percentOfPath;
1294
1295 // sanity checks
1296
1297 bool done = false;
1298
1299 while (!done)
1300 {
1301 PathNode newNode = new PathNode();
1302
1303 newNode.xScale = 1.0f;
1304 if (this.taperX == 0.0f)
1305 newNode.xScale = 1.0f;
1306 else if (this.taperX > 0.0f)
1307 newNode.xScale = 1.0f - percentOfPath * this.taperX;
1308 else newNode.xScale = 1.0f + (1.0f - percentOfPath) * this.taperX;
1309
1310 newNode.yScale = 1.0f;
1311 if (this.taperY == 0.0f)
1312 newNode.yScale = 1.0f;
1313 else if (this.taperY > 0.0f)
1314 newNode.yScale = 1.0f - percentOfPath * this.taperY;
1315 else newNode.yScale = 1.0f + (1.0f - percentOfPath) * this.taperY;
1316
1317 float twist = twistBegin + twistTotal * percentOfPath;
1318
1319 newNode.rotation = new Quat(new Coord(0.0f, 0.0f, 1.0f), twist);
1320 newNode.position = new Coord(xOffset, yOffset, zOffset);
1321 newNode.percentOfPath = percentOfPath;
1322
1323 pathNodes.Add(newNode);
1324
1325 if (step < steps)
1326 {
1327 step += 1;
1328 percentOfPath += percentOfPathMultiplier;
1329 xOffset += xOffsetStepIncrement;
1330 yOffset += yOffsetStepIncrement;
1331 zOffset += stepSize;
1332 if (percentOfPath > this.pathCutEnd)
1333 done = true;
1334 }
1335 else done = true;
1336 }
1337 } // end of linear path code
1338
1339 else // pathType == Circular
1340 {
1341 float twistTotal = twistEnd - twistBegin;
1342
1343 // if the profile has a lot of twist, add more layers otherwise the layers may overlap
1344 // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't
1345 // accurately match the viewer
1346 float twistTotalAbs = Math.Abs(twistTotal);
1347 if (twistTotalAbs > 0.01f)
1348 {
1349 if (twistTotalAbs > Math.PI * 1.5f)
1350 steps *= 2;
1351 if (twistTotalAbs > Math.PI * 3.0f)
1352 steps *= 2;
1353 }
1354
1355 float yPathScale = this.holeSizeY * 0.5f;
1356 float pathLength = this.pathCutEnd - this.pathCutBegin;
1357 float totalSkew = this.skew * 2.0f * pathLength;
1358 float skewStart = this.pathCutBegin * 2.0f * this.skew - this.skew;
1359 float xOffsetTopShearXFactor = this.topShearX * (0.25f + 0.5f * (0.5f - this.holeSizeY));
1360 float yShearCompensation = 1.0f + Math.Abs(this.topShearY) * 0.25f;
1361
1362 // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end
1363 // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used
1364 // to calculate the sine for generating the path radius appears to approximate it's effects there
1365 // too, but there are some subtle differences in the radius which are noticeable as the prim size
1366 // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on
1367 // the meshes generated with this technique appear nearly identical in shape to the same prims when
1368 // displayed by the viewer.
1369
1370 float startAngle = (twoPi * this.pathCutBegin * this.revolutions) - this.topShearY * 0.9f;
1371 float endAngle = (twoPi * this.pathCutEnd * this.revolutions) - this.topShearY * 0.9f;
1372 float stepSize = twoPi / this.stepsPerRevolution;
1373
1374 int step = (int)(startAngle / stepSize);
1375 float angle = startAngle;
1376
1377 bool done = false;
1378 while (!done) // loop through the length of the path and add the layers
1379 {
1380 PathNode newNode = new PathNode();
1381
1382 float xProfileScale = (1.0f - Math.Abs(this.skew)) * this.holeSizeX;
1383 float yProfileScale = this.holeSizeY;
1384
1385 float percentOfPath = angle / (twoPi * this.revolutions);
1386 float percentOfAngles = (angle - startAngle) / (endAngle - startAngle);
1387
1388 if (this.taperX > 0.01f)
1389 xProfileScale *= 1.0f - percentOfPath * this.taperX;
1390 else if (this.taperX < -0.01f)
1391 xProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperX;
1392
1393 if (this.taperY > 0.01f)
1394 yProfileScale *= 1.0f - percentOfPath * this.taperY;
1395 else if (this.taperY < -0.01f)
1396 yProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperY;
1397
1398 newNode.xScale = xProfileScale;
1399 newNode.yScale = yProfileScale;
1400
1401 float radiusScale = 1.0f;
1402 if (this.radius > 0.001f)
1403 radiusScale = 1.0f - this.radius * percentOfPath;
1404 else if (this.radius < 0.001f)
1405 radiusScale = 1.0f + this.radius * (1.0f - percentOfPath);
1406
1407 float twist = twistBegin + twistTotal * percentOfPath;
1408
1409 float xOffset = 0.5f * (skewStart + totalSkew * percentOfAngles);
1410 xOffset += (float)Math.Sin(angle) * xOffsetTopShearXFactor;
1411
1412 float yOffset = yShearCompensation * (float)Math.Cos(angle) * (0.5f - yPathScale) * radiusScale;
1413
1414 float zOffset = (float)Math.Sin(angle + this.topShearY) * (0.5f - yPathScale) * radiusScale;
1415
1416 newNode.position = new Coord(xOffset, yOffset, zOffset);
1417
1418 // now orient the rotation of the profile layer relative to it's position on the path
1419 // adding taperY to the angle used to generate the quat appears to approximate the viewer
1420
1421 newNode.rotation = new Quat(new Coord(1.0f, 0.0f, 0.0f), angle + this.topShearY);
1422
1423 // next apply twist rotation to the profile layer
1424 if (twistTotal != 0.0f || twistBegin != 0.0f)
1425 newNode.rotation *= new Quat(new Coord(0.0f, 0.0f, 1.0f), twist);
1426
1427 newNode.percentOfPath = percentOfPath;
1428
1429 pathNodes.Add(newNode);
1430
1431 // calculate terms for next iteration
1432 // calculate the angle for the next iteration of the loop
1433
1434 if (angle >= endAngle - 0.01)
1435 done = true;
1436 else
1437 {
1438 step += 1;
1439 angle = stepSize * step;
1440 if (angle > endAngle)
1441 angle = endAngle;
1442 }
1443 }
1444 }
1445 }
1446 }
1447
1448 public class PrimMesh
1449 {
1450 public string errorMessage = "";
1451 private const float twoPi = 2.0f * (float)Math.PI;
1452
1453 public List<Coord> coords;
1454 public List<Coord> normals;
1455 public List<Face> faces;
1456
1457 public List<ViewerFace> viewerFaces;
1458
1459 private int sides = 4;
1460 private int hollowSides = 4;
1461 private float profileStart = 0.0f;
1462 private float profileEnd = 1.0f;
1463 private float hollow = 0.0f;
1464 public int twistBegin = 0;
1465 public int twistEnd = 0;
1466 public float topShearX = 0.0f;
1467 public float topShearY = 0.0f;
1468 public float pathCutBegin = 0.0f;
1469 public float pathCutEnd = 1.0f;
1470 public float dimpleBegin = 0.0f;
1471 public float dimpleEnd = 1.0f;
1472 public float skew = 0.0f;
1473 public float holeSizeX = 1.0f; // called pathScaleX in pbs
1474 public float holeSizeY = 0.25f;
1475 public float taperX = 0.0f;
1476 public float taperY = 0.0f;
1477 public float radius = 0.0f;
1478 public float revolutions = 1.0f;
1479 public int stepsPerRevolution = 24;
1480
1481 private int profileOuterFaceNumber = -1;
1482 private int profileHollowFaceNumber = -1;
1483
1484 private bool hasProfileCut = false;
1485 private bool hasHollow = false;
1486 public bool calcVertexNormals = false;
1487 private bool normalsProcessed = false;
1488 public bool viewerMode = false;
1489 public bool sphereMode = false;
1490
1491 public int numPrimFaces = 0;
1492
1493 /// <summary>
1494 /// Human readable string representation of the parameters used to create a mesh.
1495 /// </summary>
1496 /// <returns></returns>
1497 public string ParamsToDisplayString()
1498 {
1499 string s = "";
1500 s += "sides..................: " + this.sides.ToString();
1501 s += "\nhollowSides..........: " + this.hollowSides.ToString();
1502 s += "\nprofileStart.........: " + this.profileStart.ToString();
1503 s += "\nprofileEnd...........: " + this.profileEnd.ToString();
1504 s += "\nhollow...............: " + this.hollow.ToString();
1505 s += "\ntwistBegin...........: " + this.twistBegin.ToString();
1506 s += "\ntwistEnd.............: " + this.twistEnd.ToString();
1507 s += "\ntopShearX............: " + this.topShearX.ToString();
1508 s += "\ntopShearY............: " + this.topShearY.ToString();
1509 s += "\npathCutBegin.........: " + this.pathCutBegin.ToString();
1510 s += "\npathCutEnd...........: " + this.pathCutEnd.ToString();
1511 s += "\ndimpleBegin..........: " + this.dimpleBegin.ToString();
1512 s += "\ndimpleEnd............: " + this.dimpleEnd.ToString();
1513 s += "\nskew.................: " + this.skew.ToString();
1514 s += "\nholeSizeX............: " + this.holeSizeX.ToString();
1515 s += "\nholeSizeY............: " + this.holeSizeY.ToString();
1516 s += "\ntaperX...............: " + this.taperX.ToString();
1517 s += "\ntaperY...............: " + this.taperY.ToString();
1518 s += "\nradius...............: " + this.radius.ToString();
1519 s += "\nrevolutions..........: " + this.revolutions.ToString();
1520 s += "\nstepsPerRevolution...: " + this.stepsPerRevolution.ToString();
1521 s += "\nsphereMode...........: " + this.sphereMode.ToString();
1522 s += "\nhasProfileCut........: " + this.hasProfileCut.ToString();
1523 s += "\nhasHollow............: " + this.hasHollow.ToString();
1524 s += "\nviewerMode...........: " + this.viewerMode.ToString();
1525
1526 return s;
1527 }
1528
1529 public int ProfileOuterFaceNumber
1530 {
1531 get { return profileOuterFaceNumber; }
1532 }
1533
1534 public int ProfileHollowFaceNumber
1535 {
1536 get { return profileHollowFaceNumber; }
1537 }
1538
1539 public bool HasProfileCut
1540 {
1541 get { return hasProfileCut; }
1542 }
1543
1544 public bool HasHollow
1545 {
1546 get { return hasHollow; }
1547 }
1548
1549
1550 /// <summary>
1551 /// Constructs a PrimMesh object and creates the profile for extrusion.
1552 /// </summary>
1553 /// <param name="sides"></param>
1554 /// <param name="profileStart"></param>
1555 /// <param name="profileEnd"></param>
1556 /// <param name="hollow"></param>
1557 /// <param name="hollowSides"></param>
1558 public PrimMesh(int sides, float profileStart, float profileEnd, float hollow, int hollowSides)
1559 {
1560 this.coords = new List<Coord>();
1561 this.faces = new List<Face>();
1562
1563 this.sides = sides;
1564 this.profileStart = profileStart;
1565 this.profileEnd = profileEnd;
1566 this.hollow = hollow;
1567 this.hollowSides = hollowSides;
1568
1569 if (sides < 3)
1570 this.sides = 3;
1571 if (hollowSides < 3)
1572 this.hollowSides = 3;
1573 if (profileStart < 0.0f)
1574 this.profileStart = 0.0f;
1575 if (profileEnd > 1.0f)
1576 this.profileEnd = 1.0f;
1577 if (profileEnd < 0.02f)
1578 this.profileEnd = 0.02f;
1579 if (profileStart >= profileEnd)
1580 this.profileStart = profileEnd - 0.02f;
1581 if (hollow > 0.99f)
1582 this.hollow = 0.99f;
1583 if (hollow < 0.0f)
1584 this.hollow = 0.0f;
1585 }
1586
1587 /// <summary>
1588 /// Extrudes a profile along a path.
1589 /// </summary>
1590 public void Extrude(PathType pathType)
1591 {
1592 bool needEndFaces = false;
1593
1594 this.coords = new List<Coord>();
1595 this.faces = new List<Face>();
1596
1597 if (this.viewerMode)
1598 {
1599 this.viewerFaces = new List<ViewerFace>();
1600 this.calcVertexNormals = true;
1601 }
1602
1603 if (this.calcVertexNormals)
1604 this.normals = new List<Coord>();
1605
1606 int steps = 1;
1607
1608 float length = this.pathCutEnd - this.pathCutBegin;
1609 normalsProcessed = false;
1610
1611 if (this.viewerMode && this.sides == 3)
1612 {
1613 // prisms don't taper well so add some vertical resolution
1614 // other prims may benefit from this but just do prisms for now
1615 if (Math.Abs(this.taperX) > 0.01 || Math.Abs(this.taperY) > 0.01)
1616 steps = (int)(steps * 4.5 * length);
1617 }
1618
1619 if (this.sphereMode)
1620 this.hasProfileCut = this.profileEnd - this.profileStart < 0.4999f;
1621 else
1622 this.hasProfileCut = this.profileEnd - this.profileStart < 0.9999f;
1623 this.hasHollow = (this.hollow > 0.001f);
1624
1625 float twistBegin = this.twistBegin / 360.0f * twoPi;
1626 float twistEnd = this.twistEnd / 360.0f * twoPi;
1627 float twistTotal = twistEnd - twistBegin;
1628 float twistTotalAbs = Math.Abs(twistTotal);
1629 if (twistTotalAbs > 0.01f)
1630 steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number
1631
1632 float hollow = this.hollow;
1633
1634 if (pathType == PathType.Circular)
1635 {
1636 needEndFaces = false;
1637 if (this.pathCutBegin != 0.0f || this.pathCutEnd != 1.0f)
1638 needEndFaces = true;
1639 else if (this.taperX != 0.0f || this.taperY != 0.0f)
1640 needEndFaces = true;
1641 else if (this.skew != 0.0f)
1642 needEndFaces = true;
1643 else if (twistTotal != 0.0f)
1644 needEndFaces = true;
1645 else if (this.radius != 0.0f)
1646 needEndFaces = true;
1647 }
1648 else needEndFaces = true;
1649
1650 // sanity checks
1651 float initialProfileRot = 0.0f;
1652 if (pathType == PathType.Circular)
1653 {
1654 if (this.sides == 3)
1655 {
1656 initialProfileRot = (float)Math.PI;
1657 if (this.hollowSides == 4)
1658 {
1659 if (hollow > 0.7f)
1660 hollow = 0.7f;
1661 hollow *= 0.707f;
1662 }
1663 else hollow *= 0.5f;
1664 }
1665 else if (this.sides == 4)
1666 {
1667 initialProfileRot = 0.25f * (float)Math.PI;
1668 if (this.hollowSides != 4)
1669 hollow *= 0.707f;
1670 }
1671 else if (this.sides > 4)
1672 {
1673 initialProfileRot = (float)Math.PI;
1674 if (this.hollowSides == 4)
1675 {
1676 if (hollow > 0.7f)
1677 hollow = 0.7f;
1678 hollow /= 0.7f;
1679 }
1680 }
1681 }
1682 else
1683 {
1684 if (this.sides == 3)
1685 {
1686 if (this.hollowSides == 4)
1687 {
1688 if (hollow > 0.7f)
1689 hollow = 0.7f;
1690 hollow *= 0.707f;
1691 }
1692 else hollow *= 0.5f;
1693 }
1694 else if (this.sides == 4)
1695 {
1696 initialProfileRot = 1.25f * (float)Math.PI;
1697 if (this.hollowSides != 4)
1698 hollow *= 0.707f;
1699 }
1700 else if (this.sides == 24 && this.hollowSides == 4)
1701 hollow *= 1.414f;
1702 }
1703
1704 Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, true, calcVertexNormals);
1705 this.errorMessage = profile.errorMessage;
1706
1707 this.numPrimFaces = profile.numPrimFaces;
1708
1709 int cut1FaceNumber = profile.bottomFaceNumber + 1;
1710 int cut2FaceNumber = cut1FaceNumber + 1;
1711 if (!needEndFaces)
1712 {
1713 cut1FaceNumber -= 2;
1714 cut2FaceNumber -= 2;
1715 }
1716
1717 profileOuterFaceNumber = profile.outerFaceNumber;
1718 if (!needEndFaces)
1719 profileOuterFaceNumber--;
1720
1721 if (hasHollow)
1722 {
1723 profileHollowFaceNumber = profile.hollowFaceNumber;
1724 if (!needEndFaces)
1725 profileHollowFaceNumber--;
1726 }
1727
1728 int cut1Vert = -1;
1729 int cut2Vert = -1;
1730 if (hasProfileCut)
1731 {
1732 cut1Vert = hasHollow ? profile.coords.Count - 1 : 0;
1733 cut2Vert = hasHollow ? profile.numOuterVerts - 1 : profile.numOuterVerts;
1734 }
1735
1736 if (initialProfileRot != 0.0f)
1737 {
1738 profile.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), initialProfileRot));
1739 if (viewerMode)
1740 profile.MakeFaceUVs();
1741 }
1742
1743 Coord lastCutNormal1 = new Coord();
1744 Coord lastCutNormal2 = new Coord();
1745 float thisV = 0.0f;
1746 float lastV = 0.0f;
1747
1748 Path path = new Path();
1749 path.twistBegin = twistBegin;
1750 path.twistEnd = twistEnd;
1751 path.topShearX = topShearX;
1752 path.topShearY = topShearY;
1753 path.pathCutBegin = pathCutBegin;
1754 path.pathCutEnd = pathCutEnd;
1755 path.dimpleBegin = dimpleBegin;
1756 path.dimpleEnd = dimpleEnd;
1757 path.skew = skew;
1758 path.holeSizeX = holeSizeX;
1759 path.holeSizeY = holeSizeY;
1760 path.taperX = taperX;
1761 path.taperY = taperY;
1762 path.radius = radius;
1763 path.revolutions = revolutions;
1764 path.stepsPerRevolution = stepsPerRevolution;
1765
1766 path.Create(pathType, steps);
1767
1768 for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++)
1769 {
1770 PathNode node = path.pathNodes[nodeIndex];
1771 Profile newLayer = profile.Copy();
1772 newLayer.Scale(node.xScale, node.yScale);
1773
1774 newLayer.AddRot(node.rotation);
1775 newLayer.AddPos(node.position);
1776
1777 if (needEndFaces && nodeIndex == 0)
1778 {
1779 newLayer.FlipNormals();
1780
1781 // add the bottom faces to the viewerFaces list
1782 if (this.viewerMode)
1783 {
1784 Coord faceNormal = newLayer.faceNormal;
1785 ViewerFace newViewerFace = new ViewerFace(profile.bottomFaceNumber);
1786 int numFaces = newLayer.faces.Count;
1787 List<Face> faces = newLayer.faces;
1788
1789 for (int i = 0; i < numFaces; i++)
1790 {
1791 Face face = faces[i];
1792 newViewerFace.v1 = newLayer.coords[face.v1];
1793 newViewerFace.v2 = newLayer.coords[face.v2];
1794 newViewerFace.v3 = newLayer.coords[face.v3];
1795
1796 newViewerFace.coordIndex1 = face.v1;
1797 newViewerFace.coordIndex2 = face.v2;
1798 newViewerFace.coordIndex3 = face.v3;
1799
1800 newViewerFace.n1 = faceNormal;
1801 newViewerFace.n2 = faceNormal;
1802 newViewerFace.n3 = faceNormal;
1803
1804 newViewerFace.uv1 = newLayer.faceUVs[face.v1];
1805 newViewerFace.uv2 = newLayer.faceUVs[face.v2];
1806 newViewerFace.uv3 = newLayer.faceUVs[face.v3];
1807
1808 if (pathType == PathType.Linear)
1809 {
1810 newViewerFace.uv1.Flip();
1811 newViewerFace.uv2.Flip();
1812 newViewerFace.uv3.Flip();
1813 }
1814
1815 this.viewerFaces.Add(newViewerFace);
1816 }
1817 }
1818 } // if (nodeIndex == 0)
1819
1820 // append this layer
1821
1822 int coordsLen = this.coords.Count;
1823 newLayer.AddValue2FaceVertexIndices(coordsLen);
1824
1825 this.coords.AddRange(newLayer.coords);
1826
1827 if (this.calcVertexNormals)
1828 {
1829 newLayer.AddValue2FaceNormalIndices(this.normals.Count);
1830 this.normals.AddRange(newLayer.vertexNormals);
1831 }
1832
1833 if (node.percentOfPath < this.pathCutBegin + 0.01f || node.percentOfPath > this.pathCutEnd - 0.01f)
1834 this.faces.AddRange(newLayer.faces);
1835
1836 // fill faces between layers
1837
1838 int numVerts = newLayer.coords.Count;
1839 Face newFace1 = new Face();
1840 Face newFace2 = new Face();
1841
1842 thisV = 1.0f - node.percentOfPath;
1843
1844 if (nodeIndex > 0)
1845 {
1846 int startVert = coordsLen + 1;
1847 int endVert = this.coords.Count;
1848
1849 if (sides < 5 || this.hasProfileCut || this.hasHollow)
1850 startVert--;
1851
1852 for (int i = startVert; i < endVert; i++)
1853 {
1854 int iNext = i + 1;
1855 if (i == endVert - 1)
1856 iNext = startVert;
1857
1858 int whichVert = i - startVert;
1859
1860 newFace1.v1 = i;
1861 newFace1.v2 = i - numVerts;
1862 newFace1.v3 = iNext;
1863
1864 newFace1.n1 = newFace1.v1;
1865 newFace1.n2 = newFace1.v2;
1866 newFace1.n3 = newFace1.v3;
1867 this.faces.Add(newFace1);
1868
1869 newFace2.v1 = iNext;
1870 newFace2.v2 = i - numVerts;
1871 newFace2.v3 = iNext - numVerts;
1872
1873 newFace2.n1 = newFace2.v1;
1874 newFace2.n2 = newFace2.v2;
1875 newFace2.n3 = newFace2.v3;
1876 this.faces.Add(newFace2);
1877
1878 if (this.viewerMode)
1879 {
1880 // add the side faces to the list of viewerFaces here
1881
1882 int primFaceNum = profile.faceNumbers[whichVert];
1883 if (!needEndFaces)
1884 primFaceNum -= 1;
1885
1886 ViewerFace newViewerFace1 = new ViewerFace(primFaceNum);
1887 ViewerFace newViewerFace2 = new ViewerFace(primFaceNum);
1888
1889 int uIndex = whichVert;
1890 if (!hasHollow && sides > 4 && uIndex < newLayer.us.Count - 1)
1891 {
1892 uIndex++;
1893 }
1894
1895 float u1 = newLayer.us[uIndex];
1896 float u2 = 1.0f;
1897 if (uIndex < (int)newLayer.us.Count - 1)
1898 u2 = newLayer.us[uIndex + 1];
1899
1900 if (whichVert == cut1Vert || whichVert == cut2Vert)
1901 {
1902 u1 = 0.0f;
1903 u2 = 1.0f;
1904 }
1905 else if (sides < 5)
1906 {
1907 if (whichVert < profile.numOuterVerts)
1908 { // boxes and prisms have one texture face per side of the prim, so the U values have to be scaled
1909 // to reflect the entire texture width
1910 u1 *= sides;
1911 u2 *= sides;
1912 u2 -= (int)u1;
1913 u1 -= (int)u1;
1914 if (u2 < 0.1f)
1915 u2 = 1.0f;
1916 }
1917 }
1918
1919 if (this.sphereMode)
1920 {
1921 if (whichVert != cut1Vert && whichVert != cut2Vert)
1922 {
1923 u1 = u1 * 2.0f - 1.0f;
1924 u2 = u2 * 2.0f - 1.0f;
1925
1926 if (whichVert >= newLayer.numOuterVerts)
1927 {
1928 u1 -= hollow;
1929 u2 -= hollow;
1930 }
1931
1932 }
1933 }
1934
1935 newViewerFace1.uv1.U = u1;
1936 newViewerFace1.uv2.U = u1;
1937 newViewerFace1.uv3.U = u2;
1938
1939 newViewerFace1.uv1.V = thisV;
1940 newViewerFace1.uv2.V = lastV;
1941 newViewerFace1.uv3.V = thisV;
1942
1943 newViewerFace2.uv1.U = u2;
1944 newViewerFace2.uv2.U = u1;
1945 newViewerFace2.uv3.U = u2;
1946
1947 newViewerFace2.uv1.V = thisV;
1948 newViewerFace2.uv2.V = lastV;
1949 newViewerFace2.uv3.V = lastV;
1950
1951 newViewerFace1.v1 = this.coords[newFace1.v1];
1952 newViewerFace1.v2 = this.coords[newFace1.v2];
1953 newViewerFace1.v3 = this.coords[newFace1.v3];
1954
1955 newViewerFace2.v1 = this.coords[newFace2.v1];
1956 newViewerFace2.v2 = this.coords[newFace2.v2];
1957 newViewerFace2.v3 = this.coords[newFace2.v3];
1958
1959 newViewerFace1.coordIndex1 = newFace1.v1;
1960 newViewerFace1.coordIndex2 = newFace1.v2;
1961 newViewerFace1.coordIndex3 = newFace1.v3;
1962
1963 newViewerFace2.coordIndex1 = newFace2.v1;
1964 newViewerFace2.coordIndex2 = newFace2.v2;
1965 newViewerFace2.coordIndex3 = newFace2.v3;
1966
1967 // profile cut faces
1968 if (whichVert == cut1Vert)
1969 {
1970 newViewerFace1.primFaceNumber = cut1FaceNumber;
1971 newViewerFace2.primFaceNumber = cut1FaceNumber;
1972 newViewerFace1.n1 = newLayer.cutNormal1;
1973 newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal1;
1974
1975 newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal1;
1976 newViewerFace2.n2 = lastCutNormal1;
1977 }
1978 else if (whichVert == cut2Vert)
1979 {
1980 newViewerFace1.primFaceNumber = cut2FaceNumber;
1981 newViewerFace2.primFaceNumber = cut2FaceNumber;
1982 newViewerFace1.n1 = newLayer.cutNormal2;
1983 newViewerFace1.n2 = lastCutNormal2;
1984 newViewerFace1.n3 = lastCutNormal2;
1985
1986 newViewerFace2.n1 = newLayer.cutNormal2;
1987 newViewerFace2.n3 = newLayer.cutNormal2;
1988 newViewerFace2.n2 = lastCutNormal2;
1989 }
1990
1991 else // outer and hollow faces
1992 {
1993 if ((sides < 5 && whichVert < newLayer.numOuterVerts) || (hollowSides < 5 && whichVert >= newLayer.numOuterVerts))
1994 { // looks terrible when path is twisted... need vertex normals here
1995 newViewerFace1.CalcSurfaceNormal();
1996 newViewerFace2.CalcSurfaceNormal();
1997 }
1998 else
1999 {
2000 newViewerFace1.n1 = this.normals[newFace1.n1];
2001 newViewerFace1.n2 = this.normals[newFace1.n2];
2002 newViewerFace1.n3 = this.normals[newFace1.n3];
2003
2004 newViewerFace2.n1 = this.normals[newFace2.n1];
2005 newViewerFace2.n2 = this.normals[newFace2.n2];
2006 newViewerFace2.n3 = this.normals[newFace2.n3];
2007 }
2008 }
2009
2010 this.viewerFaces.Add(newViewerFace1);
2011 this.viewerFaces.Add(newViewerFace2);
2012
2013 }
2014 }
2015 }
2016
2017 lastCutNormal1 = newLayer.cutNormal1;
2018 lastCutNormal2 = newLayer.cutNormal2;
2019 lastV = thisV;
2020
2021 if (needEndFaces && nodeIndex == path.pathNodes.Count - 1 && viewerMode)
2022 {
2023 // add the top faces to the viewerFaces list here
2024 Coord faceNormal = newLayer.faceNormal;
2025 ViewerFace newViewerFace = new ViewerFace(0);
2026 int numFaces = newLayer.faces.Count;
2027 List<Face> faces = newLayer.faces;
2028
2029 for (int i = 0; i < numFaces; i++)
2030 {
2031 Face face = faces[i];
2032 newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen];
2033 newViewerFace.v2 = newLayer.coords[face.v2 - coordsLen];
2034 newViewerFace.v3 = newLayer.coords[face.v3 - coordsLen];
2035
2036 newViewerFace.coordIndex1 = face.v1 - coordsLen;
2037 newViewerFace.coordIndex2 = face.v2 - coordsLen;
2038 newViewerFace.coordIndex3 = face.v3 - coordsLen;
2039
2040 newViewerFace.n1 = faceNormal;
2041 newViewerFace.n2 = faceNormal;
2042 newViewerFace.n3 = faceNormal;
2043
2044 newViewerFace.uv1 = newLayer.faceUVs[face.v1 - coordsLen];
2045 newViewerFace.uv2 = newLayer.faceUVs[face.v2 - coordsLen];
2046 newViewerFace.uv3 = newLayer.faceUVs[face.v3 - coordsLen];
2047
2048 if (pathType == PathType.Linear)
2049 {
2050 newViewerFace.uv1.Flip();
2051 newViewerFace.uv2.Flip();
2052 newViewerFace.uv3.Flip();
2053 }
2054
2055 this.viewerFaces.Add(newViewerFace);
2056 }
2057 }
2058
2059
2060 } // for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++)
2061
2062 }
2063
2064
2065 /// <summary>
2066 /// DEPRICATED - use Extrude(PathType.Linear) instead
2067 /// Extrudes a profile along a straight line path. Used for prim types box, cylinder, and prism.
2068 /// </summary>
2069 ///
2070 public void ExtrudeLinear()
2071 {
2072 this.Extrude(PathType.Linear);
2073 }
2074
2075
2076 /// <summary>
2077 /// DEPRICATED - use Extrude(PathType.Circular) instead
2078 /// Extrude a profile into a circular path prim mesh. Used for prim types torus, tube, and ring.
2079 /// </summary>
2080 ///
2081 public void ExtrudeCircular()
2082 {
2083 this.Extrude(PathType.Circular);
2084 }
2085
2086
2087 private Coord SurfaceNormal(Coord c1, Coord c2, Coord c3)
2088 {
2089 Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z);
2090 Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z);
2091
2092 Coord normal = Coord.Cross(edge1, edge2);
2093
2094 normal.Normalize();
2095
2096 return normal;
2097 }
2098
2099 private Coord SurfaceNormal(Face face)
2100 {
2101 return SurfaceNormal(this.coords[face.v1], this.coords[face.v2], this.coords[face.v3]);
2102 }
2103
2104 /// <summary>
2105 /// Calculate the surface normal for a face in the list of faces
2106 /// </summary>
2107 /// <param name="faceIndex"></param>
2108 /// <returns></returns>
2109 public Coord SurfaceNormal(int faceIndex)
2110 {
2111 int numFaces = this.faces.Count;
2112 if (faceIndex < 0 || faceIndex >= numFaces)
2113 throw new Exception("faceIndex out of range");
2114
2115 return SurfaceNormal(this.faces[faceIndex]);
2116 }
2117
2118 /// <summary>
2119 /// Duplicates a PrimMesh object. All object properties are copied by value, including lists.
2120 /// </summary>
2121 /// <returns></returns>
2122 public PrimMesh Copy()
2123 {
2124 PrimMesh copy = new PrimMesh(this.sides, this.profileStart, this.profileEnd, this.hollow, this.hollowSides);
2125 copy.twistBegin = this.twistBegin;
2126 copy.twistEnd = this.twistEnd;
2127 copy.topShearX = this.topShearX;
2128 copy.topShearY = this.topShearY;
2129 copy.pathCutBegin = this.pathCutBegin;
2130 copy.pathCutEnd = this.pathCutEnd;
2131 copy.dimpleBegin = this.dimpleBegin;
2132 copy.dimpleEnd = this.dimpleEnd;
2133 copy.skew = this.skew;
2134 copy.holeSizeX = this.holeSizeX;
2135 copy.holeSizeY = this.holeSizeY;
2136 copy.taperX = this.taperX;
2137 copy.taperY = this.taperY;
2138 copy.radius = this.radius;
2139 copy.revolutions = this.revolutions;
2140 copy.stepsPerRevolution = this.stepsPerRevolution;
2141 copy.calcVertexNormals = this.calcVertexNormals;
2142 copy.normalsProcessed = this.normalsProcessed;
2143 copy.viewerMode = this.viewerMode;
2144 copy.numPrimFaces = this.numPrimFaces;
2145 copy.errorMessage = this.errorMessage;
2146
2147 copy.coords = new List<Coord>(this.coords);
2148 copy.faces = new List<Face>(this.faces);
2149 copy.viewerFaces = new List<ViewerFace>(this.viewerFaces);
2150 copy.normals = new List<Coord>(this.normals);
2151
2152 return copy;
2153 }
2154
2155 /// <summary>
2156 /// Calculate surface normals for all of the faces in the list of faces in this mesh
2157 /// </summary>
2158 public void CalcNormals()
2159 {
2160 if (normalsProcessed)
2161 return;
2162
2163 normalsProcessed = true;
2164
2165 int numFaces = faces.Count;
2166
2167 if (!this.calcVertexNormals)
2168 this.normals = new List<Coord>();
2169
2170 for (int i = 0; i < numFaces; i++)
2171 {
2172 Face face = faces[i];
2173
2174 this.normals.Add(SurfaceNormal(i).Normalize());
2175
2176 int normIndex = normals.Count - 1;
2177 face.n1 = normIndex;
2178 face.n2 = normIndex;
2179 face.n3 = normIndex;
2180
2181 this.faces[i] = face;
2182 }
2183 }
2184
2185 /// <summary>
2186 /// Adds a value to each XYZ vertex coordinate in the mesh
2187 /// </summary>
2188 /// <param name="x"></param>
2189 /// <param name="y"></param>
2190 /// <param name="z"></param>
2191 public void AddPos(float x, float y, float z)
2192 {
2193 int i;
2194 int numVerts = this.coords.Count;
2195 Coord vert;
2196
2197 for (i = 0; i < numVerts; i++)
2198 {
2199 vert = this.coords[i];
2200 vert.X += x;
2201 vert.Y += y;
2202 vert.Z += z;
2203 this.coords[i] = vert;
2204 }
2205
2206 if (this.viewerFaces != null)
2207 {
2208 int numViewerFaces = this.viewerFaces.Count;
2209
2210 for (i = 0; i < numViewerFaces; i++)
2211 {
2212 ViewerFace v = this.viewerFaces[i];
2213 v.AddPos(x, y, z);
2214 this.viewerFaces[i] = v;
2215 }
2216 }
2217 }
2218
2219 /// <summary>
2220 /// Rotates the mesh
2221 /// </summary>
2222 /// <param name="q"></param>
2223 public void AddRot(Quat q)
2224 {
2225 int i;
2226 int numVerts = this.coords.Count;
2227
2228 for (i = 0; i < numVerts; i++)
2229 this.coords[i] *= q;
2230
2231 if (this.normals != null)
2232 {
2233 int numNormals = this.normals.Count;
2234 for (i = 0; i < numNormals; i++)
2235 this.normals[i] *= q;
2236 }
2237
2238 if (this.viewerFaces != null)
2239 {
2240 int numViewerFaces = this.viewerFaces.Count;
2241
2242 for (i = 0; i < numViewerFaces; i++)
2243 {
2244 ViewerFace v = this.viewerFaces[i];
2245 v.v1 *= q;
2246 v.v2 *= q;
2247 v.v3 *= q;
2248
2249 v.n1 *= q;
2250 v.n2 *= q;
2251 v.n3 *= q;
2252 this.viewerFaces[i] = v;
2253 }
2254 }
2255 }
2256
2257#if VERTEX_INDEXER
2258 public VertexIndexer GetVertexIndexer()
2259 {
2260 if (this.viewerMode && this.viewerFaces.Count > 0)
2261 return new VertexIndexer(this);
2262 return null;
2263 }
2264#endif
2265
2266 /// <summary>
2267 /// Scales the mesh
2268 /// </summary>
2269 /// <param name="x"></param>
2270 /// <param name="y"></param>
2271 /// <param name="z"></param>
2272 public void Scale(float x, float y, float z)
2273 {
2274 int i;
2275 int numVerts = this.coords.Count;
2276 //Coord vert;
2277
2278 Coord m = new Coord(x, y, z);
2279 for (i = 0; i < numVerts; i++)
2280 this.coords[i] *= m;
2281
2282 if (this.viewerFaces != null)
2283 {
2284 int numViewerFaces = this.viewerFaces.Count;
2285 for (i = 0; i < numViewerFaces; i++)
2286 {
2287 ViewerFace v = this.viewerFaces[i];
2288 v.v1 *= m;
2289 v.v2 *= m;
2290 v.v3 *= m;
2291 this.viewerFaces[i] = v;
2292 }
2293
2294 }
2295
2296 }
2297
2298 /// <summary>
2299 /// Dumps the mesh to a Blender compatible "Raw" format file
2300 /// </summary>
2301 /// <param name="path"></param>
2302 /// <param name="name"></param>
2303 /// <param name="title"></param>
2304 public void DumpRaw(String path, String name, String title)
2305 {
2306 if (path == null)
2307 return;
2308 String fileName = name + "_" + title + ".raw";
2309 String completePath = System.IO.Path.Combine(path, fileName);
2310 StreamWriter sw = new StreamWriter(completePath);
2311
2312 for (int i = 0; i < this.faces.Count; i++)
2313 {
2314 string s = this.coords[this.faces[i].v1].ToString();
2315 s += " " + this.coords[this.faces[i].v2].ToString();
2316 s += " " + this.coords[this.faces[i].v3].ToString();
2317
2318 sw.WriteLine(s);
2319 }
2320
2321 sw.Close();
2322 }
2323 }
2324}
diff --git a/OpenSim/Region/Physics/UbitMeshing/SculptMap.cs b/OpenSim/Region/Physics/UbitMeshing/SculptMap.cs
new file mode 100644
index 0000000..1c75db6
--- /dev/null
+++ b/OpenSim/Region/Physics/UbitMeshing/SculptMap.cs
@@ -0,0 +1,244 @@
1/*
2 * Copyright (c) Contributors
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Text;
31
32using System.Drawing;
33using System.Drawing.Imaging;
34
35namespace PrimMesher
36{
37 public class SculptMap
38 {
39 public int width;
40 public int height;
41 public byte[] redBytes;
42 public byte[] greenBytes;
43 public byte[] blueBytes;
44
45 public SculptMap()
46 {
47 }
48
49 public SculptMap(Bitmap bm, int lod)
50 {
51 int bmW = bm.Width;
52 int bmH = bm.Height;
53
54 if (bmW == 0 || bmH == 0)
55 throw new Exception("SculptMap: bitmap has no data");
56
57 int numLodPixels = lod * lod; // (32 * 2)^2 = 64^2 pixels for default sculpt map image
58
59 bool needsScaling = false;
60 bool smallMap = false;
61
62 width = bmW;
63 height = bmH;
64
65 while (width * height > numLodPixels * 4)
66 {
67 width >>= 1;
68 height >>= 1;
69 needsScaling = true;
70 }
71
72 try
73 {
74 if (needsScaling)
75 bm = ScaleImage(bm, width, height);
76 }
77
78 catch (Exception e)
79 {
80 throw new Exception("Exception in ScaleImage(): e: " + e.ToString());
81 }
82
83 if (width * height > numLodPixels)
84 {
85 smallMap = false;
86 width >>= 1;
87 height >>= 1;
88 }
89 else
90 smallMap = true;
91
92 int numBytes = (width + 1) * (height + 1);
93 redBytes = new byte[numBytes];
94 greenBytes = new byte[numBytes];
95 blueBytes = new byte[numBytes];
96
97 int byteNdx = 0;
98 Color c;
99
100 try
101 {
102 for (int y = 0; y <= height; y++)
103 {
104 for (int x = 0; x < width; x++)
105 {
106 if (smallMap)
107 c = bm.GetPixel(x, y < height ? y : y - 1);
108 else
109 c = bm.GetPixel(x * 2, y < height ? y * 2 : y * 2 - 1);
110
111 redBytes[byteNdx] = c.R;
112 greenBytes[byteNdx] = c.G;
113 blueBytes[byteNdx] = c.B;
114
115 ++byteNdx;
116 }
117
118 if (smallMap)
119 c = bm.GetPixel(width - 1, y < height ? y : y - 1);
120 else
121 c = bm.GetPixel(width * 2 - 1, y < height ? y * 2 : y * 2 - 1);
122
123 redBytes[byteNdx] = c.R;
124 greenBytes[byteNdx] = c.G;
125 blueBytes[byteNdx] = c.B;
126
127 ++byteNdx;
128 }
129 }
130 catch (Exception e)
131 {
132 throw new Exception("Caught exception processing byte arrays in SculptMap(): e: " + e.ToString());
133 }
134
135 width++;
136 height++;
137 }
138
139 public List<List<Coord>> ToRows(bool mirror)
140 {
141 int numRows = height;
142 int numCols = width;
143
144 List<List<Coord>> rows = new List<List<Coord>>(numRows);
145
146 float pixScale = 1.0f / 255;
147
148 int rowNdx, colNdx;
149 int smNdx = 0;
150
151 for (rowNdx = 0; rowNdx < numRows; rowNdx++)
152 {
153 List<Coord> row = new List<Coord>(numCols);
154 for (colNdx = 0; colNdx < numCols; colNdx++)
155 {
156
157 if (mirror)
158 row.Add(new Coord(-((float)redBytes[smNdx] * pixScale - 0.5f), ((float)greenBytes[smNdx] * pixScale - 0.5f), (float)blueBytes[smNdx] * pixScale - 0.5f));
159 else
160 row.Add(new Coord((float)redBytes[smNdx] * pixScale - 0.5f, (float)greenBytes[smNdx] * pixScale - 0.5f, (float)blueBytes[smNdx] * pixScale - 0.5f));
161
162 ++smNdx;
163 }
164 rows.Add(row);
165 }
166 return rows;
167 }
168
169 private Bitmap ScaleImage(Bitmap srcImage, int destWidth, int destHeight)
170 {
171
172 Bitmap scaledImage = new Bitmap(destWidth, destHeight, PixelFormat.Format24bppRgb);
173
174 Color c;
175
176
177 // will let last step to be eventually diferent, as seems to be in sl
178
179 float xscale = (float)srcImage.Width / (float)destWidth;
180 float yscale = (float)srcImage.Height / (float)destHeight;
181
182 int lastsx = srcImage.Width - 1;
183 int lastsy = srcImage.Height - 1;
184 int lastdx = destWidth - 1;
185 int lastdy = destHeight - 1;
186
187 float sy = 0.5f;
188 float sx;
189
190 for (int y = 0; y < lastdy; y++)
191 {
192 sx = 0.5f;
193 for (int x = 0; x < lastdx; x++)
194 {
195 try
196 {
197 c = srcImage.GetPixel((int)(sx), (int)(sy));
198 scaledImage.SetPixel(x, y, Color.FromArgb(c.R, c.G, c.B));
199 }
200 catch (IndexOutOfRangeException)
201 {
202 }
203 sx += xscale;
204 }
205 try
206 {
207 c = srcImage.GetPixel(lastsx, (int)(sy));
208 scaledImage.SetPixel(lastdx, y, Color.FromArgb(c.R, c.G, c.B));
209 }
210 catch (IndexOutOfRangeException)
211 {
212 }
213
214 sy += yscale;
215 }
216
217 sx = 0.5f;
218 for (int x = 0; x < lastdx; x++)
219 {
220 try
221 {
222 c = srcImage.GetPixel((int)(sx), lastsy);
223 scaledImage.SetPixel(x, lastdy, Color.FromArgb(c.R, c.G, c.B));
224 }
225 catch (IndexOutOfRangeException)
226 {
227 }
228
229 sx += xscale;
230 }
231 try
232 {
233 c = srcImage.GetPixel(lastsx, lastsy);
234 scaledImage.SetPixel(lastdx, lastdy, Color.FromArgb(c.R, c.G, c.B));
235 }
236 catch (IndexOutOfRangeException)
237 {
238 }
239
240 srcImage.Dispose();
241 return scaledImage;
242 }
243 }
244} \ No newline at end of file
diff --git a/OpenSim/Region/Physics/UbitMeshing/SculptMesh.cs b/OpenSim/Region/Physics/UbitMeshing/SculptMesh.cs
new file mode 100644
index 0000000..bc1375b
--- /dev/null
+++ b/OpenSim/Region/Physics/UbitMeshing/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}