aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/PhysicsModules/Meshing/Meshmerizer/Meshmerizer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/PhysicsModules/Meshing/Meshmerizer/Meshmerizer.cs')
-rw-r--r--OpenSim/Region/PhysicsModules/Meshing/Meshmerizer/Meshmerizer.cs1010
1 files changed, 1010 insertions, 0 deletions
diff --git a/OpenSim/Region/PhysicsModules/Meshing/Meshmerizer/Meshmerizer.cs b/OpenSim/Region/PhysicsModules/Meshing/Meshmerizer/Meshmerizer.cs
new file mode 100644
index 0000000..4d25bf3
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/Meshing/Meshmerizer/Meshmerizer.cs
@@ -0,0 +1,1010 @@
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 System.Reflection;
32using System.IO;
33using OpenSim.Framework;
34using OpenSim.Region.Framework.Scenes;
35using OpenSim.Region.Framework.Interfaces;
36using OpenSim.Region.PhysicsModules.SharedBase;
37using OpenMetaverse;
38using OpenMetaverse.StructuredData;
39using System.Drawing;
40using System.Drawing.Imaging;
41using System.IO.Compression;
42using PrimMesher;
43using log4net;
44using Nini.Config;
45using Mono.Addins;
46
47namespace OpenSim.Region.PhysicsModules.Meshing
48{
49 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "Meshmerizer")]
50 public class Meshmerizer : IMesher, INonSharedRegionModule
51 {
52 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
53 private static string LogHeader = "[MESH]";
54
55 // Setting baseDir to a path will enable the dumping of raw files
56 // raw files can be imported by blender so a visual inspection of the results can be done
57#if SPAM
58 const string baseDir = "rawFiles";
59#else
60 private const string baseDir = null; //"rawFiles";
61#endif
62 private bool m_Enabled = false;
63
64 // If 'true', lots of DEBUG logging of asset parsing details
65 private bool debugDetail = false;
66
67 private bool cacheSculptMaps = true;
68 private string decodedSculptMapPath = null;
69 private bool useMeshiesPhysicsMesh = false;
70
71 private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh
72
73 private List<List<Vector3>> mConvexHulls = null;
74 private List<Vector3> mBoundingHull = null;
75
76 // Mesh cache. Static so it can be shared across instances of this class
77 private static Dictionary<ulong, Mesh> m_uniqueMeshes = new Dictionary<ulong, Mesh>();
78
79 #region INonSharedRegionModule
80 public string Name
81 {
82 get { return "Meshmerizer"; }
83 }
84
85 public Type ReplaceableInterface
86 {
87 get { return null; }
88 }
89
90 public void Initialise(IConfigSource source)
91 {
92 IConfig config = source.Configs["Startup"];
93 if (config != null)
94 {
95 string mesher = config.GetString("meshing", string.Empty);
96 if (mesher == Name)
97 {
98 m_Enabled = true;
99
100 IConfig mesh_config = source.Configs["Mesh"];
101
102 decodedSculptMapPath = config.GetString("DecodedSculptMapPath", "j2kDecodeCache");
103 cacheSculptMaps = config.GetBoolean("CacheSculptMaps", cacheSculptMaps);
104 if (mesh_config != null)
105 {
106 useMeshiesPhysicsMesh = mesh_config.GetBoolean("UseMeshiesPhysicsMesh", useMeshiesPhysicsMesh);
107 debugDetail = mesh_config.GetBoolean("LogMeshDetails", debugDetail);
108 }
109
110 try
111 {
112 if (!Directory.Exists(decodedSculptMapPath))
113 Directory.CreateDirectory(decodedSculptMapPath);
114 }
115 catch (Exception e)
116 {
117 m_log.WarnFormat("[SCULPT]: Unable to create {0} directory: ", decodedSculptMapPath, e.Message);
118 }
119
120 }
121 }
122 }
123
124 public void Close()
125 {
126 }
127
128 public void AddRegion(Scene scene)
129 {
130 if (!m_Enabled)
131 return;
132
133 scene.RegisterModuleInterface<IMesher>(this);
134 }
135
136 public void RemoveRegion(Scene scene)
137 {
138 if (!m_Enabled)
139 return;
140
141 scene.UnregisterModuleInterface<IMesher>(this);
142 }
143
144 public void RegionLoaded(Scene scene)
145 {
146 if (!m_Enabled)
147 return;
148 }
149 #endregion
150
151
152 /// <summary>
153 /// creates a simple box mesh of the specified size. This mesh is of very low vertex count and may
154 /// be useful as a backup proxy when level of detail is not needed or when more complex meshes fail
155 /// for some reason
156 /// </summary>
157 /// <param name="minX"></param>
158 /// <param name="maxX"></param>
159 /// <param name="minY"></param>
160 /// <param name="maxY"></param>
161 /// <param name="minZ"></param>
162 /// <param name="maxZ"></param>
163 /// <returns></returns>
164 private static Mesh CreateSimpleBoxMesh(float minX, float maxX, float minY, float maxY, float minZ, float maxZ)
165 {
166 Mesh box = new Mesh();
167 List<Vertex> vertices = new List<Vertex>();
168 // bottom
169
170 vertices.Add(new Vertex(minX, maxY, minZ));
171 vertices.Add(new Vertex(maxX, maxY, minZ));
172 vertices.Add(new Vertex(maxX, minY, minZ));
173 vertices.Add(new Vertex(minX, minY, minZ));
174
175 box.Add(new Triangle(vertices[0], vertices[1], vertices[2]));
176 box.Add(new Triangle(vertices[0], vertices[2], vertices[3]));
177
178 // top
179
180 vertices.Add(new Vertex(maxX, maxY, maxZ));
181 vertices.Add(new Vertex(minX, maxY, maxZ));
182 vertices.Add(new Vertex(minX, minY, maxZ));
183 vertices.Add(new Vertex(maxX, minY, maxZ));
184
185 box.Add(new Triangle(vertices[4], vertices[5], vertices[6]));
186 box.Add(new Triangle(vertices[4], vertices[6], vertices[7]));
187
188 // sides
189
190 box.Add(new Triangle(vertices[5], vertices[0], vertices[3]));
191 box.Add(new Triangle(vertices[5], vertices[3], vertices[6]));
192
193 box.Add(new Triangle(vertices[1], vertices[0], vertices[5]));
194 box.Add(new Triangle(vertices[1], vertices[5], vertices[4]));
195
196 box.Add(new Triangle(vertices[7], vertices[1], vertices[4]));
197 box.Add(new Triangle(vertices[7], vertices[2], vertices[1]));
198
199 box.Add(new Triangle(vertices[3], vertices[2], vertices[7]));
200 box.Add(new Triangle(vertices[3], vertices[7], vertices[6]));
201
202 return box;
203 }
204
205 /// <summary>
206 /// Creates a simple bounding box mesh for a complex input mesh
207 /// </summary>
208 /// <param name="meshIn"></param>
209 /// <returns></returns>
210 private static Mesh CreateBoundingBoxMesh(Mesh meshIn)
211 {
212 float minX = float.MaxValue;
213 float maxX = float.MinValue;
214 float minY = float.MaxValue;
215 float maxY = float.MinValue;
216 float minZ = float.MaxValue;
217 float maxZ = float.MinValue;
218
219 foreach (Vector3 v in meshIn.getVertexList())
220 {
221 if (v.X < minX) minX = v.X;
222 if (v.Y < minY) minY = v.Y;
223 if (v.Z < minZ) minZ = v.Z;
224
225 if (v.X > maxX) maxX = v.X;
226 if (v.Y > maxY) maxY = v.Y;
227 if (v.Z > maxZ) maxZ = v.Z;
228 }
229
230 return CreateSimpleBoxMesh(minX, maxX, minY, maxY, minZ, maxZ);
231 }
232
233 private void ReportPrimError(string message, string primName, PrimMesh primMesh)
234 {
235 m_log.Error(message);
236 m_log.Error("\nPrim Name: " + primName);
237 m_log.Error("****** PrimMesh Parameters ******\n" + primMesh.ParamsToDisplayString());
238 }
239
240 /// <summary>
241 /// Add a submesh to an existing list of coords and faces.
242 /// </summary>
243 /// <param name="subMeshData"></param>
244 /// <param name="size">Size of entire object</param>
245 /// <param name="coords"></param>
246 /// <param name="faces"></param>
247 private void AddSubMesh(OSDMap subMeshData, Vector3 size, List<Coord> coords, List<Face> faces)
248 {
249 // Console.WriteLine("subMeshMap for {0} - {1}", primName, Util.GetFormattedXml((OSD)subMeshMap));
250
251 // As per http://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format, some Mesh Level
252 // of Detail Blocks (maps) contain just a NoGeometry key to signal there is no
253 // geometry for this submesh.
254 if (subMeshData.ContainsKey("NoGeometry") && ((OSDBoolean)subMeshData["NoGeometry"]))
255 return;
256
257 OpenMetaverse.Vector3 posMax = ((OSDMap)subMeshData["PositionDomain"])["Max"].AsVector3();
258 OpenMetaverse.Vector3 posMin = ((OSDMap)subMeshData["PositionDomain"])["Min"].AsVector3();
259 ushort faceIndexOffset = (ushort)coords.Count;
260
261 byte[] posBytes = subMeshData["Position"].AsBinary();
262 for (int i = 0; i < posBytes.Length; i += 6)
263 {
264 ushort uX = Utils.BytesToUInt16(posBytes, i);
265 ushort uY = Utils.BytesToUInt16(posBytes, i + 2);
266 ushort uZ = Utils.BytesToUInt16(posBytes, i + 4);
267
268 Coord c = new Coord(
269 Utils.UInt16ToFloat(uX, posMin.X, posMax.X) * size.X,
270 Utils.UInt16ToFloat(uY, posMin.Y, posMax.Y) * size.Y,
271 Utils.UInt16ToFloat(uZ, posMin.Z, posMax.Z) * size.Z);
272
273 coords.Add(c);
274 }
275
276 byte[] triangleBytes = subMeshData["TriangleList"].AsBinary();
277 for (int i = 0; i < triangleBytes.Length; i += 6)
278 {
279 ushort v1 = (ushort)(Utils.BytesToUInt16(triangleBytes, i) + faceIndexOffset);
280 ushort v2 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 2) + faceIndexOffset);
281 ushort v3 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 4) + faceIndexOffset);
282 Face f = new Face(v1, v2, v3);
283 faces.Add(f);
284 }
285 }
286
287 /// <summary>
288 /// Create a physics mesh from data that comes with the prim. The actual data used depends on the prim type.
289 /// </summary>
290 /// <param name="primName"></param>
291 /// <param name="primShape"></param>
292 /// <param name="size"></param>
293 /// <param name="lod"></param>
294 /// <returns></returns>
295 private Mesh CreateMeshFromPrimMesher(string primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
296 {
297// m_log.DebugFormat(
298// "[MESH]: Creating physics proxy for {0}, shape {1}",
299// primName, (OpenMetaverse.SculptType)primShape.SculptType);
300
301 List<Coord> coords;
302 List<Face> faces;
303
304 if (primShape.SculptEntry)
305 {
306 if (((OpenMetaverse.SculptType)primShape.SculptType) == SculptType.Mesh)
307 {
308 if (!useMeshiesPhysicsMesh)
309 return null;
310
311 if (!GenerateCoordsAndFacesFromPrimMeshData(primName, primShape, size, out coords, out faces))
312 return null;
313 }
314 else
315 {
316 if (!GenerateCoordsAndFacesFromPrimSculptData(primName, primShape, size, lod, out coords, out faces))
317 return null;
318 }
319 }
320 else
321 {
322 if (!GenerateCoordsAndFacesFromPrimShapeData(primName, primShape, size, lod, out coords, out faces))
323 return null;
324 }
325
326 // Remove the reference to any JPEG2000 sculpt data so it can be GCed
327 primShape.SculptData = Utils.EmptyBytes;
328
329 int numCoords = coords.Count;
330 int numFaces = faces.Count;
331
332 // Create the list of vertices
333 List<Vertex> vertices = new List<Vertex>();
334 for (int i = 0; i < numCoords; i++)
335 {
336 Coord c = coords[i];
337 vertices.Add(new Vertex(c.X, c.Y, c.Z));
338 }
339
340 Mesh mesh = new Mesh();
341 // Add the corresponding triangles to the mesh
342 for (int i = 0; i < numFaces; i++)
343 {
344 Face f = faces[i];
345 mesh.Add(new Triangle(vertices[f.v1], vertices[f.v2], vertices[f.v3]));
346 }
347
348 return mesh;
349 }
350
351 /// <summary>
352 /// Generate the co-ords and faces necessary to construct a mesh from the mesh data the accompanies a prim.
353 /// </summary>
354 /// <param name="primName"></param>
355 /// <param name="primShape"></param>
356 /// <param name="size"></param>
357 /// <param name="coords">Coords are added to this list by the method.</param>
358 /// <param name="faces">Faces are added to this list by the method.</param>
359 /// <returns>true if coords and faces were successfully generated, false if not</returns>
360 private bool GenerateCoordsAndFacesFromPrimMeshData(
361 string primName, PrimitiveBaseShape primShape, Vector3 size, out List<Coord> coords, out List<Face> faces)
362 {
363// m_log.DebugFormat("[MESH]: experimental mesh proxy generation for {0}", primName);
364
365 coords = new List<Coord>();
366 faces = new List<Face>();
367 OSD meshOsd = null;
368
369 mConvexHulls = null;
370 mBoundingHull = null;
371
372 if (primShape.SculptData.Length <= 0)
373 {
374 // XXX: At the moment we can not log here since ODEPrim, for instance, ends up triggering this
375 // method twice - once before it has loaded sculpt data from the asset service and once afterwards.
376 // The first time will always call with unloaded SculptData if this needs to be uploaded.
377// m_log.ErrorFormat("[MESH]: asset data for {0} is zero length", primName);
378 return false;
379 }
380
381 long start = 0;
382 using (MemoryStream data = new MemoryStream(primShape.SculptData))
383 {
384 try
385 {
386 OSD osd = OSDParser.DeserializeLLSDBinary(data);
387 if (osd is OSDMap)
388 meshOsd = (OSDMap)osd;
389 else
390 {
391 m_log.Warn("[Mesh}: unable to cast mesh asset to OSDMap");
392 return false;
393 }
394 }
395 catch (Exception e)
396 {
397 m_log.Error("[MESH]: Exception deserializing mesh asset header:" + e.ToString());
398 }
399
400 start = data.Position;
401 }
402
403 if (meshOsd is OSDMap)
404 {
405 OSDMap physicsParms = null;
406 OSDMap map = (OSDMap)meshOsd;
407 if (map.ContainsKey("physics_shape"))
408 {
409 physicsParms = (OSDMap)map["physics_shape"]; // old asset format
410 if (debugDetail) m_log.DebugFormat("{0} prim='{1}': using 'physics_shape' mesh data", LogHeader, primName);
411 }
412 else if (map.ContainsKey("physics_mesh"))
413 {
414 physicsParms = (OSDMap)map["physics_mesh"]; // new asset format
415 if (debugDetail) m_log.DebugFormat("{0} prim='{1}':using 'physics_mesh' mesh data", LogHeader, primName);
416 }
417 else if (map.ContainsKey("medium_lod"))
418 {
419 physicsParms = (OSDMap)map["medium_lod"]; // if no physics mesh, try to fall back to medium LOD display mesh
420 if (debugDetail) m_log.DebugFormat("{0} prim='{1}':using 'medium_lod' mesh data", LogHeader, primName);
421 }
422 else if (map.ContainsKey("high_lod"))
423 {
424 physicsParms = (OSDMap)map["high_lod"]; // if all else fails, use highest LOD display mesh and hope it works :)
425 if (debugDetail) m_log.DebugFormat("{0} prim='{1}':using 'high_lod' mesh data", LogHeader, primName);
426 }
427
428 if (map.ContainsKey("physics_convex"))
429 { // pull this out also in case physics engine can use it
430 OSD convexBlockOsd = null;
431 try
432 {
433 OSDMap convexBlock = (OSDMap)map["physics_convex"];
434 {
435 int convexOffset = convexBlock["offset"].AsInteger() + (int)start;
436 int convexSize = convexBlock["size"].AsInteger();
437
438 byte[] convexBytes = new byte[convexSize];
439
440 System.Buffer.BlockCopy(primShape.SculptData, convexOffset, convexBytes, 0, convexSize);
441
442 try
443 {
444 convexBlockOsd = DecompressOsd(convexBytes);
445 }
446 catch (Exception e)
447 {
448 m_log.ErrorFormat("{0} prim='{1}': exception decoding convex block: {2}", LogHeader, primName, e);
449 //return false;
450 }
451 }
452
453 if (convexBlockOsd != null && convexBlockOsd is OSDMap)
454 {
455 convexBlock = convexBlockOsd as OSDMap;
456
457 if (debugDetail)
458 {
459 string keys = LogHeader + " keys found in convexBlock: ";
460 foreach (KeyValuePair<string, OSD> kvp in convexBlock)
461 keys += "'" + kvp.Key + "' ";
462 m_log.Debug(keys);
463 }
464
465 Vector3 min = new Vector3(-0.5f, -0.5f, -0.5f);
466 if (convexBlock.ContainsKey("Min")) min = convexBlock["Min"].AsVector3();
467 Vector3 max = new Vector3(0.5f, 0.5f, 0.5f);
468 if (convexBlock.ContainsKey("Max")) max = convexBlock["Max"].AsVector3();
469
470 List<Vector3> boundingHull = null;
471
472 if (convexBlock.ContainsKey("BoundingVerts"))
473 {
474 byte[] boundingVertsBytes = convexBlock["BoundingVerts"].AsBinary();
475 boundingHull = new List<Vector3>();
476 for (int i = 0; i < boundingVertsBytes.Length; )
477 {
478 ushort uX = Utils.BytesToUInt16(boundingVertsBytes, i); i += 2;
479 ushort uY = Utils.BytesToUInt16(boundingVertsBytes, i); i += 2;
480 ushort uZ = Utils.BytesToUInt16(boundingVertsBytes, i); i += 2;
481
482 Vector3 pos = new Vector3(
483 Utils.UInt16ToFloat(uX, min.X, max.X),
484 Utils.UInt16ToFloat(uY, min.Y, max.Y),
485 Utils.UInt16ToFloat(uZ, min.Z, max.Z)
486 );
487
488 boundingHull.Add(pos);
489 }
490
491 mBoundingHull = boundingHull;
492 if (debugDetail) m_log.DebugFormat("{0} prim='{1}': parsed bounding hull. nVerts={2}", LogHeader, primName, mBoundingHull.Count);
493 }
494
495 if (convexBlock.ContainsKey("HullList"))
496 {
497 byte[] hullList = convexBlock["HullList"].AsBinary();
498
499 byte[] posBytes = convexBlock["Positions"].AsBinary();
500
501 List<List<Vector3>> hulls = new List<List<Vector3>>();
502 int posNdx = 0;
503
504 foreach (byte cnt in hullList)
505 {
506 int count = cnt == 0 ? 256 : cnt;
507 List<Vector3> hull = new List<Vector3>();
508
509 for (int i = 0; i < count; i++)
510 {
511 ushort uX = Utils.BytesToUInt16(posBytes, posNdx); posNdx += 2;
512 ushort uY = Utils.BytesToUInt16(posBytes, posNdx); posNdx += 2;
513 ushort uZ = Utils.BytesToUInt16(posBytes, posNdx); posNdx += 2;
514
515 Vector3 pos = new Vector3(
516 Utils.UInt16ToFloat(uX, min.X, max.X),
517 Utils.UInt16ToFloat(uY, min.Y, max.Y),
518 Utils.UInt16ToFloat(uZ, min.Z, max.Z)
519 );
520
521 hull.Add(pos);
522 }
523
524 hulls.Add(hull);
525 }
526
527 mConvexHulls = hulls;
528 if (debugDetail) m_log.DebugFormat("{0} prim='{1}': parsed hulls. nHulls={2}", LogHeader, primName, mConvexHulls.Count);
529 }
530 else
531 {
532 if (debugDetail) m_log.DebugFormat("{0} prim='{1}' has physics_convex but no HullList", LogHeader, primName);
533 }
534 }
535 }
536 catch (Exception e)
537 {
538 m_log.WarnFormat("{0} exception decoding convex block: {1}", LogHeader, e);
539 }
540 }
541
542 if (physicsParms == null)
543 {
544 m_log.WarnFormat("[MESH]: No recognized physics mesh found in mesh asset for {0}", primName);
545 return false;
546 }
547
548 int physOffset = physicsParms["offset"].AsInteger() + (int)start;
549 int physSize = physicsParms["size"].AsInteger();
550
551 if (physOffset < 0 || physSize == 0)
552 return false; // no mesh data in asset
553
554 OSD decodedMeshOsd = new OSD();
555 byte[] meshBytes = new byte[physSize];
556 System.Buffer.BlockCopy(primShape.SculptData, physOffset, meshBytes, 0, physSize);
557 // byte[] decompressed = new byte[physSize * 5];
558 try
559 {
560 decodedMeshOsd = DecompressOsd(meshBytes);
561 }
562 catch (Exception e)
563 {
564 m_log.ErrorFormat("{0} prim='{1}': exception decoding physical mesh: {2}", LogHeader, primName, e);
565 return false;
566 }
567
568 OSDArray decodedMeshOsdArray = null;
569
570 // physics_shape is an array of OSDMaps, one for each submesh
571 if (decodedMeshOsd is OSDArray)
572 {
573 // Console.WriteLine("decodedMeshOsd for {0} - {1}", primName, Util.GetFormattedXml(decodedMeshOsd));
574
575 decodedMeshOsdArray = (OSDArray)decodedMeshOsd;
576 foreach (OSD subMeshOsd in decodedMeshOsdArray)
577 {
578 if (subMeshOsd is OSDMap)
579 AddSubMesh(subMeshOsd as OSDMap, size, coords, faces);
580 }
581 if (debugDetail)
582 m_log.DebugFormat("{0} {1}: mesh decoded. offset={2}, size={3}, nCoords={4}, nFaces={5}",
583 LogHeader, primName, physOffset, physSize, coords.Count, faces.Count);
584 }
585 }
586
587 return true;
588 }
589
590 /// <summary>
591 /// decompresses a gzipped OSD object
592 /// </summary>
593 /// <param name="decodedOsd"></param> the OSD object
594 /// <param name="meshBytes"></param>
595 /// <returns></returns>
596 private static OSD DecompressOsd(byte[] meshBytes)
597 {
598 OSD decodedOsd = null;
599
600 using (MemoryStream inMs = new MemoryStream(meshBytes))
601 {
602 using (MemoryStream outMs = new MemoryStream())
603 {
604 using (DeflateStream decompressionStream = new DeflateStream(inMs, CompressionMode.Decompress))
605 {
606 byte[] readBuffer = new byte[2048];
607 inMs.Read(readBuffer, 0, 2); // skip first 2 bytes in header
608 int readLen = 0;
609
610 while ((readLen = decompressionStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
611 outMs.Write(readBuffer, 0, readLen);
612
613 outMs.Flush();
614
615 outMs.Seek(0, SeekOrigin.Begin);
616
617 byte[] decompressedBuf = outMs.GetBuffer();
618
619 decodedOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
620 }
621 }
622 }
623 return decodedOsd;
624 }
625
626 /// <summary>
627 /// Generate the co-ords and faces necessary to construct a mesh from the sculpt data the accompanies a prim.
628 /// </summary>
629 /// <param name="primName"></param>
630 /// <param name="primShape"></param>
631 /// <param name="size"></param>
632 /// <param name="lod"></param>
633 /// <param name="coords">Coords are added to this list by the method.</param>
634 /// <param name="faces">Faces are added to this list by the method.</param>
635 /// <returns>true if coords and faces were successfully generated, false if not</returns>
636 private bool GenerateCoordsAndFacesFromPrimSculptData(
637 string primName, PrimitiveBaseShape primShape, Vector3 size, float lod, out List<Coord> coords, out List<Face> faces)
638 {
639 coords = new List<Coord>();
640 faces = new List<Face>();
641 PrimMesher.SculptMesh sculptMesh;
642 Image idata = null;
643 string decodedSculptFileName = "";
644
645 if (cacheSculptMaps && primShape.SculptTexture != UUID.Zero)
646 {
647 decodedSculptFileName = System.IO.Path.Combine(decodedSculptMapPath, "smap_" + primShape.SculptTexture.ToString());
648 try
649 {
650 if (File.Exists(decodedSculptFileName))
651 {
652 idata = Image.FromFile(decodedSculptFileName);
653 }
654 }
655 catch (Exception e)
656 {
657 m_log.Error("[SCULPT]: unable to load cached sculpt map " + decodedSculptFileName + " " + e.Message);
658
659 }
660 //if (idata != null)
661 // m_log.Debug("[SCULPT]: loaded cached map asset for map ID: " + primShape.SculptTexture.ToString());
662 }
663
664 if (idata == null)
665 {
666 if (primShape.SculptData == null || primShape.SculptData.Length == 0)
667 return false;
668
669 try
670 {
671 OpenMetaverse.Imaging.ManagedImage managedImage;
672
673 OpenMetaverse.Imaging.OpenJPEG.DecodeToImage(primShape.SculptData, out managedImage);
674
675 if (managedImage == null)
676 {
677 // In some cases it seems that the decode can return a null bitmap without throwing
678 // an exception
679 m_log.WarnFormat("[PHYSICS]: OpenJPEG decoded sculpt data for {0} to a null bitmap. Ignoring.", primName);
680
681 return false;
682 }
683
684 if ((managedImage.Channels & OpenMetaverse.Imaging.ManagedImage.ImageChannels.Alpha) != 0)
685 managedImage.ConvertChannels(managedImage.Channels & ~OpenMetaverse.Imaging.ManagedImage.ImageChannels.Alpha);
686
687 Bitmap imgData = OpenMetaverse.Imaging.LoadTGAClass.LoadTGA(new MemoryStream(managedImage.ExportTGA()));
688 idata = (Image)imgData;
689 managedImage = null;
690
691 if (cacheSculptMaps)
692 {
693 try { idata.Save(decodedSculptFileName, ImageFormat.MemoryBmp); }
694 catch (Exception e) { m_log.Error("[SCULPT]: unable to cache sculpt map " + decodedSculptFileName + " " + e.Message); }
695 }
696 }
697 catch (DllNotFoundException)
698 {
699 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!");
700 return false;
701 }
702 catch (IndexOutOfRangeException)
703 {
704 m_log.Error("[PHYSICS]: OpenJpeg was unable to decode this. Physics Proxy generation failed");
705 return false;
706 }
707 catch (Exception ex)
708 {
709 m_log.Error("[PHYSICS]: Unable to generate a Sculpty physics proxy. Sculpty texture decode failed: " + ex.Message);
710 return false;
711 }
712 }
713
714 PrimMesher.SculptMesh.SculptType sculptType;
715 switch ((OpenMetaverse.SculptType)primShape.SculptType)
716 {
717 case OpenMetaverse.SculptType.Cylinder:
718 sculptType = PrimMesher.SculptMesh.SculptType.cylinder;
719 break;
720 case OpenMetaverse.SculptType.Plane:
721 sculptType = PrimMesher.SculptMesh.SculptType.plane;
722 break;
723 case OpenMetaverse.SculptType.Torus:
724 sculptType = PrimMesher.SculptMesh.SculptType.torus;
725 break;
726 case OpenMetaverse.SculptType.Sphere:
727 sculptType = PrimMesher.SculptMesh.SculptType.sphere;
728 break;
729 default:
730 sculptType = PrimMesher.SculptMesh.SculptType.plane;
731 break;
732 }
733
734 bool mirror = ((primShape.SculptType & 128) != 0);
735 bool invert = ((primShape.SculptType & 64) != 0);
736
737 sculptMesh = new PrimMesher.SculptMesh((Bitmap)idata, sculptType, (int)lod, false, mirror, invert);
738
739 idata.Dispose();
740
741 sculptMesh.DumpRaw(baseDir, primName, "primMesh");
742
743 sculptMesh.Scale(size.X, size.Y, size.Z);
744
745 coords = sculptMesh.coords;
746 faces = sculptMesh.faces;
747
748 return true;
749 }
750
751 /// <summary>
752 /// Generate the co-ords and faces necessary to construct a mesh from the shape data the accompanies a prim.
753 /// </summary>
754 /// <param name="primName"></param>
755 /// <param name="primShape"></param>
756 /// <param name="size"></param>
757 /// <param name="coords">Coords are added to this list by the method.</param>
758 /// <param name="faces">Faces are added to this list by the method.</param>
759 /// <returns>true if coords and faces were successfully generated, false if not</returns>
760 private bool GenerateCoordsAndFacesFromPrimShapeData(
761 string primName, PrimitiveBaseShape primShape, Vector3 size, float lod, out List<Coord> coords, out List<Face> faces)
762 {
763 PrimMesh primMesh;
764 coords = new List<Coord>();
765 faces = new List<Face>();
766
767 float pathShearX = primShape.PathShearX < 128 ? (float)primShape.PathShearX * 0.01f : (float)(primShape.PathShearX - 256) * 0.01f;
768 float pathShearY = primShape.PathShearY < 128 ? (float)primShape.PathShearY * 0.01f : (float)(primShape.PathShearY - 256) * 0.01f;
769 float pathBegin = (float)primShape.PathBegin * 2.0e-5f;
770 float pathEnd = 1.0f - (float)primShape.PathEnd * 2.0e-5f;
771 float pathScaleX = (float)(primShape.PathScaleX - 100) * 0.01f;
772 float pathScaleY = (float)(primShape.PathScaleY - 100) * 0.01f;
773
774 float profileBegin = (float)primShape.ProfileBegin * 2.0e-5f;
775 float profileEnd = 1.0f - (float)primShape.ProfileEnd * 2.0e-5f;
776 float profileHollow = (float)primShape.ProfileHollow * 2.0e-5f;
777 if (profileHollow > 0.95f)
778 profileHollow = 0.95f;
779
780 int sides = 4;
781 LevelOfDetail iLOD = (LevelOfDetail)lod;
782 if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
783 sides = 3;
784 else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
785 {
786 switch (iLOD)
787 {
788 case LevelOfDetail.High: sides = 24; break;
789 case LevelOfDetail.Medium: sides = 12; break;
790 case LevelOfDetail.Low: sides = 6; break;
791 case LevelOfDetail.VeryLow: sides = 3; break;
792 default: sides = 24; break;
793 }
794 }
795 else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
796 { // half circle, prim is a sphere
797 switch (iLOD)
798 {
799 case LevelOfDetail.High: sides = 24; break;
800 case LevelOfDetail.Medium: sides = 12; break;
801 case LevelOfDetail.Low: sides = 6; break;
802 case LevelOfDetail.VeryLow: sides = 3; break;
803 default: sides = 24; break;
804 }
805
806 profileBegin = 0.5f * profileBegin + 0.5f;
807 profileEnd = 0.5f * profileEnd + 0.5f;
808 }
809
810 int hollowSides = sides;
811 if (primShape.HollowShape == HollowShape.Circle)
812 {
813 switch (iLOD)
814 {
815 case LevelOfDetail.High: hollowSides = 24; break;
816 case LevelOfDetail.Medium: hollowSides = 12; break;
817 case LevelOfDetail.Low: hollowSides = 6; break;
818 case LevelOfDetail.VeryLow: hollowSides = 3; break;
819 default: hollowSides = 24; break;
820 }
821 }
822 else if (primShape.HollowShape == HollowShape.Square)
823 hollowSides = 4;
824 else if (primShape.HollowShape == HollowShape.Triangle)
825 hollowSides = 3;
826
827 primMesh = new PrimMesh(sides, profileBegin, profileEnd, profileHollow, hollowSides);
828
829 if (primMesh.errorMessage != null)
830 if (primMesh.errorMessage.Length > 0)
831 m_log.Error("[ERROR] " + primMesh.errorMessage);
832
833 primMesh.topShearX = pathShearX;
834 primMesh.topShearY = pathShearY;
835 primMesh.pathCutBegin = pathBegin;
836 primMesh.pathCutEnd = pathEnd;
837
838 if (primShape.PathCurve == (byte)Extrusion.Straight || primShape.PathCurve == (byte) Extrusion.Flexible)
839 {
840 primMesh.twistBegin = primShape.PathTwistBegin * 18 / 10;
841 primMesh.twistEnd = primShape.PathTwist * 18 / 10;
842 primMesh.taperX = pathScaleX;
843 primMesh.taperY = pathScaleY;
844
845 if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f)
846 {
847 ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh);
848 if (profileBegin < 0.0f) profileBegin = 0.0f;
849 if (profileEnd > 1.0f) profileEnd = 1.0f;
850 }
851#if SPAM
852 m_log.Debug("****** PrimMesh Parameters (Linear) ******\n" + primMesh.ParamsToDisplayString());
853#endif
854 try
855 {
856 primMesh.ExtrudeLinear();
857 }
858 catch (Exception ex)
859 {
860 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
861 return false;
862 }
863 }
864 else
865 {
866 primMesh.holeSizeX = (200 - primShape.PathScaleX) * 0.01f;
867 primMesh.holeSizeY = (200 - primShape.PathScaleY) * 0.01f;
868 primMesh.radius = 0.01f * primShape.PathRadiusOffset;
869 primMesh.revolutions = 1.0f + 0.015f * primShape.PathRevolutions;
870 primMesh.skew = 0.01f * primShape.PathSkew;
871 primMesh.twistBegin = primShape.PathTwistBegin * 36 / 10;
872 primMesh.twistEnd = primShape.PathTwist * 36 / 10;
873 primMesh.taperX = primShape.PathTaperX * 0.01f;
874 primMesh.taperY = primShape.PathTaperY * 0.01f;
875
876 if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f)
877 {
878 ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh);
879 if (profileBegin < 0.0f) profileBegin = 0.0f;
880 if (profileEnd > 1.0f) profileEnd = 1.0f;
881 }
882#if SPAM
883 m_log.Debug("****** PrimMesh Parameters (Circular) ******\n" + primMesh.ParamsToDisplayString());
884#endif
885 try
886 {
887 primMesh.ExtrudeCircular();
888 }
889 catch (Exception ex)
890 {
891 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
892 return false;
893 }
894 }
895
896 primMesh.DumpRaw(baseDir, primName, "primMesh");
897
898 primMesh.Scale(size.X, size.Y, size.Z);
899
900 coords = primMesh.coords;
901 faces = primMesh.faces;
902
903 return true;
904 }
905
906 /// <summary>
907 /// temporary prototype code - please do not use until the interface has been finalized!
908 /// </summary>
909 /// <param name="size">value to scale the hull points by</param>
910 /// <returns>a list of vertices in the bounding hull if it exists and has been successfully decoded, otherwise null</returns>
911 public List<Vector3> GetBoundingHull(Vector3 size)
912 {
913 if (mBoundingHull == null)
914 return null;
915
916 List<Vector3> verts = new List<Vector3>();
917 foreach (var vert in mBoundingHull)
918 verts.Add(vert * size);
919
920 return verts;
921 }
922
923 /// <summary>
924 /// temporary prototype code - please do not use until the interface has been finalized!
925 /// </summary>
926 /// <param name="size">value to scale the hull points by</param>
927 /// <returns>a list of hulls if they exist and have been successfully decoded, otherwise null</returns>
928 public List<List<Vector3>> GetConvexHulls(Vector3 size)
929 {
930 if (mConvexHulls == null)
931 return null;
932
933 List<List<Vector3>> hulls = new List<List<Vector3>>();
934 foreach (var hull in mConvexHulls)
935 {
936 List<Vector3> verts = new List<Vector3>();
937 foreach (var vert in hull)
938 verts.Add(vert * size);
939 hulls.Add(verts);
940 }
941
942 return hulls;
943 }
944
945 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
946 {
947 return CreateMesh(primName, primShape, size, lod, false, true);
948 }
949
950 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
951 {
952 return CreateMesh(primName, primShape, size, lod, isPhysical, true);
953 }
954
955 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache)
956 {
957#if SPAM
958 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName);
959#endif
960
961 Mesh mesh = null;
962 ulong key = 0;
963
964 // If this mesh has been created already, return it instead of creating another copy
965 // For large regions with 100k+ prims and hundreds of copies of each, this can save a GB or more of memory
966 if (shouldCache)
967 {
968 key = primShape.GetMeshKey(size, lod);
969 lock (m_uniqueMeshes)
970 {
971 if (m_uniqueMeshes.TryGetValue(key, out mesh))
972 return mesh;
973 }
974 }
975
976 if (size.X < 0.01f) size.X = 0.01f;
977 if (size.Y < 0.01f) size.Y = 0.01f;
978 if (size.Z < 0.01f) size.Z = 0.01f;
979
980 mesh = CreateMeshFromPrimMesher(primName, primShape, size, lod);
981
982 if (mesh != null)
983 {
984 if ((!isPhysical) && size.X < minSizeForComplexMesh && size.Y < minSizeForComplexMesh && size.Z < minSizeForComplexMesh)
985 {
986#if SPAM
987 m_log.Debug("Meshmerizer: prim " + primName + " has a size of " + size.ToString() + " which is below threshold of " +
988 minSizeForComplexMesh.ToString() + " - creating simple bounding box");
989#endif
990 mesh = CreateBoundingBoxMesh(mesh);
991 mesh.DumpRaw(baseDir, primName, "Z extruded");
992 }
993
994 // trim the vertex and triangle lists to free up memory
995 mesh.TrimExcess();
996
997 if (shouldCache)
998 {
999 lock (m_uniqueMeshes)
1000 {
1001 m_uniqueMeshes.Add(key, mesh);
1002 }
1003 }
1004 }
1005
1006 return mesh;
1007 }
1008
1009 }
1010}