OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
Meshmerizer.cs
Go to the documentation of this file.
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 
29 using System;
30 using System.Collections.Generic;
31 using System.Reflection;
32 using System.IO;
33 using OpenSim.Framework;
34 using OpenSim.Region.Framework.Scenes;
35 using OpenSim.Region.Framework.Interfaces;
36 using OpenSim.Region.PhysicsModules.SharedBase;
37 using OpenMetaverse;
38 using OpenMetaverse.StructuredData;
39 using System.Drawing;
40 using System.Drawing.Imaging;
41 using System.IO.Compression;
42 using PrimMesher;
43 using log4net;
44 using Nini.Config;
45 using Mono.Addins;
46 
47 namespace OpenSim.Region.PhysicsModule.Meshing
48 {
49  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "Meshmerizer")]
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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, bool shouldCache, bool convex, bool forOde)
951  {
952  return CreateMesh(primName, primShape, size, lod, false);
953  }
954 
955  public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
956  {
957  return CreateMesh(primName, primShape, size, lod, isPhysical, true);
958  }
959 
960  public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex, bool forOde)
961  {
962  return CreateMesh(primName, primShape, size, lod, isPhysical, true);
963  }
964 
965  public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache)
966  {
967 #if SPAM
968  m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName);
969 #endif
970 
971  Mesh mesh = null;
972  ulong key = 0;
973 
974  // If this mesh has been created already, return it instead of creating another copy
975  // For large regions with 100k+ prims and hundreds of copies of each, this can save a GB or more of memory
976  if (shouldCache)
977  {
978  key = primShape.GetMeshKey(size, lod);
979  lock (m_uniqueMeshes)
980  {
981  if (m_uniqueMeshes.TryGetValue(key, out mesh))
982  return mesh;
983  }
984  }
985 
986  if (size.X < 0.01f) size.X = 0.01f;
987  if (size.Y < 0.01f) size.Y = 0.01f;
988  if (size.Z < 0.01f) size.Z = 0.01f;
989 
990  mesh = CreateMeshFromPrimMesher(primName, primShape, size, lod);
991 
992  if (mesh != null)
993  {
994  if ((!isPhysical) && size.X < minSizeForComplexMesh && size.Y < minSizeForComplexMesh && size.Z < minSizeForComplexMesh)
995  {
996 #if SPAM
997  m_log.Debug("Meshmerizer: prim " + primName + " has a size of " + size.ToString() + " which is below threshold of " +
998  minSizeForComplexMesh.ToString() + " - creating simple bounding box");
999 #endif
1000  mesh = CreateBoundingBoxMesh(mesh);
1001  mesh.DumpRaw(baseDir, primName, "Z extruded");
1002  }
1003 
1004  // trim the vertex and triangle lists to free up memory
1005  mesh.TrimExcess();
1006 
1007  if (shouldCache)
1008  {
1009  lock (m_uniqueMeshes)
1010  {
1011  m_uniqueMeshes.Add(key, mesh);
1012  }
1013  }
1014  }
1015 
1016  return mesh;
1017  }
1018  public IMesh GetMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex)
1019  {
1020  return null;
1021  }
1022 
1023  public void ReleaseMesh(IMesh imesh) { }
1024  public void ExpireReleaseMeshs() { }
1025  public void ExpireFileCache() { }
1026  }
1027 }
List< List< Vector3 > > GetConvexHulls(Vector3 size)
temporary prototype code - please do not use until the interface has been finalized! ...
Definition: Meshmerizer.cs:928
OpenMetaverse.StructuredData.OSDArray OSDArray
OpenSim.Server.Handlers.Simulation.Utils Utils
IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache)
Definition: Meshmerizer.cs:965
OpenMetaverse.StructuredData.OSDMap OSDMap
IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache, bool convex, bool forOde)
Definition: Meshmerizer.cs:950
OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString key
Definition: ICM_Api.cs:31
IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
Definition: Meshmerizer.cs:955
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
Definition: Meshmerizer.cs:144
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
Definition: Meshmerizer.cs:128
void Initialise(IConfigSource source)
This is called to initialize the region module. For shared modules, this is called exactly once...
Definition: Meshmerizer.cs:90
List< Vector3 > GetBoundingHull(Vector3 size)
temporary prototype code - please do not use until the interface has been finalized! ...
Definition: Meshmerizer.cs:911
IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex, bool forOde)
Definition: Meshmerizer.cs:960
OpenSim.Region.OptionalModules.Scripting.Minimodule.Object.SculptType SculptType
Definition: SOPObject.cs:39
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
Definition: Meshmerizer.cs:124
OpenMetaverse.StructuredData.OSD OSD
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
Definition: Meshmerizer.cs:136
Ionic.Zlib.CompressionMode CompressionMode
IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
Definition: Meshmerizer.cs:945
IMesh GetMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex)