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