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