diff options
Diffstat (limited to 'OpenSim/Region/Physics/Meshing/Meshmerizer.cs')
-rw-r--r-- | OpenSim/Region/Physics/Meshing/Meshmerizer.cs | 265 |
1 files changed, 234 insertions, 31 deletions
diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs index a32f401..d67d4d2 100644 --- a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs +++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs | |||
@@ -40,7 +40,6 @@ using log4net; | |||
40 | using Nini.Config; | 40 | using Nini.Config; |
41 | using System.Reflection; | 41 | using System.Reflection; |
42 | using System.IO; | 42 | using System.IO; |
43 | using ComponentAce.Compression.Libs.zlib; | ||
44 | 43 | ||
45 | namespace OpenSim.Region.Physics.Meshing | 44 | namespace OpenSim.Region.Physics.Meshing |
46 | { | 45 | { |
@@ -64,6 +63,7 @@ namespace OpenSim.Region.Physics.Meshing | |||
64 | public class Meshmerizer : IMesher | 63 | public class Meshmerizer : IMesher |
65 | { | 64 | { |
66 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 65 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
66 | private static string LogHeader = "[MESH]"; | ||
67 | 67 | ||
68 | // Setting baseDir to a path will enable the dumping of raw files | 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 | 69 | // raw files can be imported by blender so a visual inspection of the results can be done |
@@ -72,6 +72,8 @@ namespace OpenSim.Region.Physics.Meshing | |||
72 | #else | 72 | #else |
73 | private const string baseDir = null; //"rawFiles"; | 73 | private const string baseDir = null; //"rawFiles"; |
74 | #endif | 74 | #endif |
75 | // If 'true', lots of DEBUG logging of asset parsing details | ||
76 | private bool debugDetail = false; | ||
75 | 77 | ||
76 | private bool cacheSculptMaps = true; | 78 | private bool cacheSculptMaps = true; |
77 | private string decodedSculptMapPath = null; | 79 | private string decodedSculptMapPath = null; |
@@ -79,6 +81,9 @@ namespace OpenSim.Region.Physics.Meshing | |||
79 | 81 | ||
80 | private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh | 82 | private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh |
81 | 83 | ||
84 | private List<List<Vector3>> mConvexHulls = null; | ||
85 | private List<Vector3> mBoundingHull = null; | ||
86 | |||
82 | private Dictionary<ulong, Mesh> m_uniqueMeshes = new Dictionary<ulong, Mesh>(); | 87 | private Dictionary<ulong, Mesh> m_uniqueMeshes = new Dictionary<ulong, Mesh>(); |
83 | 88 | ||
84 | public Meshmerizer(IConfigSource config) | 89 | public Meshmerizer(IConfigSource config) |
@@ -88,8 +93,11 @@ namespace OpenSim.Region.Physics.Meshing | |||
88 | 93 | ||
89 | decodedSculptMapPath = start_config.GetString("DecodedSculptMapPath","j2kDecodeCache"); | 94 | decodedSculptMapPath = start_config.GetString("DecodedSculptMapPath","j2kDecodeCache"); |
90 | cacheSculptMaps = start_config.GetBoolean("CacheSculptMaps", cacheSculptMaps); | 95 | cacheSculptMaps = start_config.GetBoolean("CacheSculptMaps", cacheSculptMaps); |
91 | if(mesh_config != null) | 96 | if (mesh_config != null) |
97 | { | ||
92 | useMeshiesPhysicsMesh = mesh_config.GetBoolean("UseMeshiesPhysicsMesh", useMeshiesPhysicsMesh); | 98 | useMeshiesPhysicsMesh = mesh_config.GetBoolean("UseMeshiesPhysicsMesh", useMeshiesPhysicsMesh); |
99 | debugDetail = mesh_config.GetBoolean("LogMeshDetails", debugDetail); | ||
100 | } | ||
93 | 101 | ||
94 | try | 102 | try |
95 | { | 103 | { |
@@ -319,6 +327,9 @@ namespace OpenSim.Region.Physics.Meshing | |||
319 | faces = new List<Face>(); | 327 | faces = new List<Face>(); |
320 | OSD meshOsd = null; | 328 | OSD meshOsd = null; |
321 | 329 | ||
330 | mConvexHulls = null; | ||
331 | mBoundingHull = null; | ||
332 | |||
322 | if (primShape.SculptData.Length <= 0) | 333 | if (primShape.SculptData.Length <= 0) |
323 | { | 334 | { |
324 | // XXX: At the moment we can not log here since ODEPrim, for instance, ends up triggering this | 335 | // XXX: At the moment we can not log here since ODEPrim, for instance, ends up triggering this |
@@ -355,9 +366,139 @@ namespace OpenSim.Region.Physics.Meshing | |||
355 | OSDMap physicsParms = null; | 366 | OSDMap physicsParms = null; |
356 | OSDMap map = (OSDMap)meshOsd; | 367 | OSDMap map = (OSDMap)meshOsd; |
357 | if (map.ContainsKey("physics_shape")) | 368 | if (map.ContainsKey("physics_shape")) |
369 | { | ||
358 | physicsParms = (OSDMap)map["physics_shape"]; // old asset format | 370 | physicsParms = (OSDMap)map["physics_shape"]; // old asset format |
371 | if (debugDetail) m_log.DebugFormat("{0} prim='{1}': using 'physics_shape' mesh data", LogHeader, primName); | ||
372 | } | ||
359 | else if (map.ContainsKey("physics_mesh")) | 373 | else if (map.ContainsKey("physics_mesh")) |
374 | { | ||
360 | physicsParms = (OSDMap)map["physics_mesh"]; // new asset format | 375 | physicsParms = (OSDMap)map["physics_mesh"]; // new asset format |
376 | if (debugDetail) m_log.DebugFormat("{0} prim='{1}':using 'physics_mesh' mesh data", LogHeader, primName); | ||
377 | } | ||
378 | else if (map.ContainsKey("medium_lod")) | ||
379 | { | ||
380 | physicsParms = (OSDMap)map["medium_lod"]; // if no physics mesh, try to fall back to medium LOD display mesh | ||
381 | if (debugDetail) m_log.DebugFormat("{0} prim='{1}':using 'medium_lod' mesh data", LogHeader, primName); | ||
382 | } | ||
383 | else if (map.ContainsKey("high_lod")) | ||
384 | { | ||
385 | physicsParms = (OSDMap)map["high_lod"]; // if all else fails, use highest LOD display mesh and hope it works :) | ||
386 | if (debugDetail) m_log.DebugFormat("{0} prim='{1}':using 'high_lod' mesh data", LogHeader, primName); | ||
387 | } | ||
388 | |||
389 | if (map.ContainsKey("physics_convex")) | ||
390 | { // pull this out also in case physics engine can use it | ||
391 | OSD convexBlockOsd = null; | ||
392 | try | ||
393 | { | ||
394 | OSDMap convexBlock = (OSDMap)map["physics_convex"]; | ||
395 | { | ||
396 | int convexOffset = convexBlock["offset"].AsInteger() + (int)start; | ||
397 | int convexSize = convexBlock["size"].AsInteger(); | ||
398 | |||
399 | byte[] convexBytes = new byte[convexSize]; | ||
400 | |||
401 | System.Buffer.BlockCopy(primShape.SculptData, convexOffset, convexBytes, 0, convexSize); | ||
402 | |||
403 | try | ||
404 | { | ||
405 | convexBlockOsd = DecompressOsd(convexBytes); | ||
406 | } | ||
407 | catch (Exception e) | ||
408 | { | ||
409 | m_log.ErrorFormat("{0} prim='{1}': exception decoding convex block: {2}", LogHeader, primName, e); | ||
410 | //return false; | ||
411 | } | ||
412 | } | ||
413 | |||
414 | if (convexBlockOsd != null && convexBlockOsd is OSDMap) | ||
415 | { | ||
416 | convexBlock = convexBlockOsd as OSDMap; | ||
417 | |||
418 | if (debugDetail) | ||
419 | { | ||
420 | string keys = LogHeader + " keys found in convexBlock: "; | ||
421 | foreach (KeyValuePair<string, OSD> kvp in convexBlock) | ||
422 | keys += "'" + kvp.Key + "' "; | ||
423 | m_log.Debug(keys); | ||
424 | } | ||
425 | |||
426 | Vector3 min = new Vector3(-0.5f, -0.5f, -0.5f); | ||
427 | if (convexBlock.ContainsKey("Min")) min = convexBlock["Min"].AsVector3(); | ||
428 | Vector3 max = new Vector3(0.5f, 0.5f, 0.5f); | ||
429 | if (convexBlock.ContainsKey("Max")) max = convexBlock["Max"].AsVector3(); | ||
430 | |||
431 | List<Vector3> boundingHull = null; | ||
432 | |||
433 | if (convexBlock.ContainsKey("BoundingVerts")) | ||
434 | { | ||
435 | byte[] boundingVertsBytes = convexBlock["BoundingVerts"].AsBinary(); | ||
436 | boundingHull = new List<Vector3>(); | ||
437 | for (int i = 0; i < boundingVertsBytes.Length; ) | ||
438 | { | ||
439 | ushort uX = Utils.BytesToUInt16(boundingVertsBytes, i); i += 2; | ||
440 | ushort uY = Utils.BytesToUInt16(boundingVertsBytes, i); i += 2; | ||
441 | ushort uZ = Utils.BytesToUInt16(boundingVertsBytes, i); i += 2; | ||
442 | |||
443 | Vector3 pos = new Vector3( | ||
444 | Utils.UInt16ToFloat(uX, min.X, max.X), | ||
445 | Utils.UInt16ToFloat(uY, min.Y, max.Y), | ||
446 | Utils.UInt16ToFloat(uZ, min.Z, max.Z) | ||
447 | ); | ||
448 | |||
449 | boundingHull.Add(pos); | ||
450 | } | ||
451 | |||
452 | mBoundingHull = boundingHull; | ||
453 | if (debugDetail) m_log.DebugFormat("{0} prim='{1}': parsed bounding hull. nVerts={2}", LogHeader, primName, mBoundingHull.Count); | ||
454 | } | ||
455 | |||
456 | if (convexBlock.ContainsKey("HullList")) | ||
457 | { | ||
458 | byte[] hullList = convexBlock["HullList"].AsBinary(); | ||
459 | |||
460 | byte[] posBytes = convexBlock["Positions"].AsBinary(); | ||
461 | |||
462 | List<List<Vector3>> hulls = new List<List<Vector3>>(); | ||
463 | int posNdx = 0; | ||
464 | |||
465 | foreach (byte cnt in hullList) | ||
466 | { | ||
467 | int count = cnt == 0 ? 256 : cnt; | ||
468 | List<Vector3> hull = new List<Vector3>(); | ||
469 | |||
470 | for (int i = 0; i < count; i++) | ||
471 | { | ||
472 | ushort uX = Utils.BytesToUInt16(posBytes, posNdx); posNdx += 2; | ||
473 | ushort uY = Utils.BytesToUInt16(posBytes, posNdx); posNdx += 2; | ||
474 | ushort uZ = Utils.BytesToUInt16(posBytes, posNdx); posNdx += 2; | ||
475 | |||
476 | Vector3 pos = new Vector3( | ||
477 | Utils.UInt16ToFloat(uX, min.X, max.X), | ||
478 | Utils.UInt16ToFloat(uY, min.Y, max.Y), | ||
479 | Utils.UInt16ToFloat(uZ, min.Z, max.Z) | ||
480 | ); | ||
481 | |||
482 | hull.Add(pos); | ||
483 | } | ||
484 | |||
485 | hulls.Add(hull); | ||
486 | } | ||
487 | |||
488 | mConvexHulls = hulls; | ||
489 | if (debugDetail) m_log.DebugFormat("{0} prim='{1}': parsed hulls. nHulls={2}", LogHeader, primName, mConvexHulls.Count); | ||
490 | } | ||
491 | else | ||
492 | { | ||
493 | if (debugDetail) m_log.DebugFormat("{0} prim='{1}' has physics_convex but no HullList", LogHeader, primName); | ||
494 | } | ||
495 | } | ||
496 | } | ||
497 | catch (Exception e) | ||
498 | { | ||
499 | m_log.WarnFormat("{0} exception decoding convex block: {1}", LogHeader, e); | ||
500 | } | ||
501 | } | ||
361 | 502 | ||
362 | if (physicsParms == null) | 503 | if (physicsParms == null) |
363 | { | 504 | { |
@@ -374,34 +515,14 @@ namespace OpenSim.Region.Physics.Meshing | |||
374 | OSD decodedMeshOsd = new OSD(); | 515 | OSD decodedMeshOsd = new OSD(); |
375 | byte[] meshBytes = new byte[physSize]; | 516 | byte[] meshBytes = new byte[physSize]; |
376 | System.Buffer.BlockCopy(primShape.SculptData, physOffset, meshBytes, 0, physSize); | 517 | System.Buffer.BlockCopy(primShape.SculptData, physOffset, meshBytes, 0, physSize); |
377 | // byte[] decompressed = new byte[physSize * 5]; | 518 | // byte[] decompressed = new byte[physSize * 5]; |
378 | try | 519 | try |
379 | { | 520 | { |
380 | using (MemoryStream inMs = new MemoryStream(meshBytes)) | 521 | decodedMeshOsd = DecompressOsd(meshBytes); |
381 | { | ||
382 | using (MemoryStream outMs = new MemoryStream()) | ||
383 | { | ||
384 | using (ZOutputStream zOut = new ZOutputStream(outMs)) | ||
385 | { | ||
386 | byte[] readBuffer = new byte[2048]; | ||
387 | int readLen = 0; | ||
388 | while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0) | ||
389 | { | ||
390 | zOut.Write(readBuffer, 0, readLen); | ||
391 | } | ||
392 | zOut.Flush(); | ||
393 | outMs.Seek(0, SeekOrigin.Begin); | ||
394 | |||
395 | byte[] decompressedBuf = outMs.GetBuffer(); | ||
396 | |||
397 | decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf); | ||
398 | } | ||
399 | } | ||
400 | } | ||
401 | } | 522 | } |
402 | catch (Exception e) | 523 | catch (Exception e) |
403 | { | 524 | { |
404 | m_log.Error("[MESH]: exception decoding physical mesh: " + e.ToString()); | 525 | m_log.ErrorFormat("{0} prim='{1}': exception decoding physical mesh: {2}", LogHeader, primName, e); |
405 | return false; | 526 | return false; |
406 | } | 527 | } |
407 | 528 | ||
@@ -410,7 +531,7 @@ namespace OpenSim.Region.Physics.Meshing | |||
410 | // physics_shape is an array of OSDMaps, one for each submesh | 531 | // physics_shape is an array of OSDMaps, one for each submesh |
411 | if (decodedMeshOsd is OSDArray) | 532 | if (decodedMeshOsd is OSDArray) |
412 | { | 533 | { |
413 | // Console.WriteLine("decodedMeshOsd for {0} - {1}", primName, Util.GetFormattedXml(decodedMeshOsd)); | 534 | // Console.WriteLine("decodedMeshOsd for {0} - {1}", primName, Util.GetFormattedXml(decodedMeshOsd)); |
414 | 535 | ||
415 | decodedMeshOsdArray = (OSDArray)decodedMeshOsd; | 536 | decodedMeshOsdArray = (OSDArray)decodedMeshOsd; |
416 | foreach (OSD subMeshOsd in decodedMeshOsdArray) | 537 | foreach (OSD subMeshOsd in decodedMeshOsdArray) |
@@ -418,6 +539,9 @@ namespace OpenSim.Region.Physics.Meshing | |||
418 | if (subMeshOsd is OSDMap) | 539 | if (subMeshOsd is OSDMap) |
419 | AddSubMesh(subMeshOsd as OSDMap, size, coords, faces); | 540 | AddSubMesh(subMeshOsd as OSDMap, size, coords, faces); |
420 | } | 541 | } |
542 | if (debugDetail) | ||
543 | m_log.DebugFormat("{0} {1}: mesh decoded. offset={2}, size={3}, nCoords={4}, nFaces={5}", | ||
544 | LogHeader, primName, physOffset, physSize, coords.Count, faces.Count); | ||
421 | } | 545 | } |
422 | } | 546 | } |
423 | 547 | ||
@@ -425,6 +549,42 @@ namespace OpenSim.Region.Physics.Meshing | |||
425 | } | 549 | } |
426 | 550 | ||
427 | /// <summary> | 551 | /// <summary> |
552 | /// decompresses a gzipped OSD object | ||
553 | /// </summary> | ||
554 | /// <param name="decodedOsd"></param> the OSD object | ||
555 | /// <param name="meshBytes"></param> | ||
556 | /// <returns></returns> | ||
557 | private static OSD DecompressOsd(byte[] meshBytes) | ||
558 | { | ||
559 | OSD decodedOsd = null; | ||
560 | |||
561 | using (MemoryStream inMs = new MemoryStream(meshBytes)) | ||
562 | { | ||
563 | using (MemoryStream outMs = new MemoryStream()) | ||
564 | { | ||
565 | using (DeflateStream decompressionStream = new DeflateStream(inMs, CompressionMode.Decompress)) | ||
566 | { | ||
567 | byte[] readBuffer = new byte[2048]; | ||
568 | inMs.Read(readBuffer, 0, 2); // skip first 2 bytes in header | ||
569 | int readLen = 0; | ||
570 | |||
571 | while ((readLen = decompressionStream.Read(readBuffer, 0, readBuffer.Length)) > 0) | ||
572 | outMs.Write(readBuffer, 0, readLen); | ||
573 | |||
574 | outMs.Flush(); | ||
575 | |||
576 | outMs.Seek(0, SeekOrigin.Begin); | ||
577 | |||
578 | byte[] decompressedBuf = outMs.GetBuffer(); | ||
579 | |||
580 | decodedOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf); | ||
581 | } | ||
582 | } | ||
583 | } | ||
584 | return decodedOsd; | ||
585 | } | ||
586 | |||
587 | /// <summary> | ||
428 | /// Generate the co-ords and faces necessary to construct a mesh from the sculpt data the accompanies a prim. | 588 | /// Generate the co-ords and faces necessary to construct a mesh from the sculpt data the accompanies a prim. |
429 | /// </summary> | 589 | /// </summary> |
430 | /// <param name="primName"></param> | 590 | /// <param name="primName"></param> |
@@ -469,10 +629,11 @@ namespace OpenSim.Region.Physics.Meshing | |||
469 | 629 | ||
470 | try | 630 | try |
471 | { | 631 | { |
472 | OpenMetaverse.Imaging.ManagedImage unusedData; | 632 | OpenMetaverse.Imaging.ManagedImage managedImage; |
473 | OpenMetaverse.Imaging.OpenJPEG.DecodeToImage(primShape.SculptData, out unusedData, out idata); | 633 | |
634 | OpenMetaverse.Imaging.OpenJPEG.DecodeToImage(primShape.SculptData, out managedImage); | ||
474 | 635 | ||
475 | if (idata == null) | 636 | if (managedImage == null) |
476 | { | 637 | { |
477 | // In some cases it seems that the decode can return a null bitmap without throwing | 638 | // In some cases it seems that the decode can return a null bitmap without throwing |
478 | // an exception | 639 | // an exception |
@@ -481,9 +642,12 @@ namespace OpenSim.Region.Physics.Meshing | |||
481 | return false; | 642 | return false; |
482 | } | 643 | } |
483 | 644 | ||
484 | unusedData = null; | 645 | if ((managedImage.Channels & OpenMetaverse.Imaging.ManagedImage.ImageChannels.Alpha) != 0) |
646 | managedImage.ConvertChannels(managedImage.Channels & ~OpenMetaverse.Imaging.ManagedImage.ImageChannels.Alpha); | ||
485 | 647 | ||
486 | //idata = CSJ2K.J2kImage.FromBytes(primShape.SculptData); | 648 | Bitmap imgData = OpenMetaverse.Imaging.LoadTGAClass.LoadTGA(new MemoryStream(managedImage.ExportTGA())); |
649 | idata = (Image)imgData; | ||
650 | managedImage = null; | ||
487 | 651 | ||
488 | if (cacheSculptMaps) | 652 | if (cacheSculptMaps) |
489 | { | 653 | { |
@@ -700,6 +864,45 @@ namespace OpenSim.Region.Physics.Meshing | |||
700 | return true; | 864 | return true; |
701 | } | 865 | } |
702 | 866 | ||
867 | /// <summary> | ||
868 | /// temporary prototype code - please do not use until the interface has been finalized! | ||
869 | /// </summary> | ||
870 | /// <param name="size">value to scale the hull points by</param> | ||
871 | /// <returns>a list of vertices in the bounding hull if it exists and has been successfully decoded, otherwise null</returns> | ||
872 | public List<Vector3> GetBoundingHull(Vector3 size) | ||
873 | { | ||
874 | if (mBoundingHull == null) | ||
875 | return null; | ||
876 | |||
877 | List<Vector3> verts = new List<Vector3>(); | ||
878 | foreach (var vert in mBoundingHull) | ||
879 | verts.Add(vert * size); | ||
880 | |||
881 | return verts; | ||
882 | } | ||
883 | |||
884 | /// <summary> | ||
885 | /// temporary prototype code - please do not use until the interface has been finalized! | ||
886 | /// </summary> | ||
887 | /// <param name="size">value to scale the hull points by</param> | ||
888 | /// <returns>a list of hulls if they exist and have been successfully decoded, otherwise null</returns> | ||
889 | public List<List<Vector3>> GetConvexHulls(Vector3 size) | ||
890 | { | ||
891 | if (mConvexHulls == null) | ||
892 | return null; | ||
893 | |||
894 | List<List<Vector3>> hulls = new List<List<Vector3>>(); | ||
895 | foreach (var hull in mConvexHulls) | ||
896 | { | ||
897 | List<Vector3> verts = new List<Vector3>(); | ||
898 | foreach (var vert in hull) | ||
899 | verts.Add(vert * size); | ||
900 | hulls.Add(verts); | ||
901 | } | ||
902 | |||
903 | return hulls; | ||
904 | } | ||
905 | |||
703 | public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod) | 906 | public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod) |
704 | { | 907 | { |
705 | return CreateMesh(primName, primShape, size, lod, false, true); | 908 | return CreateMesh(primName, primShape, size, lod, false, true); |