aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/PhysicsModules/Meshing/Meshmerizer/Meshmerizer.cs
diff options
context:
space:
mode:
authorRobert Adams2015-09-08 04:54:16 -0700
committerRobert Adams2015-09-08 04:54:16 -0700
commite5367d822be9b05e74c859afe2d2956a3e95aa33 (patch)
treee904050a30715df587aa527d7f313755177726a7 /OpenSim/Region/PhysicsModules/Meshing/Meshmerizer/Meshmerizer.cs
parentadd lost admin_reset_land method (diff)
parentDeleted access control spec from [LoginService] section of standalone config.... (diff)
downloadopensim-SC-e5367d822be9b05e74c859afe2d2956a3e95aa33.zip
opensim-SC-e5367d822be9b05e74c859afe2d2956a3e95aa33.tar.gz
opensim-SC-e5367d822be9b05e74c859afe2d2956a3e95aa33.tar.bz2
opensim-SC-e5367d822be9b05e74c859afe2d2956a3e95aa33.tar.xz
Merge of ubitworkvarnew with opensim/master as of 20150905.
This integrates the OpenSim refactoring to make physics, etc into modules. AVN physics hasn't been moved to new location. Does not compile yet. Merge branch 'osmaster' into mbworknew1
Diffstat (limited to 'OpenSim/Region/PhysicsModules/Meshing/Meshmerizer/Meshmerizer.cs')
-rw-r--r--OpenSim/Region/PhysicsModules/Meshing/Meshmerizer/Meshmerizer.cs1027
1 files changed, 1027 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..bae3449
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/Meshing/Meshmerizer/Meshmerizer.cs
@@ -0,0 +1,1027 @@
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, 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}