aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/Meshing
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics/Meshing')
-rw-r--r--OpenSim/Region/Physics/Meshing/Meshmerizer.cs727
-rw-r--r--OpenSim/Region/Physics/Meshing/PrimMesher.cs132
-rw-r--r--OpenSim/Region/Physics/Meshing/SculptMap.cs183
-rw-r--r--OpenSim/Region/Physics/Meshing/SculptMesh.cs149
4 files changed, 845 insertions, 346 deletions
diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
index fded95e..f15e81b 100644
--- a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
+++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
@@ -31,12 +31,16 @@ using System.Collections.Generic;
31using OpenSim.Framework; 31using OpenSim.Framework;
32using OpenSim.Region.Physics.Manager; 32using OpenSim.Region.Physics.Manager;
33using OpenMetaverse; 33using OpenMetaverse;
34using OpenMetaverse.StructuredData;
34using System.Drawing; 35using System.Drawing;
35using System.Drawing.Imaging; 36using System.Drawing.Imaging;
37using System.IO.Compression;
36using PrimMesher; 38using PrimMesher;
37using log4net; 39using log4net;
40using Nini.Config;
38using System.Reflection; 41using System.Reflection;
39using System.IO; 42using System.IO;
43using ComponentAce.Compression.Libs.zlib;
40 44
41namespace OpenSim.Region.Physics.Meshing 45namespace OpenSim.Region.Physics.Meshing
42{ 46{
@@ -51,9 +55,9 @@ namespace OpenSim.Region.Physics.Meshing
51 return "Meshmerizer"; 55 return "Meshmerizer";
52 } 56 }
53 57
54 public IMesher GetMesher() 58 public IMesher GetMesher(IConfigSource config)
55 { 59 {
56 return new Meshmerizer(); 60 return new Meshmerizer(config);
57 } 61 }
58 } 62 }
59 63
@@ -70,24 +74,32 @@ namespace OpenSim.Region.Physics.Meshing
70#endif 74#endif
71 75
72 private bool cacheSculptMaps = true; 76 private bool cacheSculptMaps = true;
73 private string decodedScultMapPath = "j2kDecodeCache"; 77 private string decodedSculptMapPath = null;
78 private bool useMeshiesPhysicsMesh = false;
74 79
75 private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh 80 private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh
76 81
77 private Dictionary<ulong, Mesh> m_uniqueMeshes = new Dictionary<ulong, Mesh>(); 82 private Dictionary<ulong, Mesh> m_uniqueMeshes = new Dictionary<ulong, Mesh>();
78 83
79 public Meshmerizer() 84 public Meshmerizer(IConfigSource config)
80 { 85 {
86 IConfig start_config = config.Configs["Startup"];
87 IConfig mesh_config = config.Configs["Mesh"];
88
89 decodedSculptMapPath = start_config.GetString("DecodedSculptMapPath","j2kDecodeCache");
90 cacheSculptMaps = start_config.GetBoolean("CacheSculptMaps", cacheSculptMaps);
91 if(mesh_config != null)
92 useMeshiesPhysicsMesh = mesh_config.GetBoolean("UseMeshiesPhysicsMesh", useMeshiesPhysicsMesh);
93
81 try 94 try
82 { 95 {
83 if (!Directory.Exists(decodedScultMapPath)) 96 if (!Directory.Exists(decodedSculptMapPath))
84 Directory.CreateDirectory(decodedScultMapPath); 97 Directory.CreateDirectory(decodedSculptMapPath);
85 } 98 }
86 catch (Exception e) 99 catch (Exception e)
87 { 100 {
88 m_log.WarnFormat("[SCULPT]: Unable to create {0} directory: ", decodedScultMapPath, e.Message); 101 m_log.WarnFormat("[SCULPT]: Unable to create {0} directory: ", decodedSculptMapPath, e.Message);
89 } 102 }
90
91 } 103 }
92 104
93 /// <summary> 105 /// <summary>
@@ -143,7 +155,6 @@ namespace OpenSim.Region.Physics.Meshing
143 return box; 155 return box;
144 } 156 }
145 157
146
147 /// <summary> 158 /// <summary>
148 /// Creates a simple bounding box mesh for a complex input mesh 159 /// Creates a simple bounding box mesh for a complex input mesh
149 /// </summary> 160 /// </summary>
@@ -160,16 +171,13 @@ namespace OpenSim.Region.Physics.Meshing
160 171
161 foreach (Vector3 v in meshIn.getVertexList()) 172 foreach (Vector3 v in meshIn.getVertexList())
162 { 173 {
163 if (v != null) 174 if (v.X < minX) minX = v.X;
164 { 175 if (v.Y < minY) minY = v.Y;
165 if (v.X < minX) minX = v.X; 176 if (v.Z < minZ) minZ = v.Z;
166 if (v.Y < minY) minY = v.Y;
167 if (v.Z < minZ) minZ = v.Z;
168 177
169 if (v.X > maxX) maxX = v.X; 178 if (v.X > maxX) maxX = v.X;
170 if (v.Y > maxY) maxY = v.Y; 179 if (v.Y > maxY) maxY = v.Y;
171 if (v.Z > maxZ) maxZ = v.Z; 180 if (v.Z > maxZ) maxZ = v.Z;
172 }
173 } 181 }
174 182
175 return CreateSimpleBoxMesh(minX, maxX, minY, maxY, minZ, maxZ); 183 return CreateSimpleBoxMesh(minX, maxX, minY, maxY, minZ, maxZ);
@@ -180,309 +188,513 @@ namespace OpenSim.Region.Physics.Meshing
180 m_log.Error(message); 188 m_log.Error(message);
181 m_log.Error("\nPrim Name: " + primName); 189 m_log.Error("\nPrim Name: " + primName);
182 m_log.Error("****** PrimMesh Parameters ******\n" + primMesh.ParamsToDisplayString()); 190 m_log.Error("****** PrimMesh Parameters ******\n" + primMesh.ParamsToDisplayString());
191 }
183 192
193 /// <summary>
194 /// Add a submesh to an existing list of coords and faces.
195 /// </summary>
196 /// <param name="subMeshData"></param>
197 /// <param name="size">Size of entire object</param>
198 /// <param name="coords"></param>
199 /// <param name="faces"></param>
200 private void AddSubMesh(OSDMap subMeshData, Vector3 size, List<Coord> coords, List<Face> faces)
201 {
202 // Console.WriteLine("subMeshMap for {0} - {1}", primName, Util.GetFormattedXml((OSD)subMeshMap));
203
204 // As per http://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format, some Mesh Level
205 // of Detail Blocks (maps) contain just a NoGeometry key to signal there is no
206 // geometry for this submesh.
207 if (subMeshData.ContainsKey("NoGeometry") && ((OSDBoolean)subMeshData["NoGeometry"]))
208 return;
209
210 OpenMetaverse.Vector3 posMax = ((OSDMap)subMeshData["PositionDomain"])["Max"].AsVector3();
211 OpenMetaverse.Vector3 posMin = ((OSDMap)subMeshData["PositionDomain"])["Min"].AsVector3();
212 ushort faceIndexOffset = (ushort)coords.Count;
213
214 byte[] posBytes = subMeshData["Position"].AsBinary();
215 for (int i = 0; i < posBytes.Length; i += 6)
216 {
217 ushort uX = Utils.BytesToUInt16(posBytes, i);
218 ushort uY = Utils.BytesToUInt16(posBytes, i + 2);
219 ushort uZ = Utils.BytesToUInt16(posBytes, i + 4);
220
221 Coord c = new Coord(
222 Utils.UInt16ToFloat(uX, posMin.X, posMax.X) * size.X,
223 Utils.UInt16ToFloat(uY, posMin.Y, posMax.Y) * size.Y,
224 Utils.UInt16ToFloat(uZ, posMin.Z, posMax.Z) * size.Z);
225
226 coords.Add(c);
227 }
228
229 byte[] triangleBytes = subMeshData["TriangleList"].AsBinary();
230 for (int i = 0; i < triangleBytes.Length; i += 6)
231 {
232 ushort v1 = (ushort)(Utils.BytesToUInt16(triangleBytes, i) + faceIndexOffset);
233 ushort v2 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 2) + faceIndexOffset);
234 ushort v3 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 4) + faceIndexOffset);
235 Face f = new Face(v1, v2, v3);
236 faces.Add(f);
237 }
184 } 238 }
185 239
186 private ulong GetMeshKey(PrimitiveBaseShape pbs, Vector3 size, float lod) 240 /// <summary>
241 /// Create a physics mesh from data that comes with the prim. The actual data used depends on the prim type.
242 /// </summary>
243 /// <param name="primName"></param>
244 /// <param name="primShape"></param>
245 /// <param name="size"></param>
246 /// <param name="lod"></param>
247 /// <returns></returns>
248 private Mesh CreateMeshFromPrimMesher(string primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
187 { 249 {
188 ulong hash = 5381; 250// m_log.DebugFormat(
189 251// "[MESH]: Creating physics proxy for {0}, shape {1}",
190 hash = djb2(hash, pbs.PathCurve); 252// primName, (OpenMetaverse.SculptType)primShape.SculptType);
191 hash = djb2(hash, (byte)((byte)pbs.HollowShape | (byte)pbs.ProfileShape)); 253
192 hash = djb2(hash, pbs.PathBegin); 254 List<Coord> coords;
193 hash = djb2(hash, pbs.PathEnd); 255 List<Face> faces;
194 hash = djb2(hash, pbs.PathScaleX); 256
195 hash = djb2(hash, pbs.PathScaleY); 257 if (primShape.SculptEntry)
196 hash = djb2(hash, pbs.PathShearX); 258 {
197 hash = djb2(hash, pbs.PathShearY); 259 if (((OpenMetaverse.SculptType)primShape.SculptType) == SculptType.Mesh)
198 hash = djb2(hash, (byte)pbs.PathTwist); 260 {
199 hash = djb2(hash, (byte)pbs.PathTwistBegin); 261 if (!useMeshiesPhysicsMesh)
200 hash = djb2(hash, (byte)pbs.PathRadiusOffset); 262 return null;
201 hash = djb2(hash, (byte)pbs.PathTaperX); 263
202 hash = djb2(hash, (byte)pbs.PathTaperY); 264 if (!GenerateCoordsAndFacesFromPrimMeshData(primName, primShape, size, out coords, out faces))
203 hash = djb2(hash, pbs.PathRevolutions); 265 return null;
204 hash = djb2(hash, (byte)pbs.PathSkew); 266 }
205 hash = djb2(hash, pbs.ProfileBegin); 267 else
206 hash = djb2(hash, pbs.ProfileEnd); 268 {
207 hash = djb2(hash, pbs.ProfileHollow); 269 if (!GenerateCoordsAndFacesFromPrimSculptData(primName, primShape, size, lod, out coords, out faces))
208 270 return null;
209 // TODO: Separate scale out from the primitive shape data (after 271 }
210 // scaling is supported at the physics engine level) 272 }
211 byte[] scaleBytes = size.GetBytes(); 273 else
212 for (int i = 0; i < scaleBytes.Length; i++)
213 hash = djb2(hash, scaleBytes[i]);
214
215 // Include LOD in hash, accounting for endianness
216 byte[] lodBytes = new byte[4];
217 Buffer.BlockCopy(BitConverter.GetBytes(lod), 0, lodBytes, 0, 4);
218 if (!BitConverter.IsLittleEndian)
219 { 274 {
220 Array.Reverse(lodBytes, 0, 4); 275 if (!GenerateCoordsAndFacesFromPrimShapeData(primName, primShape, size, lod, out coords, out faces))
276 return null;
221 } 277 }
222 for (int i = 0; i < lodBytes.Length; i++)
223 hash = djb2(hash, lodBytes[i]);
224 278
225 // include sculpt UUID 279 // Remove the reference to any JPEG2000 sculpt data so it can be GCed
226 if (pbs.SculptEntry) 280 primShape.SculptData = Utils.EmptyBytes;
281
282 int numCoords = coords.Count;
283 int numFaces = faces.Count;
284
285 // Create the list of vertices
286 List<Vertex> vertices = new List<Vertex>();
287 for (int i = 0; i < numCoords; i++)
227 { 288 {
228 scaleBytes = pbs.SculptTexture.GetBytes(); 289 Coord c = coords[i];
229 for (int i = 0; i < scaleBytes.Length; i++) 290 vertices.Add(new Vertex(c.X, c.Y, c.Z));
230 hash = djb2(hash, scaleBytes[i]);
231 } 291 }
232 292
233 return hash; 293 Mesh mesh = new Mesh();
234 } 294 // Add the corresponding triangles to the mesh
295 for (int i = 0; i < numFaces; i++)
296 {
297 Face f = faces[i];
298 mesh.Add(new Triangle(vertices[f.v1], vertices[f.v2], vertices[f.v3]));
299 }
235 300
236 private ulong djb2(ulong hash, byte c) 301 return mesh;
237 {
238 return ((hash << 5) + hash) + (ulong)c;
239 } 302 }
240 303
241 private ulong djb2(ulong hash, ushort c) 304 /// <summary>
305 /// Generate the co-ords and faces necessary to construct a mesh from the mesh data the accompanies a prim.
306 /// </summary>
307 /// <param name="primName"></param>
308 /// <param name="primShape"></param>
309 /// <param name="size"></param>
310 /// <param name="coords">Coords are added to this list by the method.</param>
311 /// <param name="faces">Faces are added to this list by the method.</param>
312 /// <returns>true if coords and faces were successfully generated, false if not</returns>
313 private bool GenerateCoordsAndFacesFromPrimMeshData(
314 string primName, PrimitiveBaseShape primShape, Vector3 size, out List<Coord> coords, out List<Face> faces)
242 { 315 {
243 hash = ((hash << 5) + hash) + (ulong)((byte)c); 316 m_log.DebugFormat("[MESH]: experimental mesh proxy generation for {0}", primName);
244 return ((hash << 5) + hash) + (ulong)(c >> 8);
245 }
246 317
318 coords = new List<Coord>();
319 faces = new List<Face>();
320 OSD meshOsd = null;
247 321
248 private Mesh CreateMeshFromPrimMesher(string primName, PrimitiveBaseShape primShape, Vector3 size, float lod) 322 if (primShape.SculptData.Length <= 0)
249 { 323 {
250 PrimMesh primMesh; 324 m_log.Error("[MESH]: asset data is zero length");
251 PrimMesher.SculptMesh sculptMesh; 325 return false;
326 }
252 327
253 List<Coord> coords; 328 long start = 0;
254 List<Face> faces; 329 using (MemoryStream data = new MemoryStream(primShape.SculptData))
330 {
331 try
332 {
333 OSD osd = OSDParser.DeserializeLLSDBinary(data);
334 if (osd is OSDMap)
335 meshOsd = (OSDMap)osd;
336 else
337 {
338 m_log.Warn("[Mesh}: unable to cast mesh asset to OSDMap");
339 return false;
340 }
341 }
342 catch (Exception e)
343 {
344 m_log.Error("[MESH]: Exception deserializing mesh asset header:" + e.ToString());
345 }
255 346
256 Image idata = null; 347 start = data.Position;
257 string decodedSculptFileName = ""; 348 }
258 349
259 if (primShape.SculptEntry) 350 if (meshOsd is OSDMap)
260 { 351 {
261 if (cacheSculptMaps && primShape.SculptTexture != UUID.Zero) 352 OSDMap physicsParms = null;
353 OSDMap map = (OSDMap)meshOsd;
354 if (map.ContainsKey("physics_shape"))
355 physicsParms = (OSDMap)map["physics_shape"]; // old asset format
356 else if (map.ContainsKey("physics_mesh"))
357 physicsParms = (OSDMap)map["physics_mesh"]; // new asset format
358
359 if (physicsParms == null)
262 { 360 {
263 decodedSculptFileName = System.IO.Path.Combine(decodedScultMapPath, "smap_" + primShape.SculptTexture.ToString()); 361 m_log.Warn("[MESH]: no recognized physics mesh found in mesh asset");
264 try 362 return false;
363 }
364
365 int physOffset = physicsParms["offset"].AsInteger() + (int)start;
366 int physSize = physicsParms["size"].AsInteger();
367
368 if (physOffset < 0 || physSize == 0)
369 return false; // no mesh data in asset
370
371 OSD decodedMeshOsd = new OSD();
372 byte[] meshBytes = new byte[physSize];
373 System.Buffer.BlockCopy(primShape.SculptData, physOffset, meshBytes, 0, physSize);
374// byte[] decompressed = new byte[physSize * 5];
375 try
376 {
377 using (MemoryStream inMs = new MemoryStream(meshBytes))
265 { 378 {
266 if (File.Exists(decodedSculptFileName)) 379 using (MemoryStream outMs = new MemoryStream())
267 { 380 {
268 idata = Image.FromFile(decodedSculptFileName); 381 using (ZOutputStream zOut = new ZOutputStream(outMs))
382 {
383 byte[] readBuffer = new byte[2048];
384 int readLen = 0;
385 while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0)
386 {
387 zOut.Write(readBuffer, 0, readLen);
388 }
389 zOut.Flush();
390 outMs.Seek(0, SeekOrigin.Begin);
391
392 byte[] decompressedBuf = outMs.GetBuffer();
393
394 decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
395 }
269 } 396 }
270 } 397 }
271 catch (Exception e) 398 }
272 { 399 catch (Exception e)
273 m_log.Error("[SCULPT]: unable to load cached sculpt map " + decodedSculptFileName + " " + e.Message); 400 {
274 401 m_log.Error("[MESH]: exception decoding physical mesh: " + e.ToString());
275 } 402 return false;
276 //if (idata != null)
277 // m_log.Debug("[SCULPT]: loaded cached map asset for map ID: " + primShape.SculptTexture.ToString());
278 } 403 }
279 404
280 if (idata == null) 405 OSDArray decodedMeshOsdArray = null;
406
407 // physics_shape is an array of OSDMaps, one for each submesh
408 if (decodedMeshOsd is OSDArray)
281 { 409 {
282 if (primShape.SculptData == null || primShape.SculptData.Length == 0) 410// Console.WriteLine("decodedMeshOsd for {0} - {1}", primName, Util.GetFormattedXml(decodedMeshOsd));
283 return null;
284 411
285 try 412 decodedMeshOsdArray = (OSDArray)decodedMeshOsd;
413 foreach (OSD subMeshOsd in decodedMeshOsdArray)
286 { 414 {
287 OpenMetaverse.Imaging.ManagedImage unusedData; 415 if (subMeshOsd is OSDMap)
288 OpenMetaverse.Imaging.OpenJPEG.DecodeToImage(primShape.SculptData, out unusedData, out idata); 416 AddSubMesh(subMeshOsd as OSDMap, size, coords, faces);
289 unusedData = null; 417 }
418 }
419 }
290 420
291 //idata = CSJ2K.J2kImage.FromBytes(primShape.SculptData); 421 return true;
422 }
292 423
293 if (cacheSculptMaps && idata != null) 424 /// <summary>
294 { 425 /// Generate the co-ords and faces necessary to construct a mesh from the sculpt data the accompanies a prim.
295 try { idata.Save(decodedSculptFileName, ImageFormat.MemoryBmp); } 426 /// </summary>
296 catch (Exception e) { m_log.Error("[SCULPT]: unable to cache sculpt map " + decodedSculptFileName + " " + e.Message); } 427 /// <param name="primName"></param>
297 } 428 /// <param name="primShape"></param>
298 } 429 /// <param name="size"></param>
299 catch (DllNotFoundException) 430 /// <param name="lod"></param>
431 /// <param name="coords">Coords are added to this list by the method.</param>
432 /// <param name="faces">Faces are added to this list by the method.</param>
433 /// <returns>true if coords and faces were successfully generated, false if not</returns>
434 private bool GenerateCoordsAndFacesFromPrimSculptData(
435 string primName, PrimitiveBaseShape primShape, Vector3 size, float lod, out List<Coord> coords, out List<Face> faces)
436 {
437 coords = new List<Coord>();
438 faces = new List<Face>();
439 PrimMesher.SculptMesh sculptMesh;
440 Image idata = null;
441 string decodedSculptFileName = "";
442
443 if (cacheSculptMaps && primShape.SculptTexture != UUID.Zero)
444 {
445 decodedSculptFileName = System.IO.Path.Combine(decodedSculptMapPath, "smap_" + primShape.SculptTexture.ToString());
446 try
447 {
448 if (File.Exists(decodedSculptFileName))
300 { 449 {
301 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!"); 450 idata = Image.FromFile(decodedSculptFileName);
302 return null;
303 } 451 }
304 catch (IndexOutOfRangeException) 452 }
453 catch (Exception e)
454 {
455 m_log.Error("[SCULPT]: unable to load cached sculpt map " + decodedSculptFileName + " " + e.Message);
456
457 }
458 //if (idata != null)
459 // m_log.Debug("[SCULPT]: loaded cached map asset for map ID: " + primShape.SculptTexture.ToString());
460 }
461
462 if (idata == null)
463 {
464 if (primShape.SculptData == null || primShape.SculptData.Length == 0)
465 return false;
466
467 try
468 {
469 OpenMetaverse.Imaging.ManagedImage unusedData;
470 OpenMetaverse.Imaging.OpenJPEG.DecodeToImage(primShape.SculptData, out unusedData, out idata);
471
472 if (idata == null)
305 { 473 {
306 m_log.Error("[PHYSICS]: OpenJpeg was unable to decode this. Physics Proxy generation failed"); 474 // In some cases it seems that the decode can return a null bitmap without throwing
307 return null; 475 // an exception
476 m_log.WarnFormat("[PHYSICS]: OpenJPEG decoded sculpt data for {0} to a null bitmap. Ignoring.", primName);
477
478 return false;
308 } 479 }
309 catch (Exception ex) 480
481 unusedData = null;
482
483 //idata = CSJ2K.J2kImage.FromBytes(primShape.SculptData);
484
485 if (cacheSculptMaps)
310 { 486 {
311 m_log.Error("[PHYSICS]: Unable to generate a Sculpty physics proxy. Sculpty texture decode failed: " + ex.Message); 487 try { idata.Save(decodedSculptFileName, ImageFormat.MemoryBmp); }
312 return null; 488 catch (Exception e) { m_log.Error("[SCULPT]: unable to cache sculpt map " + decodedSculptFileName + " " + e.Message); }
313 } 489 }
314 } 490 }
315 491 catch (DllNotFoundException)
316 PrimMesher.SculptMesh.SculptType sculptType; 492 {
317 switch ((OpenMetaverse.SculptType)primShape.SculptType) 493 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!");
494 return false;
495 }
496 catch (IndexOutOfRangeException)
318 { 497 {
319 case OpenMetaverse.SculptType.Cylinder: 498 m_log.Error("[PHYSICS]: OpenJpeg was unable to decode this. Physics Proxy generation failed");
320 sculptType = PrimMesher.SculptMesh.SculptType.cylinder; 499 return false;
321 break;
322 case OpenMetaverse.SculptType.Plane:
323 sculptType = PrimMesher.SculptMesh.SculptType.plane;
324 break;
325 case OpenMetaverse.SculptType.Torus:
326 sculptType = PrimMesher.SculptMesh.SculptType.torus;
327 break;
328 case OpenMetaverse.SculptType.Sphere:
329 sculptType = PrimMesher.SculptMesh.SculptType.sphere;
330 break;
331 default:
332 sculptType = PrimMesher.SculptMesh.SculptType.plane;
333 break;
334 } 500 }
501 catch (Exception ex)
502 {
503 m_log.Error("[PHYSICS]: Unable to generate a Sculpty physics proxy. Sculpty texture decode failed: " + ex.Message);
504 return false;
505 }
506 }
335 507
336 bool mirror = ((primShape.SculptType & 128) != 0); 508 PrimMesher.SculptMesh.SculptType sculptType;
337 bool invert = ((primShape.SculptType & 64) != 0); 509 switch ((OpenMetaverse.SculptType)primShape.SculptType)
510 {
511 case OpenMetaverse.SculptType.Cylinder:
512 sculptType = PrimMesher.SculptMesh.SculptType.cylinder;
513 break;
514 case OpenMetaverse.SculptType.Plane:
515 sculptType = PrimMesher.SculptMesh.SculptType.plane;
516 break;
517 case OpenMetaverse.SculptType.Torus:
518 sculptType = PrimMesher.SculptMesh.SculptType.torus;
519 break;
520 case OpenMetaverse.SculptType.Sphere:
521 sculptType = PrimMesher.SculptMesh.SculptType.sphere;
522 break;
523 default:
524 sculptType = PrimMesher.SculptMesh.SculptType.plane;
525 break;
526 }
338 527
339 sculptMesh = new PrimMesher.SculptMesh((Bitmap)idata, sculptType, (int)lod, false, mirror, invert); 528 bool mirror = ((primShape.SculptType & 128) != 0);
340 529 bool invert = ((primShape.SculptType & 64) != 0);
341 idata.Dispose();
342 530
343 sculptMesh.DumpRaw(baseDir, primName, "primMesh"); 531 sculptMesh = new PrimMesher.SculptMesh((Bitmap)idata, sculptType, (int)lod, false, mirror, invert);
344 532
345 sculptMesh.Scale(size.X, size.Y, size.Z); 533 idata.Dispose();
346 534
347 coords = sculptMesh.coords; 535 sculptMesh.DumpRaw(baseDir, primName, "primMesh");
348 faces = sculptMesh.faces; 536
349 } 537 sculptMesh.Scale(size.X, size.Y, size.Z);
350 else 538
539 coords = sculptMesh.coords;
540 faces = sculptMesh.faces;
541
542 return true;
543 }
544
545 /// <summary>
546 /// Generate the co-ords and faces necessary to construct a mesh from the shape data the accompanies a prim.
547 /// </summary>
548 /// <param name="primName"></param>
549 /// <param name="primShape"></param>
550 /// <param name="size"></param>
551 /// <param name="coords">Coords are added to this list by the method.</param>
552 /// <param name="faces">Faces are added to this list by the method.</param>
553 /// <returns>true if coords and faces were successfully generated, false if not</returns>
554 private bool GenerateCoordsAndFacesFromPrimShapeData(
555 string primName, PrimitiveBaseShape primShape, Vector3 size, float lod, out List<Coord> coords, out List<Face> faces)
556 {
557 PrimMesh primMesh;
558 coords = new List<Coord>();
559 faces = new List<Face>();
560
561 float pathShearX = primShape.PathShearX < 128 ? (float)primShape.PathShearX * 0.01f : (float)(primShape.PathShearX - 256) * 0.01f;
562 float pathShearY = primShape.PathShearY < 128 ? (float)primShape.PathShearY * 0.01f : (float)(primShape.PathShearY - 256) * 0.01f;
563 float pathBegin = (float)primShape.PathBegin * 2.0e-5f;
564 float pathEnd = 1.0f - (float)primShape.PathEnd * 2.0e-5f;
565 float pathScaleX = (float)(primShape.PathScaleX - 100) * 0.01f;
566 float pathScaleY = (float)(primShape.PathScaleY - 100) * 0.01f;
567
568 float profileBegin = (float)primShape.ProfileBegin * 2.0e-5f;
569 float profileEnd = 1.0f - (float)primShape.ProfileEnd * 2.0e-5f;
570 float profileHollow = (float)primShape.ProfileHollow * 2.0e-5f;
571 if (profileHollow > 0.95f)
572 profileHollow = 0.95f;
573
574 int sides = 4;
575 LevelOfDetail iLOD = (LevelOfDetail)lod;
576 if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
577 sides = 3;
578 else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
351 { 579 {
352 float pathShearX = primShape.PathShearX < 128 ? (float)primShape.PathShearX * 0.01f : (float)(primShape.PathShearX - 256) * 0.01f; 580 switch (iLOD)
353 float pathShearY = primShape.PathShearY < 128 ? (float)primShape.PathShearY * 0.01f : (float)(primShape.PathShearY - 256) * 0.01f; 581 {
354 float pathBegin = (float)primShape.PathBegin * 2.0e-5f; 582 case LevelOfDetail.High: sides = 24; break;
355 float pathEnd = 1.0f - (float)primShape.PathEnd * 2.0e-5f; 583 case LevelOfDetail.Medium: sides = 12; break;
356 float pathScaleX = (float)(primShape.PathScaleX - 100) * 0.01f; 584 case LevelOfDetail.Low: sides = 6; break;
357 float pathScaleY = (float)(primShape.PathScaleY - 100) * 0.01f; 585 case LevelOfDetail.VeryLow: sides = 3; break;
358 586 default: sides = 24; break;
359 float profileBegin = (float)primShape.ProfileBegin * 2.0e-5f; 587 }
360 float profileEnd = 1.0f - (float)primShape.ProfileEnd * 2.0e-5f; 588 }
361 float profileHollow = (float)primShape.ProfileHollow * 2.0e-5f; 589 else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
362 if (profileHollow > 0.95f) 590 { // half circle, prim is a sphere
363 profileHollow = 0.95f; 591 switch (iLOD)
364 592 {
365 int sides = 4; 593 case LevelOfDetail.High: sides = 24; break;
366 if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle) 594 case LevelOfDetail.Medium: sides = 12; break;
367 sides = 3; 595 case LevelOfDetail.Low: sides = 6; break;
368 else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Circle) 596 case LevelOfDetail.VeryLow: sides = 3; break;
369 sides = 24; 597 default: sides = 24; break;
370 else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) 598 }
371 { // half circle, prim is a sphere 599
372 sides = 24; 600 profileBegin = 0.5f * profileBegin + 0.5f;
373 601 profileEnd = 0.5f * profileEnd + 0.5f;
374 profileBegin = 0.5f * profileBegin + 0.5f; 602 }
375 profileEnd = 0.5f * profileEnd + 0.5f;
376 603
604 int hollowSides = sides;
605 if (primShape.HollowShape == HollowShape.Circle)
606 {
607 switch (iLOD)
608 {
609 case LevelOfDetail.High: hollowSides = 24; break;
610 case LevelOfDetail.Medium: hollowSides = 12; break;
611 case LevelOfDetail.Low: hollowSides = 6; break;
612 case LevelOfDetail.VeryLow: hollowSides = 3; break;
613 default: hollowSides = 24; break;
377 } 614 }
615 }
616 else if (primShape.HollowShape == HollowShape.Square)
617 hollowSides = 4;
618 else if (primShape.HollowShape == HollowShape.Triangle)
619 hollowSides = 3;
378 620
379 int hollowSides = sides; 621 primMesh = new PrimMesh(sides, profileBegin, profileEnd, profileHollow, hollowSides);
380 if (primShape.HollowShape == HollowShape.Circle)
381 hollowSides = 24;
382 else if (primShape.HollowShape == HollowShape.Square)
383 hollowSides = 4;
384 else if (primShape.HollowShape == HollowShape.Triangle)
385 hollowSides = 3;
386 622
387 primMesh = new PrimMesh(sides, profileBegin, profileEnd, profileHollow, hollowSides); 623 if (primMesh.errorMessage != null)
624 if (primMesh.errorMessage.Length > 0)
625 m_log.Error("[ERROR] " + primMesh.errorMessage);
388 626
389 if (primMesh.errorMessage != null) 627 primMesh.topShearX = pathShearX;
390 if (primMesh.errorMessage.Length > 0) 628 primMesh.topShearY = pathShearY;
391 m_log.Error("[ERROR] " + primMesh.errorMessage); 629 primMesh.pathCutBegin = pathBegin;
630 primMesh.pathCutEnd = pathEnd;
392 631
393 primMesh.topShearX = pathShearX; 632 if (primShape.PathCurve == (byte)Extrusion.Straight || primShape.PathCurve == (byte) Extrusion.Flexible)
394 primMesh.topShearY = pathShearY; 633 {
395 primMesh.pathCutBegin = pathBegin; 634 primMesh.twistBegin = primShape.PathTwistBegin * 18 / 10;
396 primMesh.pathCutEnd = pathEnd; 635 primMesh.twistEnd = primShape.PathTwist * 18 / 10;
636 primMesh.taperX = pathScaleX;
637 primMesh.taperY = pathScaleY;
397 638
398 if (primShape.PathCurve == (byte)Extrusion.Straight || primShape.PathCurve == (byte) Extrusion.Flexible) 639 if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f)
399 { 640 {
400 primMesh.twistBegin = primShape.PathTwistBegin * 18 / 10; 641 ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh);
401 primMesh.twistEnd = primShape.PathTwist * 18 / 10; 642 if (profileBegin < 0.0f) profileBegin = 0.0f;
402 primMesh.taperX = pathScaleX; 643 if (profileEnd > 1.0f) profileEnd = 1.0f;
403 primMesh.taperY = pathScaleY; 644 }
404
405 if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f)
406 {
407 ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh);
408 if (profileBegin < 0.0f) profileBegin = 0.0f;
409 if (profileEnd > 1.0f) profileEnd = 1.0f;
410 }
411#if SPAM 645#if SPAM
412 m_log.Debug("****** PrimMesh Parameters (Linear) ******\n" + primMesh.ParamsToDisplayString()); 646 m_log.Debug("****** PrimMesh Parameters (Linear) ******\n" + primMesh.ParamsToDisplayString());
413#endif 647#endif
414 try 648 try
415 { 649 {
416 primMesh.ExtrudeLinear(); 650 primMesh.ExtrudeLinear();
417 }
418 catch (Exception ex)
419 {
420 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
421 return null;
422 }
423 } 651 }
424 else 652 catch (Exception ex)
425 { 653 {
426 primMesh.holeSizeX = (200 - primShape.PathScaleX) * 0.01f; 654 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
427 primMesh.holeSizeY = (200 - primShape.PathScaleY) * 0.01f; 655 return false;
428 primMesh.radius = 0.01f * primShape.PathRadiusOffset; 656 }
429 primMesh.revolutions = 1.0f + 0.015f * primShape.PathRevolutions; 657 }
430 primMesh.skew = 0.01f * primShape.PathSkew; 658 else
431 primMesh.twistBegin = primShape.PathTwistBegin * 36 / 10; 659 {
432 primMesh.twistEnd = primShape.PathTwist * 36 / 10; 660 primMesh.holeSizeX = (200 - primShape.PathScaleX) * 0.01f;
433 primMesh.taperX = primShape.PathTaperX * 0.01f; 661 primMesh.holeSizeY = (200 - primShape.PathScaleY) * 0.01f;
434 primMesh.taperY = primShape.PathTaperY * 0.01f; 662 primMesh.radius = 0.01f * primShape.PathRadiusOffset;
435 663 primMesh.revolutions = 1.0f + 0.015f * primShape.PathRevolutions;
436 if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f) 664 primMesh.skew = 0.01f * primShape.PathSkew;
437 { 665 primMesh.twistBegin = primShape.PathTwistBegin * 36 / 10;
438 ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh); 666 primMesh.twistEnd = primShape.PathTwist * 36 / 10;
439 if (profileBegin < 0.0f) profileBegin = 0.0f; 667 primMesh.taperX = primShape.PathTaperX * 0.01f;
440 if (profileEnd > 1.0f) profileEnd = 1.0f; 668 primMesh.taperY = primShape.PathTaperY * 0.01f;
441 } 669
670 if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f)
671 {
672 ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh);
673 if (profileBegin < 0.0f) profileBegin = 0.0f;
674 if (profileEnd > 1.0f) profileEnd = 1.0f;
675 }
442#if SPAM 676#if SPAM
443 m_log.Debug("****** PrimMesh Parameters (Circular) ******\n" + primMesh.ParamsToDisplayString()); 677 m_log.Debug("****** PrimMesh Parameters (Circular) ******\n" + primMesh.ParamsToDisplayString());
444#endif 678#endif
445 try 679 try
446 { 680 {
447 primMesh.ExtrudeCircular(); 681 primMesh.ExtrudeCircular();
448 } 682 }
449 catch (Exception ex) 683 catch (Exception ex)
450 { 684 {
451 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh); 685 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
452 return null; 686 return false;
453 }
454 } 687 }
455
456 primMesh.DumpRaw(baseDir, primName, "primMesh");
457
458 primMesh.Scale(size.X, size.Y, size.Z);
459
460 coords = primMesh.coords;
461 faces = primMesh.faces;
462 } 688 }
463 689
464 // Remove the reference to any JPEG2000 sculpt data so it can be GCed 690 primMesh.DumpRaw(baseDir, primName, "primMesh");
465 primShape.SculptData = Utils.EmptyBytes;
466 691
467 int numCoords = coords.Count; 692 primMesh.Scale(size.X, size.Y, size.Z);
468 int numFaces = faces.Count;
469 693
470 // Create the list of vertices 694 coords = primMesh.coords;
471 List<Vertex> vertices = new List<Vertex>(); 695 faces = primMesh.faces;
472 for (int i = 0; i < numCoords; i++)
473 {
474 Coord c = coords[i];
475 vertices.Add(new Vertex(c.X, c.Y, c.Z));
476 }
477 696
478 Mesh mesh = new Mesh(); 697 return true;
479 // Add the corresponding triangles to the mesh
480 for (int i = 0; i < numFaces; i++)
481 {
482 Face f = faces[i];
483 mesh.Add(new Triangle(vertices[f.v1], vertices[f.v2], vertices[f.v3]));
484 }
485 return mesh;
486 } 698 }
487 699
488 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod) 700 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
@@ -492,13 +704,16 @@ namespace OpenSim.Region.Physics.Meshing
492 704
493 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical) 705 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
494 { 706 {
707#if SPAM
708 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName);
709#endif
710
495 Mesh mesh = null; 711 Mesh mesh = null;
496 ulong key = 0; 712 ulong key = 0;
497 713
498 // If this mesh has been created already, return it instead of creating another copy 714 // If this mesh has been created already, return it instead of creating another copy
499 // For large regions with 100k+ prims and hundreds of copies of each, this can save a GB or more of memory 715 // For large regions with 100k+ prims and hundreds of copies of each, this can save a GB or more of memory
500 716 key = primShape.GetMeshKey(size, lod);
501 key = GetMeshKey(primShape, size, lod);
502 if (m_uniqueMeshes.TryGetValue(key, out mesh)) 717 if (m_uniqueMeshes.TryGetValue(key, out mesh))
503 return mesh; 718 return mesh;
504 719
diff --git a/OpenSim/Region/Physics/Meshing/PrimMesher.cs b/OpenSim/Region/Physics/Meshing/PrimMesher.cs
index 2a213c3..53022ad 100644
--- a/OpenSim/Region/Physics/Meshing/PrimMesher.cs
+++ b/OpenSim/Region/Physics/Meshing/PrimMesher.cs
@@ -257,7 +257,6 @@ namespace PrimMesher
257 public int uv2; 257 public int uv2;
258 public int uv3; 258 public int uv3;
259 259
260
261 public Face(int v1, int v2, int v3) 260 public Face(int v1, int v2, int v3)
262 { 261 {
263 primFace = 0; 262 primFace = 0;
@@ -630,6 +629,9 @@ namespace PrimMesher
630 internal int numOuterVerts = 0; 629 internal int numOuterVerts = 0;
631 internal int numHollowVerts = 0; 630 internal int numHollowVerts = 0;
632 631
632 internal int outerFaceNumber = -1;
633 internal int hollowFaceNumber = -1;
634
633 internal bool calcVertexNormals = false; 635 internal bool calcVertexNormals = false;
634 internal int bottomFaceNumber = 0; 636 internal int bottomFaceNumber = 0;
635 internal int numPrimFaces = 0; 637 internal int numPrimFaces = 0;
@@ -827,15 +829,16 @@ namespace PrimMesher
827 829
828 if (createFaces) 830 if (createFaces)
829 { 831 {
830 int numOuterVerts = this.coords.Count; 832 //int numOuterVerts = this.coords.Count;
831 int numHollowVerts = hollowCoords.Count; 833 //numOuterVerts = this.coords.Count;
832 int numTotalVerts = numOuterVerts + numHollowVerts; 834 //int numHollowVerts = hollowCoords.Count;
835 int numTotalVerts = this.numOuterVerts + this.numHollowVerts;
833 836
834 if (numOuterVerts == numHollowVerts) 837 if (this.numOuterVerts == this.numHollowVerts)
835 { 838 {
836 Face newFace = new Face(); 839 Face newFace = new Face();
837 840
838 for (int coordIndex = 0; coordIndex < numOuterVerts - 1; coordIndex++) 841 for (int coordIndex = 0; coordIndex < this.numOuterVerts - 1; coordIndex++)
839 { 842 {
840 newFace.v1 = coordIndex; 843 newFace.v1 = coordIndex;
841 newFace.v2 = coordIndex + 1; 844 newFace.v2 = coordIndex + 1;
@@ -850,12 +853,12 @@ namespace PrimMesher
850 } 853 }
851 else 854 else
852 { 855 {
853 if (numOuterVerts < numHollowVerts) 856 if (this.numOuterVerts < this.numHollowVerts)
854 { 857 {
855 Face newFace = new Face(); 858 Face newFace = new Face();
856 int j = 0; // j is the index for outer vertices 859 int j = 0; // j is the index for outer vertices
857 int maxJ = numOuterVerts - 1; 860 int maxJ = this.numOuterVerts - 1;
858 for (int i = 0; i < numHollowVerts; i++) // i is the index for inner vertices 861 for (int i = 0; i < this.numHollowVerts; i++) // i is the index for inner vertices
859 { 862 {
860 if (j < maxJ) 863 if (j < maxJ)
861 if (angles.angles[j + 1].angle - hollowAngles.angles[i].angle < hollowAngles.angles[i].angle - angles.angles[j].angle + 0.000001f) 864 if (angles.angles[j + 1].angle - hollowAngles.angles[i].angle < hollowAngles.angles[i].angle - angles.angles[j].angle + 0.000001f)
@@ -879,8 +882,8 @@ namespace PrimMesher
879 { 882 {
880 Face newFace = new Face(); 883 Face newFace = new Face();
881 int j = 0; // j is the index for inner vertices 884 int j = 0; // j is the index for inner vertices
882 int maxJ = numHollowVerts - 1; 885 int maxJ = this.numHollowVerts - 1;
883 for (int i = 0; i < numOuterVerts; i++) 886 for (int i = 0; i < this.numOuterVerts; i++)
884 { 887 {
885 if (j < maxJ) 888 if (j < maxJ)
886 if (hollowAngles.angles[j + 1].angle - angles.angles[i].angle < angles.angles[i].angle - hollowAngles.angles[j].angle + 0.000001f) 889 if (hollowAngles.angles[j + 1].angle - angles.angles[i].angle < angles.angles[i].angle - hollowAngles.angles[j].angle + 0.000001f)
@@ -935,10 +938,10 @@ namespace PrimMesher
935 938
936 if (calcVertexNormals && hasProfileCut) 939 if (calcVertexNormals && hasProfileCut)
937 { 940 {
941 int lastOuterVertIndex = this.numOuterVerts - 1;
942
938 if (hasHollow) 943 if (hasHollow)
939 { 944 {
940 int lastOuterVertIndex = this.numOuterVerts - 1;
941
942 this.cut1CoordIndices.Add(0); 945 this.cut1CoordIndices.Add(0);
943 this.cut1CoordIndices.Add(this.coords.Count - 1); 946 this.cut1CoordIndices.Add(this.coords.Count - 1);
944 947
@@ -954,6 +957,12 @@ namespace PrimMesher
954 957
955 else 958 else
956 { 959 {
960 this.cut1CoordIndices.Add(0);
961 this.cut1CoordIndices.Add(1);
962
963 this.cut2CoordIndices.Add(lastOuterVertIndex);
964 this.cut2CoordIndices.Add(0);
965
957 this.cutNormal1.X = this.vertexNormals[1].Y; 966 this.cutNormal1.X = this.vertexNormals[1].Y;
958 this.cutNormal1.Y = -this.vertexNormals[1].X; 967 this.cutNormal1.Y = -this.vertexNormals[1].X;
959 968
@@ -978,11 +987,14 @@ namespace PrimMesher
978 // I know it's ugly but so is the whole concept of prim face numbers 987 // I know it's ugly but so is the whole concept of prim face numbers
979 988
980 int faceNum = 1; // start with outer faces 989 int faceNum = 1; // start with outer faces
990 this.outerFaceNumber = faceNum;
991
981 int startVert = hasProfileCut && !hasHollow ? 1 : 0; 992 int startVert = hasProfileCut && !hasHollow ? 1 : 0;
982 if (startVert > 0) 993 if (startVert > 0)
983 this.faceNumbers.Add(-1); 994 this.faceNumbers.Add(-1);
984 for (int i = 0; i < numOuterVerts - 1; i++) 995 for (int i = 0; i < this.numOuterVerts - 1; i++)
985 this.faceNumbers.Add(sides < 5 ? faceNum++ : faceNum); 996 //this.faceNumbers.Add(sides < 5 ? faceNum++ : faceNum);
997 this.faceNumbers.Add(sides < 5 && i < sides ? faceNum++ : faceNum);
986 998
987 //if (!hasHollow && !hasProfileCut) 999 //if (!hasHollow && !hasProfileCut)
988 // this.bottomFaceNumber = faceNum++; 1000 // this.bottomFaceNumber = faceNum++;
@@ -992,12 +1004,15 @@ namespace PrimMesher
992 if (sides > 4 && (hasHollow || hasProfileCut)) 1004 if (sides > 4 && (hasHollow || hasProfileCut))
993 faceNum++; 1005 faceNum++;
994 1006
1007 if (sides < 5 && (hasHollow || hasProfileCut) && this.numOuterVerts < sides)
1008 faceNum++;
1009
995 if (hasHollow) 1010 if (hasHollow)
996 { 1011 {
997 for (int i = 0; i < numHollowVerts; i++) 1012 for (int i = 0; i < this.numHollowVerts; i++)
998 this.faceNumbers.Add(faceNum); 1013 this.faceNumbers.Add(faceNum);
999 1014
1000 faceNum++; 1015 this.hollowFaceNumber = faceNum++;
1001 } 1016 }
1002 //if (hasProfileCut || hasHollow) 1017 //if (hasProfileCut || hasHollow)
1003 // this.bottomFaceNumber = faceNum++; 1018 // this.bottomFaceNumber = faceNum++;
@@ -1005,11 +1020,11 @@ namespace PrimMesher
1005 1020
1006 if (hasHollow && hasProfileCut) 1021 if (hasHollow && hasProfileCut)
1007 this.faceNumbers.Add(faceNum++); 1022 this.faceNumbers.Add(faceNum++);
1023
1008 for (int i = 0; i < this.faceNumbers.Count; i++) 1024 for (int i = 0; i < this.faceNumbers.Count; i++)
1009 if (this.faceNumbers[i] == -1) 1025 if (this.faceNumbers[i] == -1)
1010 this.faceNumbers[i] = faceNum++; 1026 this.faceNumbers[i] = faceNum++;
1011 1027
1012
1013 this.numPrimFaces = faceNum; 1028 this.numPrimFaces = faceNum;
1014 } 1029 }
1015 1030
@@ -1019,7 +1034,7 @@ namespace PrimMesher
1019 { 1034 {
1020 this.faceUVs = new List<UVCoord>(); 1035 this.faceUVs = new List<UVCoord>();
1021 foreach (Coord c in this.coords) 1036 foreach (Coord c in this.coords)
1022 this.faceUVs.Add(new UVCoord(1.0f - (0.5f + c.X), 1.0f - (0.5f - c.Y))); 1037 this.faceUVs.Add(new UVCoord(0.5f + c.X, 0.5f - c.Y));
1023 } 1038 }
1024 1039
1025 internal Profile Copy() 1040 internal Profile Copy()
@@ -1348,7 +1363,6 @@ namespace PrimMesher
1348 float stepSize = twoPi / this.stepsPerRevolution; 1363 float stepSize = twoPi / this.stepsPerRevolution;
1349 1364
1350 int step = (int)(startAngle / stepSize); 1365 int step = (int)(startAngle / stepSize);
1351// int firstStep = step;
1352 float angle = startAngle; 1366 float angle = startAngle;
1353 1367
1354 bool done = false; 1368 bool done = false;
@@ -1455,11 +1469,15 @@ namespace PrimMesher
1455 public float revolutions = 1.0f; 1469 public float revolutions = 1.0f;
1456 public int stepsPerRevolution = 24; 1470 public int stepsPerRevolution = 24;
1457 1471
1472 private int profileOuterFaceNumber = -1;
1473 private int profileHollowFaceNumber = -1;
1474
1458 private bool hasProfileCut = false; 1475 private bool hasProfileCut = false;
1459 private bool hasHollow = false; 1476 private bool hasHollow = false;
1460 public bool calcVertexNormals = false; 1477 public bool calcVertexNormals = false;
1461 private bool normalsProcessed = false; 1478 private bool normalsProcessed = false;
1462 public bool viewerMode = false; 1479 public bool viewerMode = false;
1480 public bool sphereMode = false;
1463 1481
1464 public int numPrimFaces = 0; 1482 public int numPrimFaces = 0;
1465 1483
@@ -1491,10 +1509,35 @@ namespace PrimMesher
1491 s += "\nradius...............: " + this.radius.ToString(); 1509 s += "\nradius...............: " + this.radius.ToString();
1492 s += "\nrevolutions..........: " + this.revolutions.ToString(); 1510 s += "\nrevolutions..........: " + this.revolutions.ToString();
1493 s += "\nstepsPerRevolution...: " + this.stepsPerRevolution.ToString(); 1511 s += "\nstepsPerRevolution...: " + this.stepsPerRevolution.ToString();
1512 s += "\nsphereMode...........: " + this.sphereMode.ToString();
1513 s += "\nhasProfileCut........: " + this.hasProfileCut.ToString();
1514 s += "\nhasHollow............: " + this.hasHollow.ToString();
1515 s += "\nviewerMode...........: " + this.viewerMode.ToString();
1494 1516
1495 return s; 1517 return s;
1496 } 1518 }
1497 1519
1520 public int ProfileOuterFaceNumber
1521 {
1522 get { return profileOuterFaceNumber; }
1523 }
1524
1525 public int ProfileHollowFaceNumber
1526 {
1527 get { return profileHollowFaceNumber; }
1528 }
1529
1530 public bool HasProfileCut
1531 {
1532 get { return hasProfileCut; }
1533 }
1534
1535 public bool HasHollow
1536 {
1537 get { return hasHollow; }
1538 }
1539
1540
1498 /// <summary> 1541 /// <summary>
1499 /// Constructs a PrimMesh object and creates the profile for extrusion. 1542 /// Constructs a PrimMesh object and creates the profile for extrusion.
1500 /// </summary> 1543 /// </summary>
@@ -1531,8 +1574,12 @@ namespace PrimMesher
1531 if (hollow < 0.0f) 1574 if (hollow < 0.0f)
1532 this.hollow = 0.0f; 1575 this.hollow = 0.0f;
1533 1576
1534 this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f); 1577 //if (sphereMode)
1535 this.hasHollow = (this.hollow > 0.001f); 1578 // this.hasProfileCut = this.profileEnd - this.profileStart < 0.4999f;
1579 //else
1580 // //this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f);
1581 // this.hasProfileCut = this.profileEnd - this.profileStart < 0.9999f;
1582 //this.hasHollow = (this.hollow > 0.001f);
1536 } 1583 }
1537 1584
1538 /// <summary> 1585 /// <summary>
@@ -1540,6 +1587,8 @@ namespace PrimMesher
1540 /// </summary> 1587 /// </summary>
1541 public void Extrude(PathType pathType) 1588 public void Extrude(PathType pathType)
1542 { 1589 {
1590 bool needEndFaces = false;
1591
1543 this.coords = new List<Coord>(); 1592 this.coords = new List<Coord>();
1544 this.faces = new List<Face>(); 1593 this.faces = new List<Face>();
1545 1594
@@ -1565,6 +1614,12 @@ namespace PrimMesher
1565 steps = (int)(steps * 4.5 * length); 1614 steps = (int)(steps * 4.5 * length);
1566 } 1615 }
1567 1616
1617 if (sphereMode)
1618 this.hasProfileCut = this.profileEnd - this.profileStart < 0.4999f;
1619 else
1620 //this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f);
1621 this.hasProfileCut = this.profileEnd - this.profileStart < 0.9999f;
1622 this.hasHollow = (this.hollow > 0.001f);
1568 1623
1569 float twistBegin = this.twistBegin / 360.0f * twoPi; 1624 float twistBegin = this.twistBegin / 360.0f * twoPi;
1570 float twistEnd = this.twistEnd / 360.0f * twoPi; 1625 float twistEnd = this.twistEnd / 360.0f * twoPi;
@@ -1634,6 +1689,32 @@ namespace PrimMesher
1634 1689
1635 this.numPrimFaces = profile.numPrimFaces; 1690 this.numPrimFaces = profile.numPrimFaces;
1636 1691
1692 //profileOuterFaceNumber = profile.faceNumbers[0];
1693 //if (!needEndFaces)
1694 // profileOuterFaceNumber--;
1695 //profileOuterFaceNumber = needEndFaces ? 1 : 0;
1696
1697
1698 //if (hasHollow)
1699 //{
1700 // if (needEndFaces)
1701 // profileHollowFaceNumber = profile.faceNumbers[profile.numOuterVerts + 1];
1702 // else
1703 // profileHollowFaceNumber = profile.faceNumbers[profile.numOuterVerts] - 1;
1704 //}
1705
1706
1707 profileOuterFaceNumber = profile.outerFaceNumber;
1708 if (!needEndFaces)
1709 profileOuterFaceNumber--;
1710
1711 if (hasHollow)
1712 {
1713 profileHollowFaceNumber = profile.hollowFaceNumber;
1714 if (!needEndFaces)
1715 profileHollowFaceNumber--;
1716 }
1717
1637 int cut1Vert = -1; 1718 int cut1Vert = -1;
1638 int cut2Vert = -1; 1719 int cut2Vert = -1;
1639 if (hasProfileCut) 1720 if (hasProfileCut)
@@ -1673,7 +1754,7 @@ namespace PrimMesher
1673 1754
1674 path.Create(pathType, steps); 1755 path.Create(pathType, steps);
1675 1756
1676 bool needEndFaces = false; 1757
1677 if (pathType == PathType.Circular) 1758 if (pathType == PathType.Circular)
1678 { 1759 {
1679 needEndFaces = false; 1760 needEndFaces = false;
@@ -1738,7 +1819,6 @@ namespace PrimMesher
1738 // append this layer 1819 // append this layer
1739 1820
1740 int coordsLen = this.coords.Count; 1821 int coordsLen = this.coords.Count;
1741// int lastCoordsLen = coordsLen;
1742 newLayer.AddValue2FaceVertexIndices(coordsLen); 1822 newLayer.AddValue2FaceVertexIndices(coordsLen);
1743 1823
1744 this.coords.AddRange(newLayer.coords); 1824 this.coords.AddRange(newLayer.coords);
@@ -1762,7 +1842,7 @@ namespace PrimMesher
1762 int startVert = coordsLen + 1; 1842 int startVert = coordsLen + 1;
1763 int endVert = this.coords.Count; 1843 int endVert = this.coords.Count;
1764 1844
1765 if (sides < 5 || this.hasProfileCut || hollow > 0.0f) 1845 if (sides < 5 || this.hasProfileCut || this.hasHollow)
1766 startVert--; 1846 startVert--;
1767 1847
1768 for (int i = startVert; i < endVert; i++) 1848 for (int i = startVert; i < endVert; i++)
@@ -1814,11 +1894,13 @@ namespace PrimMesher
1814 u1 -= (int)u1; 1894 u1 -= (int)u1;
1815 if (u2 < 0.1f) 1895 if (u2 < 0.1f)
1816 u2 = 1.0f; 1896 u2 = 1.0f;
1897 //this.profileOuterFaceNumber = primFaceNum;
1817 } 1898 }
1818 else if (whichVert > profile.coords.Count - profile.numHollowVerts - 1) 1899 else if (whichVert > profile.coords.Count - profile.numHollowVerts - 1)
1819 { 1900 {
1820 u1 *= 2.0f; 1901 u1 *= 2.0f;
1821 u2 *= 2.0f; 1902 u2 *= 2.0f;
1903 //this.profileHollowFaceNumber = primFaceNum;
1822 } 1904 }
1823 } 1905 }
1824 1906
diff --git a/OpenSim/Region/Physics/Meshing/SculptMap.cs b/OpenSim/Region/Physics/Meshing/SculptMap.cs
new file mode 100644
index 0000000..740424e
--- /dev/null
+++ b/OpenSim/Region/Physics/Meshing/SculptMap.cs
@@ -0,0 +1,183 @@
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
28// to build without references to System.Drawing, comment this out
29#define SYSTEM_DRAWING
30
31using System;
32using System.Collections.Generic;
33using System.Text;
34
35#if SYSTEM_DRAWING
36using System.Drawing;
37using System.Drawing.Imaging;
38
39namespace PrimMesher
40{
41 public class SculptMap
42 {
43 public int width;
44 public int height;
45 public byte[] redBytes;
46 public byte[] greenBytes;
47 public byte[] blueBytes;
48
49 public SculptMap()
50 {
51 }
52
53 public SculptMap(Bitmap bm, int lod)
54 {
55 int bmW = bm.Width;
56 int bmH = bm.Height;
57
58 if (bmW == 0 || bmH == 0)
59 throw new Exception("SculptMap: bitmap has no data");
60
61 int numLodPixels = lod * 2 * lod * 2; // (32 * 2)^2 = 64^2 pixels for default sculpt map image
62
63 bool needsScaling = false;
64
65 bool smallMap = bmW * bmH <= lod * lod;
66
67 width = bmW;
68 height = bmH;
69 while (width * height > numLodPixels)
70 {
71 width >>= 1;
72 height >>= 1;
73 needsScaling = true;
74 }
75
76
77
78 try
79 {
80 if (needsScaling)
81 bm = ScaleImage(bm, width, height,
82 System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor);
83 }
84
85 catch (Exception e)
86 {
87 throw new Exception("Exception in ScaleImage(): e: " + e.ToString());
88 }
89
90 if (width * height > lod * lod)
91 {
92 width >>= 1;
93 height >>= 1;
94 }
95
96 int numBytes = (width + 1) * (height + 1);
97 redBytes = new byte[numBytes];
98 greenBytes = new byte[numBytes];
99 blueBytes = new byte[numBytes];
100
101 int byteNdx = 0;
102
103 try
104 {
105 for (int y = 0; y <= height; y++)
106 {
107 for (int x = 0; x <= width; x++)
108 {
109 Color c;
110
111 if (smallMap)
112 c = bm.GetPixel(x < width ? x : x - 1,
113 y < height ? y : y - 1);
114 else
115 c = bm.GetPixel(x < width ? x * 2 : x * 2 - 1,
116 y < height ? y * 2 : y * 2 - 1);
117
118 redBytes[byteNdx] = c.R;
119 greenBytes[byteNdx] = c.G;
120 blueBytes[byteNdx] = c.B;
121
122 ++byteNdx;
123 }
124 }
125 }
126 catch (Exception e)
127 {
128 throw new Exception("Caught exception processing byte arrays in SculptMap(): e: " + e.ToString());
129 }
130
131 width++;
132 height++;
133 }
134
135 public List<List<Coord>> ToRows(bool mirror)
136 {
137 int numRows = height;
138 int numCols = width;
139
140 List<List<Coord>> rows = new List<List<Coord>>(numRows);
141
142 float pixScale = 1.0f / 255;
143
144 int rowNdx, colNdx;
145 int smNdx = 0;
146
147 for (rowNdx = 0; rowNdx < numRows; rowNdx++)
148 {
149 List<Coord> row = new List<Coord>(numCols);
150 for (colNdx = 0; colNdx < numCols; colNdx++)
151 {
152 if (mirror)
153 row.Add(new Coord(-(redBytes[smNdx] * pixScale - 0.5f), (greenBytes[smNdx] * pixScale - 0.5f), blueBytes[smNdx] * pixScale - 0.5f));
154 else
155 row.Add(new Coord(redBytes[smNdx] * pixScale - 0.5f, greenBytes[smNdx] * pixScale - 0.5f, blueBytes[smNdx] * pixScale - 0.5f));
156
157 ++smNdx;
158 }
159 rows.Add(row);
160 }
161 return rows;
162 }
163
164 private Bitmap ScaleImage(Bitmap srcImage, int destWidth, int destHeight,
165 System.Drawing.Drawing2D.InterpolationMode interpMode)
166 {
167 Bitmap scaledImage = new Bitmap(srcImage, destWidth, destHeight);
168 scaledImage.SetResolution(96.0f, 96.0f);
169
170 Graphics grPhoto = Graphics.FromImage(scaledImage);
171 grPhoto.InterpolationMode = interpMode;
172
173 grPhoto.DrawImage(srcImage,
174 new Rectangle(0, 0, destWidth, destHeight),
175 new Rectangle(0, 0, srcImage.Width, srcImage.Height),
176 GraphicsUnit.Pixel);
177
178 grPhoto.Dispose();
179 return scaledImage;
180 }
181 }
182}
183#endif
diff --git a/OpenSim/Region/Physics/Meshing/SculptMesh.cs b/OpenSim/Region/Physics/Meshing/SculptMesh.cs
index 4dc6e2e..4a7f3ad 100644
--- a/OpenSim/Region/Physics/Meshing/SculptMesh.cs
+++ b/OpenSim/Region/Physics/Meshing/SculptMesh.cs
@@ -53,43 +53,6 @@ namespace PrimMesher
53 public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 }; 53 public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 };
54 54
55#if SYSTEM_DRAWING 55#if SYSTEM_DRAWING
56 // private Bitmap ScaleImage(Bitmap srcImage, float scale)
57 // {
58 // int sourceWidth = srcImage.Width;
59 // int sourceHeight = srcImage.Height;
60 // int sourceX = 0;
61 // int sourceY = 0;
62
63 // int destX = 0;
64 // int destY = 0;
65 // int destWidth = (int)(srcImage.Width * scale);
66 // int destHeight = (int)(srcImage.Height * scale);
67
68 // if (srcImage.PixelFormat == PixelFormat.Format32bppArgb)
69 // for (int y = 0; y < srcImage.Height; y++)
70 // for (int x = 0; x < srcImage.Width; x++)
71 // {
72 // Color c = srcImage.GetPixel(x, y);
73 // srcImage.SetPixel(x, y, Color.FromArgb(255, c.R, c.G, c.B));
74 // }
75
76 // Bitmap scaledImage = new Bitmap(destWidth, destHeight,
77 // PixelFormat.Format24bppRgb);
78
79 // scaledImage.SetResolution(96.0f, 96.0f);
80
81 // Graphics grPhoto = Graphics.FromImage(scaledImage);
82 // grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low;
83
84 // grPhoto.DrawImage(srcImage,
85 // new Rectangle(destX, destY, destWidth, destHeight),
86 // new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),
87 // GraphicsUnit.Pixel);
88
89 // grPhoto.Dispose();
90 // return scaledImage;
91 // }
92
93 56
94 public SculptMesh SculptMeshFromFile(string fileName, SculptType sculptType, int lod, bool viewerMode) 57 public SculptMesh SculptMeshFromFile(string fileName, SculptType sculptType, int lod, bool viewerMode)
95 { 58 {
@@ -99,6 +62,7 @@ namespace PrimMesher
99 return sculptMesh; 62 return sculptMesh;
100 } 63 }
101 64
65
102 public SculptMesh(string fileName, int sculptType, int lod, int viewerMode, int mirror, int invert) 66 public SculptMesh(string fileName, int sculptType, int lod, int viewerMode, int mirror, int invert)
103 { 67 {
104 Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); 68 Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName);
@@ -268,6 +232,11 @@ namespace PrimMesher
268 for (imageY = imageYStart; imageY < imageYEnd; imageY++) 232 for (imageY = imageYStart; imageY < imageYEnd; imageY++)
269 { 233 {
270 Color c = bitmap.GetPixel(imageX, imageY); 234 Color c = bitmap.GetPixel(imageX, imageY);
235 if (c.A != 255)
236 {
237 bitmap.SetPixel(imageX, imageY, Color.FromArgb(255, c.R, c.G, c.B));
238 c = bitmap.GetPixel(imageX, imageY);
239 }
271 rSum += c.R; 240 rSum += c.R;
272 gSum += c.G; 241 gSum += c.G;
273 bSum += c.B; 242 bSum += c.B;
@@ -284,30 +253,53 @@ namespace PrimMesher
284 return rows; 253 return rows;
285 } 254 }
286 255
287 256 private List<List<Coord>> bitmap2CoordsSampled(Bitmap bitmap, int scale, bool mirror)
288 void _SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert)
289 { 257 {
290 coords = new List<Coord>(); 258 int numRows = bitmap.Height / scale;
291 faces = new List<Face>(); 259 int numCols = bitmap.Width / scale;
292 normals = new List<Coord>(); 260 List<List<Coord>> rows = new List<List<Coord>>(numRows);
293 uvs = new List<UVCoord>();
294 261
295 sculptType = (SculptType)(((int)sculptType) & 0x07); 262 float pixScale = 1.0f / 256.0f;
296 263
297 if (mirror) 264 int imageX, imageY = 0;
298 if (sculptType == SculptType.plane) 265
299 invert = !invert; 266 int rowNdx, colNdx;
267
268 for (rowNdx = 0; rowNdx <= numRows; rowNdx++)
269 {
270 List<Coord> row = new List<Coord>(numCols);
271 imageY = rowNdx * scale;
272 if (rowNdx == numRows) imageY--;
273 for (colNdx = 0; colNdx <= numCols; colNdx++)
274 {
275 imageX = colNdx * scale;
276 if (colNdx == numCols) imageX--;
300 277
301 float sourceScaleFactor = (float)(lod) / (float)Math.Sqrt(sculptBitmap.Width * sculptBitmap.Height); 278 Color c = bitmap.GetPixel(imageX, imageY);
279 if (c.A != 255)
280 {
281 bitmap.SetPixel(imageX, imageY, Color.FromArgb(255, c.R, c.G, c.B));
282 c = bitmap.GetPixel(imageX, imageY);
283 }
302 284
303 int scale = (int)(1.0f / sourceScaleFactor); 285 if (mirror)
304 if (scale < 1) scale = 1; 286 row.Add(new Coord(-(c.R * pixScale - 0.5f), c.G * pixScale - 0.5f, c.B * pixScale - 0.5f));
287 else
288 row.Add(new Coord(c.R * pixScale - 0.5f, c.G * pixScale - 0.5f, c.B * pixScale - 0.5f));
305 289
306 _SculptMesh(bitmap2Coords(sculptBitmap, scale, mirror), sculptType, viewerMode, mirror, invert); 290 }
291 rows.Add(row);
292 }
293 return rows;
307 } 294 }
308#endif
309 295
310 296
297 void _SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert)
298 {
299 _SculptMesh(new SculptMap(sculptBitmap, lod).ToRows(mirror), sculptType, viewerMode, mirror, invert);
300 }
301#endif
302
311 void _SculptMesh(List<List<Coord>> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) 303 void _SculptMesh(List<List<Coord>> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert)
312 { 304 {
313 coords = new List<Coord>(); 305 coords = new List<Coord>();
@@ -318,8 +310,7 @@ namespace PrimMesher
318 sculptType = (SculptType)(((int)sculptType) & 0x07); 310 sculptType = (SculptType)(((int)sculptType) & 0x07);
319 311
320 if (mirror) 312 if (mirror)
321 if (sculptType == SculptType.plane) 313 invert = !invert;
322 invert = !invert;
323 314
324 viewerFaces = new List<ViewerFace>(); 315 viewerFaces = new List<ViewerFace>();
325 316
@@ -331,8 +322,18 @@ namespace PrimMesher
331 322
332 if (sculptType != SculptType.plane) 323 if (sculptType != SculptType.plane)
333 { 324 {
334 for (int rowNdx = 0; rowNdx < rows.Count; rowNdx++) 325 if (rows.Count % 2 == 0)
335 rows[rowNdx].Add(rows[rowNdx][0]); 326 {
327 for (int rowNdx = 0; rowNdx < rows.Count; rowNdx++)
328 rows[rowNdx].Add(rows[rowNdx][0]);
329 }
330 else
331 {
332 int lastIndex = rows[0].Count - 1;
333
334 for (int i = 0; i < rows.Count; i++)
335 rows[i][0] = rows[i][lastIndex];
336 }
336 } 337 }
337 338
338 Coord topPole = rows[0][width / 2]; 339 Coord topPole = rows[0][width / 2];
@@ -340,23 +341,41 @@ namespace PrimMesher
340 341
341 if (sculptType == SculptType.sphere) 342 if (sculptType == SculptType.sphere)
342 { 343 {
343 int count = rows[0].Count; 344 if (rows.Count % 2 == 0)
344 List<Coord> topPoleRow = new List<Coord>(count); 345 {
345 List<Coord> bottomPoleRow = new List<Coord>(count); 346 int count = rows[0].Count;
347 List<Coord> topPoleRow = new List<Coord>(count);
348 List<Coord> bottomPoleRow = new List<Coord>(count);
346 349
347 for (int i = 0; i < count; i++) 350 for (int i = 0; i < count; i++)
351 {
352 topPoleRow.Add(topPole);
353 bottomPoleRow.Add(bottomPole);
354 }
355 rows.Insert(0, topPoleRow);
356 rows.Add(bottomPoleRow);
357 }
358 else
348 { 359 {
349 topPoleRow.Add(topPole); 360 int count = rows[0].Count;
350 bottomPoleRow.Add(bottomPole); 361
362 List<Coord> topPoleRow = rows[0];
363 List<Coord> bottomPoleRow = rows[rows.Count - 1];
364
365 for (int i = 0; i < count; i++)
366 {
367 topPoleRow[i] = topPole;
368 bottomPoleRow[i] = bottomPole;
369 }
351 } 370 }
352 rows.Insert(0, topPoleRow);
353 rows.Add(bottomPoleRow);
354 } 371 }
355 else if (sculptType == SculptType.torus) 372
373 if (sculptType == SculptType.torus)
356 rows.Add(rows[0]); 374 rows.Add(rows[0]);
357 375
358 int coordsDown = rows.Count; 376 int coordsDown = rows.Count;
359 int coordsAcross = rows[0].Count; 377 int coordsAcross = rows[0].Count;
378// int lastColumn = coordsAcross - 1;
360 379
361 float widthUnit = 1.0f / (coordsAcross - 1); 380 float widthUnit = 1.0f / (coordsAcross - 1);
362 float heightUnit = 1.0f / (coordsDown - 1); 381 float heightUnit = 1.0f / (coordsDown - 1);