diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs | 501 |
1 files changed, 365 insertions, 136 deletions
diff --git a/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs b/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs index 4c40175..29fdda4 100644 --- a/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs +++ b/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs | |||
@@ -42,6 +42,8 @@ using System.Reflection; | |||
42 | using System.IO; | 42 | using System.IO; |
43 | using ComponentAce.Compression.Libs.zlib; | 43 | using ComponentAce.Compression.Libs.zlib; |
44 | using OpenSim.Region.Physics.ConvexDecompositionDotNet; | 44 | using OpenSim.Region.Physics.ConvexDecompositionDotNet; |
45 | using System.Runtime.Serialization; | ||
46 | using System.Runtime.Serialization.Formatters.Binary; | ||
45 | 47 | ||
46 | namespace OpenSim.Region.Physics.Meshing | 48 | namespace OpenSim.Region.Physics.Meshing |
47 | { | 49 | { |
@@ -68,22 +70,22 @@ namespace OpenSim.Region.Physics.Meshing | |||
68 | 70 | ||
69 | // Setting baseDir to a path will enable the dumping of raw files | 71 | // Setting baseDir to a path will enable the dumping of raw files |
70 | // raw files can be imported by blender so a visual inspection of the results can be done | 72 | // raw files can be imported by blender so a visual inspection of the results can be done |
71 | #if SPAM | ||
72 | const string baseDir = "rawFiles"; | ||
73 | #else | ||
74 | private const string baseDir = null; //"rawFiles"; | ||
75 | #endif | ||
76 | 73 | ||
77 | private bool cacheSculptMaps = true; | 74 | public object diskLock = new object(); |
78 | private bool cacheSculptAlphaMaps = true; | 75 | |
76 | public bool doMeshFileCache = true; | ||
77 | |||
78 | public string cachePath = "MeshCache"; | ||
79 | public TimeSpan CacheExpire; | ||
80 | public bool doCacheExpire = true; | ||
81 | |||
82 | // const string baseDir = "rawFiles"; | ||
83 | private const string baseDir = null; //"rawFiles"; | ||
79 | 84 | ||
80 | private string decodedSculptMapPath = null; | ||
81 | private bool useMeshiesPhysicsMesh = false; | 85 | private bool useMeshiesPhysicsMesh = false; |
82 | 86 | ||
83 | private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh | 87 | private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh |
84 | 88 | ||
85 | // private Dictionary<ulong, Mesh> m_uniqueMeshes = new Dictionary<ulong, Mesh>(); | ||
86 | // private Dictionary<ulong, Mesh> m_uniqueReleasedMeshes = new Dictionary<ulong, Mesh>(); | ||
87 | private Dictionary<AMeshKey, Mesh> m_uniqueMeshes = new Dictionary<AMeshKey, Mesh>(); | 89 | private Dictionary<AMeshKey, Mesh> m_uniqueMeshes = new Dictionary<AMeshKey, Mesh>(); |
88 | private Dictionary<AMeshKey, Mesh> m_uniqueReleasedMeshes = new Dictionary<AMeshKey, Mesh>(); | 90 | private Dictionary<AMeshKey, Mesh> m_uniqueReleasedMeshes = new Dictionary<AMeshKey, Mesh>(); |
89 | 91 | ||
@@ -92,29 +94,29 @@ namespace OpenSim.Region.Physics.Meshing | |||
92 | IConfig start_config = config.Configs["Startup"]; | 94 | IConfig start_config = config.Configs["Startup"]; |
93 | IConfig mesh_config = config.Configs["Mesh"]; | 95 | IConfig mesh_config = config.Configs["Mesh"]; |
94 | 96 | ||
95 | decodedSculptMapPath = start_config.GetString("DecodedSculptMapPath","j2kDecodeCache"); | ||
96 | 97 | ||
97 | cacheSculptMaps = start_config.GetBoolean("CacheSculptMaps", cacheSculptMaps); | 98 | float fcache = 48.0f; |
99 | // float fcache = 0.02f; | ||
98 | 100 | ||
99 | if (Environment.OSVersion.Platform == PlatformID.Unix) | 101 | if(mesh_config != null) |
100 | { | 102 | { |
101 | cacheSculptAlphaMaps = false; | 103 | useMeshiesPhysicsMesh = mesh_config.GetBoolean("UseMeshiesPhysicsMesh", useMeshiesPhysicsMesh); |
104 | if (useMeshiesPhysicsMesh) | ||
105 | { | ||
106 | doMeshFileCache = mesh_config.GetBoolean("MeshFileCache", doMeshFileCache); | ||
107 | cachePath = mesh_config.GetString("MeshFileCachePath", cachePath); | ||
108 | fcache = mesh_config.GetFloat("MeshFileCacheExpireHours", fcache); | ||
109 | doCacheExpire = mesh_config.GetBoolean("MeshFileCacheDoExpire", doCacheExpire); | ||
110 | } | ||
111 | else | ||
112 | { | ||
113 | doMeshFileCache = false; | ||
114 | doCacheExpire = false; | ||
115 | } | ||
102 | } | 116 | } |
103 | else | ||
104 | cacheSculptAlphaMaps = cacheSculptMaps; | ||
105 | 117 | ||
106 | if(mesh_config != null) | 118 | CacheExpire = TimeSpan.FromHours(fcache); |
107 | useMeshiesPhysicsMesh = mesh_config.GetBoolean("UseMeshiesPhysicsMesh", useMeshiesPhysicsMesh); | ||
108 | 119 | ||
109 | try | ||
110 | { | ||
111 | if (!Directory.Exists(decodedSculptMapPath)) | ||
112 | Directory.CreateDirectory(decodedSculptMapPath); | ||
113 | } | ||
114 | catch (Exception e) | ||
115 | { | ||
116 | m_log.WarnFormat("[SCULPT]: Unable to create {0} directory: ", decodedSculptMapPath, e.Message); | ||
117 | } | ||
118 | } | 120 | } |
119 | 121 | ||
120 | /// <summary> | 122 | /// <summary> |
@@ -212,7 +214,7 @@ namespace OpenSim.Region.Physics.Meshing | |||
212 | /// <param name="size">Size of entire object</param> | 214 | /// <param name="size">Size of entire object</param> |
213 | /// <param name="coords"></param> | 215 | /// <param name="coords"></param> |
214 | /// <param name="faces"></param> | 216 | /// <param name="faces"></param> |
215 | private void AddSubMesh(OSDMap subMeshData, Vector3 size, List<Coord> coords, List<Face> faces) | 217 | private void AddSubMesh(OSDMap subMeshData, List<Coord> coords, List<Face> faces) |
216 | { | 218 | { |
217 | // Console.WriteLine("subMeshMap for {0} - {1}", primName, Util.GetFormattedXml((OSD)subMeshMap)); | 219 | // Console.WriteLine("subMeshMap for {0} - {1}", primName, Util.GetFormattedXml((OSD)subMeshMap)); |
218 | 220 | ||
@@ -245,9 +247,9 @@ namespace OpenSim.Region.Physics.Meshing | |||
245 | ushort uZ = Utils.BytesToUInt16(posBytes, i + 4); | 247 | ushort uZ = Utils.BytesToUInt16(posBytes, i + 4); |
246 | 248 | ||
247 | Coord c = new Coord( | 249 | Coord c = new Coord( |
248 | Utils.UInt16ToFloat(uX, posMin.X, posMax.X) * size.X, | 250 | Utils.UInt16ToFloat(uX, posMin.X, posMax.X), |
249 | Utils.UInt16ToFloat(uY, posMin.Y, posMax.Y) * size.Y, | 251 | Utils.UInt16ToFloat(uY, posMin.Y, posMax.Y), |
250 | Utils.UInt16ToFloat(uZ, posMin.Z, posMax.Z) * size.Z); | 252 | Utils.UInt16ToFloat(uZ, posMin.Z, posMax.Z)); |
251 | 253 | ||
252 | coords.Add(c); | 254 | coords.Add(c); |
253 | } | 255 | } |
@@ -271,7 +273,7 @@ namespace OpenSim.Region.Physics.Meshing | |||
271 | /// <param name="size"></param> | 273 | /// <param name="size"></param> |
272 | /// <param name="lod"></param> | 274 | /// <param name="lod"></param> |
273 | /// <returns></returns> | 275 | /// <returns></returns> |
274 | private Mesh CreateMeshFromPrimMesher(string primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool convex) | 276 | private Mesh CreateMeshFromPrimMesher(string primName, PrimitiveBaseShape primShape, float lod, bool convex) |
275 | { | 277 | { |
276 | // m_log.DebugFormat( | 278 | // m_log.DebugFormat( |
277 | // "[MESH]: Creating physics proxy for {0}, shape {1}", | 279 | // "[MESH]: Creating physics proxy for {0}, shape {1}", |
@@ -287,18 +289,18 @@ namespace OpenSim.Region.Physics.Meshing | |||
287 | if (!useMeshiesPhysicsMesh) | 289 | if (!useMeshiesPhysicsMesh) |
288 | return null; | 290 | return null; |
289 | 291 | ||
290 | if (!GenerateCoordsAndFacesFromPrimMeshData(primName, primShape, size, out coords, out faces, convex)) | 292 | if (!GenerateCoordsAndFacesFromPrimMeshData(primName, primShape, out coords, out faces, convex)) |
291 | return null; | 293 | return null; |
292 | } | 294 | } |
293 | else | 295 | else |
294 | { | 296 | { |
295 | if (!GenerateCoordsAndFacesFromPrimSculptData(primName, primShape, size, lod, out coords, out faces)) | 297 | if (!GenerateCoordsAndFacesFromPrimSculptData(primName, primShape, lod, out coords, out faces)) |
296 | return null; | 298 | return null; |
297 | } | 299 | } |
298 | } | 300 | } |
299 | else | 301 | else |
300 | { | 302 | { |
301 | if (!GenerateCoordsAndFacesFromPrimShapeData(primName, primShape, size, lod, out coords, out faces)) | 303 | if (!GenerateCoordsAndFacesFromPrimShapeData(primName, primShape, lod, out coords, out faces)) |
302 | return null; | 304 | return null; |
303 | } | 305 | } |
304 | 306 | ||
@@ -333,7 +335,7 @@ namespace OpenSim.Region.Physics.Meshing | |||
333 | /// <param name="faces">Faces are added to this list by the method.</param> | 335 | /// <param name="faces">Faces are added to this list by the method.</param> |
334 | /// <returns>true if coords and faces were successfully generated, false if not</returns> | 336 | /// <returns>true if coords and faces were successfully generated, false if not</returns> |
335 | private bool GenerateCoordsAndFacesFromPrimMeshData( | 337 | private bool GenerateCoordsAndFacesFromPrimMeshData( |
336 | string primName, PrimitiveBaseShape primShape, Vector3 size, out List<Coord> coords, out List<Face> faces, bool convex) | 338 | string primName, PrimitiveBaseShape primShape, out List<Coord> coords, out List<Face> faces, bool convex) |
337 | { | 339 | { |
338 | // m_log.DebugFormat("[MESH]: experimental mesh proxy generation for {0}", primName); | 340 | // m_log.DebugFormat("[MESH]: experimental mesh proxy generation for {0}", primName); |
339 | 341 | ||
@@ -345,7 +347,7 @@ namespace OpenSim.Region.Physics.Meshing | |||
345 | 347 | ||
346 | if (primShape.SculptData.Length <= 0) | 348 | if (primShape.SculptData.Length <= 0) |
347 | { | 349 | { |
348 | m_log.InfoFormat("[MESH]: asset data for {0} is zero length", primName); | 350 | // m_log.InfoFormat("[MESH]: asset data for {0} is zero length", primName); |
349 | return false; | 351 | return false; |
350 | } | 352 | } |
351 | 353 | ||
@@ -406,7 +408,7 @@ namespace OpenSim.Region.Physics.Meshing | |||
406 | OSD decodedMeshOsd = new OSD(); | 408 | OSD decodedMeshOsd = new OSD(); |
407 | byte[] meshBytes = new byte[physSize]; | 409 | byte[] meshBytes = new byte[physSize]; |
408 | System.Buffer.BlockCopy(primShape.SculptData, physOffset, meshBytes, 0, physSize); | 410 | System.Buffer.BlockCopy(primShape.SculptData, physOffset, meshBytes, 0, physSize); |
409 | // byte[] decompressed = new byte[physSize * 5]; | 411 | |
410 | try | 412 | try |
411 | { | 413 | { |
412 | using (MemoryStream inMs = new MemoryStream(meshBytes)) | 414 | using (MemoryStream inMs = new MemoryStream(meshBytes)) |
@@ -444,13 +446,13 @@ namespace OpenSim.Region.Physics.Meshing | |||
444 | // physics_shape is an array of OSDMaps, one for each submesh | 446 | // physics_shape is an array of OSDMaps, one for each submesh |
445 | if (decodedMeshOsd is OSDArray) | 447 | if (decodedMeshOsd is OSDArray) |
446 | { | 448 | { |
447 | // Console.WriteLine("decodedMeshOsd for {0} - {1}", primName, Util.GetFormattedXml(decodedMeshOsd)); | 449 | // Console.WriteLine("decodedMeshOsd for {0} - {1}", primName, Util.GetFormattedXml(decodedMeshOsd)); |
448 | 450 | ||
449 | decodedMeshOsdArray = (OSDArray)decodedMeshOsd; | 451 | decodedMeshOsdArray = (OSDArray)decodedMeshOsd; |
450 | foreach (OSD subMeshOsd in decodedMeshOsdArray) | 452 | foreach (OSD subMeshOsd in decodedMeshOsdArray) |
451 | { | 453 | { |
452 | if (subMeshOsd is OSDMap) | 454 | if (subMeshOsd is OSDMap) |
453 | AddSubMesh(subMeshOsd as OSDMap, size, coords, faces); | 455 | AddSubMesh(subMeshOsd as OSDMap, coords, faces); |
454 | } | 456 | } |
455 | } | 457 | } |
456 | } | 458 | } |
@@ -522,9 +524,9 @@ namespace OpenSim.Region.Physics.Meshing | |||
522 | t3 = data[ptr++]; | 524 | t3 = data[ptr++]; |
523 | t3 += data[ptr++] << 8; | 525 | t3 += data[ptr++] << 8; |
524 | 526 | ||
525 | f3 = new float3((t1 * range.X + min.X) * size.X, | 527 | f3 = new float3((t1 * range.X + min.X), |
526 | (t2 * range.Y + min.Y) * size.Y, | 528 | (t2 * range.Y + min.Y), |
527 | (t3 * range.Z + min.Z) * size.Z); | 529 | (t3 * range.Z + min.Z)); |
528 | vs.Add(f3); | 530 | vs.Add(f3); |
529 | } | 531 | } |
530 | 532 | ||
@@ -621,9 +623,9 @@ namespace OpenSim.Region.Physics.Meshing | |||
621 | t3 = data[i++]; | 623 | t3 = data[i++]; |
622 | t3 += data[i++] << 8; | 624 | t3 += data[i++] << 8; |
623 | 625 | ||
624 | f3 = new float3((t1 * range.X + min.X) * size.X, | 626 | f3 = new float3((t1 * range.X + min.X), |
625 | (t2 * range.Y + min.Y) * size.Y, | 627 | (t2 * range.Y + min.Y), |
626 | (t3 * range.Z + min.Z) * size.Z); | 628 | (t3 * range.Z + min.Z)); |
627 | vs.Add(f3); | 629 | vs.Add(f3); |
628 | } | 630 | } |
629 | 631 | ||
@@ -711,35 +713,13 @@ namespace OpenSim.Region.Physics.Meshing | |||
711 | /// <param name="faces">Faces are added to this list by the method.</param> | 713 | /// <param name="faces">Faces are added to this list by the method.</param> |
712 | /// <returns>true if coords and faces were successfully generated, false if not</returns> | 714 | /// <returns>true if coords and faces were successfully generated, false if not</returns> |
713 | private bool GenerateCoordsAndFacesFromPrimSculptData( | 715 | private bool GenerateCoordsAndFacesFromPrimSculptData( |
714 | string primName, PrimitiveBaseShape primShape, Vector3 size, float lod, out List<Coord> coords, out List<Face> faces) | 716 | string primName, PrimitiveBaseShape primShape, float lod, out List<Coord> coords, out List<Face> faces) |
715 | { | 717 | { |
716 | coords = new List<Coord>(); | 718 | coords = new List<Coord>(); |
717 | faces = new List<Face>(); | 719 | faces = new List<Face>(); |
718 | PrimMesher.SculptMesh sculptMesh; | 720 | PrimMesher.SculptMesh sculptMesh; |
719 | Image idata = null; | 721 | Image idata = null; |
720 | string decodedSculptFileName = ""; | ||
721 | |||
722 | if (cacheSculptMaps && primShape.SculptTexture != UUID.Zero) | ||
723 | { | ||
724 | decodedSculptFileName = System.IO.Path.Combine(decodedSculptMapPath, "smap_" + primShape.SculptTexture.ToString()); | ||
725 | try | ||
726 | { | ||
727 | if (File.Exists(decodedSculptFileName)) | ||
728 | { | ||
729 | idata = Image.FromFile(decodedSculptFileName); | ||
730 | } | ||
731 | } | ||
732 | catch (Exception e) | ||
733 | { | ||
734 | m_log.Error("[SCULPT]: unable to load cached sculpt map " + decodedSculptFileName + " " + e.Message); | ||
735 | 722 | ||
736 | } | ||
737 | //if (idata != null) | ||
738 | // m_log.Debug("[SCULPT]: loaded cached map asset for map ID: " + primShape.SculptTexture.ToString()); | ||
739 | } | ||
740 | |||
741 | if (idata == null) | ||
742 | { | ||
743 | if (primShape.SculptData == null || primShape.SculptData.Length == 0) | 723 | if (primShape.SculptData == null || primShape.SculptData.Length == 0) |
744 | return false; | 724 | return false; |
745 | 725 | ||
@@ -748,25 +728,15 @@ namespace OpenSim.Region.Physics.Meshing | |||
748 | OpenMetaverse.Imaging.ManagedImage unusedData; | 728 | OpenMetaverse.Imaging.ManagedImage unusedData; |
749 | OpenMetaverse.Imaging.OpenJPEG.DecodeToImage(primShape.SculptData, out unusedData, out idata); | 729 | OpenMetaverse.Imaging.OpenJPEG.DecodeToImage(primShape.SculptData, out unusedData, out idata); |
750 | 730 | ||
731 | unusedData = null; | ||
732 | |||
751 | if (idata == null) | 733 | if (idata == null) |
752 | { | 734 | { |
753 | // In some cases it seems that the decode can return a null bitmap without throwing | 735 | // In some cases it seems that the decode can return a null bitmap without throwing |
754 | // an exception | 736 | // an exception |
755 | m_log.WarnFormat("[PHYSICS]: OpenJPEG decoded sculpt data for {0} to a null bitmap. Ignoring.", primName); | 737 | m_log.WarnFormat("[PHYSICS]: OpenJPEG decoded sculpt data for {0} to a null bitmap. Ignoring.", primName); |
756 | |||
757 | return false; | 738 | return false; |
758 | } | 739 | } |
759 | |||
760 | unusedData = null; | ||
761 | |||
762 | //idata = CSJ2K.J2kImage.FromBytes(primShape.SculptData); | ||
763 | |||
764 | if (cacheSculptMaps && (cacheSculptAlphaMaps || (((ImageFlags)(idata.Flags) & ImageFlags.HasAlpha) ==0))) | ||
765 | // don't cache images with alpha channel in linux since mono can't load them correctly) | ||
766 | { | ||
767 | try { idata.Save(decodedSculptFileName, ImageFormat.MemoryBmp); } | ||
768 | catch (Exception e) { m_log.Error("[SCULPT]: unable to cache sculpt map " + decodedSculptFileName + " " + e.Message); } | ||
769 | } | ||
770 | } | 740 | } |
771 | catch (DllNotFoundException) | 741 | catch (DllNotFoundException) |
772 | { | 742 | { |
@@ -783,7 +753,6 @@ namespace OpenSim.Region.Physics.Meshing | |||
783 | m_log.Error("[PHYSICS]: Unable to generate a Sculpty physics proxy. Sculpty texture decode failed: " + ex.Message); | 753 | m_log.Error("[PHYSICS]: Unable to generate a Sculpty physics proxy. Sculpty texture decode failed: " + ex.Message); |
784 | return false; | 754 | return false; |
785 | } | 755 | } |
786 | } | ||
787 | 756 | ||
788 | PrimMesher.SculptMesh.SculptType sculptType; | 757 | PrimMesher.SculptMesh.SculptType sculptType; |
789 | // remove mirror and invert bits | 758 | // remove mirror and invert bits |
@@ -814,9 +783,7 @@ namespace OpenSim.Region.Physics.Meshing | |||
814 | 783 | ||
815 | idata.Dispose(); | 784 | idata.Dispose(); |
816 | 785 | ||
817 | sculptMesh.DumpRaw(baseDir, primName, "primMesh"); | 786 | // sculptMesh.DumpRaw(baseDir, primName, "primMesh"); |
818 | |||
819 | sculptMesh.Scale(size.X, size.Y, size.Z); | ||
820 | 787 | ||
821 | coords = sculptMesh.coords; | 788 | coords = sculptMesh.coords; |
822 | faces = sculptMesh.faces; | 789 | faces = sculptMesh.faces; |
@@ -834,7 +801,7 @@ namespace OpenSim.Region.Physics.Meshing | |||
834 | /// <param name="faces">Faces are added to this list by the method.</param> | 801 | /// <param name="faces">Faces are added to this list by the method.</param> |
835 | /// <returns>true if coords and faces were successfully generated, false if not</returns> | 802 | /// <returns>true if coords and faces were successfully generated, false if not</returns> |
836 | private bool GenerateCoordsAndFacesFromPrimShapeData( | 803 | private bool GenerateCoordsAndFacesFromPrimShapeData( |
837 | string primName, PrimitiveBaseShape primShape, Vector3 size, float lod, out List<Coord> coords, out List<Face> faces) | 804 | string primName, PrimitiveBaseShape primShape, float lod, out List<Coord> coords, out List<Face> faces) |
838 | { | 805 | { |
839 | PrimMesh primMesh; | 806 | PrimMesh primMesh; |
840 | coords = new List<Coord>(); | 807 | coords = new List<Coord>(); |
@@ -969,9 +936,7 @@ namespace OpenSim.Region.Physics.Meshing | |||
969 | } | 936 | } |
970 | } | 937 | } |
971 | 938 | ||
972 | primMesh.DumpRaw(baseDir, primName, "primMesh"); | 939 | // primMesh.DumpRaw(baseDir, primName, "primMesh"); |
973 | |||
974 | primMesh.Scale(size.X, size.Y, size.Z); | ||
975 | 940 | ||
976 | coords = primMesh.coords; | 941 | coords = primMesh.coords; |
977 | faces = primMesh.faces; | 942 | faces = primMesh.faces; |
@@ -985,12 +950,14 @@ namespace OpenSim.Region.Physics.Meshing | |||
985 | Byte[] someBytes; | 950 | Byte[] someBytes; |
986 | 951 | ||
987 | key.hashB = 5181; | 952 | key.hashB = 5181; |
953 | key.hashC = 5181; | ||
988 | ulong hash = 5381; | 954 | ulong hash = 5381; |
989 | 955 | ||
990 | if (primShape.SculptEntry) | 956 | if (primShape.SculptEntry) |
991 | { | 957 | { |
992 | key.uuid = primShape.SculptTexture; | 958 | key.uuid = primShape.SculptTexture; |
993 | key.hashB = mdjb2(key.hashB, primShape.SculptType); | 959 | key.hashC = mdjb2(key.hashC, primShape.SculptType); |
960 | key.hashC = mdjb2(key.hashC, primShape.PCode); | ||
994 | } | 961 | } |
995 | else | 962 | else |
996 | { | 963 | { |
@@ -1002,6 +969,8 @@ namespace OpenSim.Region.Physics.Meshing | |||
1002 | hash = mdjb2(hash, primShape.PathScaleX); | 969 | hash = mdjb2(hash, primShape.PathScaleX); |
1003 | hash = mdjb2(hash, primShape.PathScaleY); | 970 | hash = mdjb2(hash, primShape.PathScaleY); |
1004 | hash = mdjb2(hash, primShape.PathShearX); | 971 | hash = mdjb2(hash, primShape.PathShearX); |
972 | key.hashA = hash; | ||
973 | hash = key.hashB; | ||
1005 | hash = mdjb2(hash, primShape.PathShearY); | 974 | hash = mdjb2(hash, primShape.PathShearY); |
1006 | hash = mdjb2(hash, (byte)primShape.PathTwist); | 975 | hash = mdjb2(hash, (byte)primShape.PathTwist); |
1007 | hash = mdjb2(hash, (byte)primShape.PathTwistBegin); | 976 | hash = mdjb2(hash, (byte)primShape.PathTwistBegin); |
@@ -1013,26 +982,38 @@ namespace OpenSim.Region.Physics.Meshing | |||
1013 | hash = mdjb2(hash, primShape.ProfileBegin); | 982 | hash = mdjb2(hash, primShape.ProfileBegin); |
1014 | hash = mdjb2(hash, primShape.ProfileEnd); | 983 | hash = mdjb2(hash, primShape.ProfileEnd); |
1015 | hash = mdjb2(hash, primShape.ProfileHollow); | 984 | hash = mdjb2(hash, primShape.ProfileHollow); |
1016 | key.hashA = hash; | 985 | hash = mdjb2(hash, primShape.PCode); |
986 | key.hashB = hash; | ||
1017 | } | 987 | } |
1018 | 988 | ||
1019 | hash = key.hashB; | 989 | hash = key.hashC; |
1020 | |||
1021 | someBytes = size.GetBytes(); | ||
1022 | for (int i = 0; i < someBytes.Length; i++) | ||
1023 | hash = mdjb2(hash, someBytes[i]); | ||
1024 | 990 | ||
1025 | hash = mdjb2(hash, lod); | 991 | hash = mdjb2(hash, lod); |
1026 | 992 | ||
1027 | hash &= 0x3fffffffffffffff; | 993 | if (size == m_MeshUnitSize) |
994 | { | ||
995 | hash = hash << 8; | ||
996 | hash |= 8; | ||
997 | } | ||
998 | else | ||
999 | { | ||
1000 | someBytes = size.GetBytes(); | ||
1001 | for (int i = 0; i < someBytes.Length; i++) | ||
1002 | hash = mdjb2(hash, someBytes[i]); | ||
1003 | hash = hash << 8; | ||
1004 | } | ||
1028 | 1005 | ||
1029 | if (convex) | 1006 | if (convex) |
1030 | hash |= 0x4000000000000000; | 1007 | hash |= 4; |
1031 | 1008 | ||
1032 | if (primShape.SculptEntry) | 1009 | if (primShape.SculptEntry) |
1033 | hash |= 0x8000000000000000; | 1010 | { |
1011 | hash |= 1; | ||
1012 | if (primShape.SculptType == (byte)SculptType.Mesh) | ||
1013 | hash |= 2; | ||
1014 | } | ||
1034 | 1015 | ||
1035 | key.hashB = hash; | 1016 | key.hashC = hash; |
1036 | 1017 | ||
1037 | return key; | 1018 | return key; |
1038 | } | 1019 | } |
@@ -1048,35 +1029,74 @@ namespace OpenSim.Region.Physics.Meshing | |||
1048 | return ((hash << 5) + hash) + (ulong)(c >> 8); | 1029 | return ((hash << 5) + hash) + (ulong)(c >> 8); |
1049 | } | 1030 | } |
1050 | 1031 | ||
1051 | |||
1052 | public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod) | 1032 | public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod) |
1053 | { | 1033 | { |
1054 | return CreateMesh(primName, primShape, size, lod, false,false); | 1034 | return CreateMesh(primName, primShape, size, lod, false,false,false); |
1055 | } | 1035 | } |
1056 | 1036 | ||
1057 | public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical) | 1037 | public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical) |
1058 | { | 1038 | { |
1059 | return CreateMesh(primName, primShape, size, lod, false,false); | 1039 | return CreateMesh(primName, primShape, size, lod, false,false,false); |
1060 | } | 1040 | } |
1061 | 1041 | ||
1062 | private static Vector3 m_MeshUnitSize = new Vector3(0.5f, 0.5f, 0.5f); | 1042 | public IMesh GetMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex) |
1043 | { | ||
1044 | Mesh mesh = null; | ||
1045 | |||
1046 | if (size.X < 0.01f) size.X = 0.01f; | ||
1047 | if (size.Y < 0.01f) size.Y = 0.01f; | ||
1048 | if (size.Z < 0.01f) size.Z = 0.01f; | ||
1049 | |||
1050 | AMeshKey key = GetMeshUniqueKey(primShape, size, (byte)lod, convex); | ||
1051 | lock (m_uniqueMeshes) | ||
1052 | { | ||
1053 | m_uniqueMeshes.TryGetValue(key, out mesh); | ||
1054 | |||
1055 | if (mesh != null) | ||
1056 | { | ||
1057 | mesh.RefCount++; | ||
1058 | return mesh; | ||
1059 | } | ||
1060 | } | ||
1061 | |||
1062 | // try to find a identical mesh on meshs recently released | ||
1063 | lock (m_uniqueReleasedMeshes) | ||
1064 | { | ||
1065 | m_uniqueReleasedMeshes.TryGetValue(key, out mesh); | ||
1066 | if (mesh != null) | ||
1067 | { | ||
1068 | m_uniqueReleasedMeshes.Remove(key); | ||
1069 | lock (m_uniqueMeshes) | ||
1070 | { | ||
1071 | try | ||
1072 | { | ||
1073 | m_uniqueMeshes.Add(key, mesh); | ||
1074 | } | ||
1075 | catch { } | ||
1076 | } | ||
1077 | mesh.RefCount = 1; | ||
1078 | return mesh; | ||
1079 | } | ||
1080 | } | ||
1081 | return null; | ||
1082 | } | ||
1063 | 1083 | ||
1064 | public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex) | 1084 | private static Vector3 m_MeshUnitSize = new Vector3(1.0f, 1.0f, 1.0f); |
1085 | |||
1086 | public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex, bool forOde) | ||
1065 | { | 1087 | { |
1066 | #if SPAM | 1088 | #if SPAM |
1067 | m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName); | 1089 | m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName); |
1068 | #endif | 1090 | #endif |
1069 | 1091 | ||
1070 | Mesh mesh = null; | 1092 | Mesh mesh = null; |
1071 | // ulong key = 0; | ||
1072 | |||
1073 | 1093 | ||
1074 | if (size.X < 0.01f) size.X = 0.01f; | 1094 | if (size.X < 0.01f) size.X = 0.01f; |
1075 | if (size.Y < 0.01f) size.Y = 0.01f; | 1095 | if (size.Y < 0.01f) size.Y = 0.01f; |
1076 | if (size.Z < 0.01f) size.Z = 0.01f; | 1096 | if (size.Z < 0.01f) size.Z = 0.01f; |
1077 | 1097 | ||
1078 | // try to find a identical mesh on meshs in use | 1098 | // try to find a identical mesh on meshs in use |
1079 | // key = primShape.GetMeshKey(size, lod, convex); | 1099 | |
1080 | AMeshKey key = GetMeshUniqueKey(primShape,size,(byte)lod, convex); | 1100 | AMeshKey key = GetMeshUniqueKey(primShape,size,(byte)lod, convex); |
1081 | 1101 | ||
1082 | lock (m_uniqueMeshes) | 1102 | lock (m_uniqueMeshes) |
@@ -1098,35 +1118,78 @@ namespace OpenSim.Region.Physics.Meshing | |||
1098 | { | 1118 | { |
1099 | m_uniqueReleasedMeshes.Remove(key); | 1119 | m_uniqueReleasedMeshes.Remove(key); |
1100 | lock (m_uniqueMeshes) | 1120 | lock (m_uniqueMeshes) |
1101 | m_uniqueMeshes.Add(key, mesh); | 1121 | { |
1122 | try | ||
1123 | { | ||
1124 | m_uniqueMeshes.Add(key, mesh); | ||
1125 | } | ||
1126 | catch { } | ||
1127 | } | ||
1102 | mesh.RefCount = 1; | 1128 | mesh.RefCount = 1; |
1103 | return mesh; | 1129 | return mesh; |
1104 | } | 1130 | } |
1105 | } | 1131 | } |
1106 | 1132 | ||
1107 | mesh = CreateMeshFromPrimMesher(primName, primShape, size, lod,convex); | 1133 | Mesh UnitMesh = null; |
1134 | AMeshKey unitKey = GetMeshUniqueKey(primShape, m_MeshUnitSize, (byte)lod, convex); | ||
1108 | 1135 | ||
1109 | if (mesh != null) | 1136 | lock (m_uniqueReleasedMeshes) |
1110 | { | 1137 | { |
1111 | if ((!isPhysical) && size.X < minSizeForComplexMesh && size.Y < minSizeForComplexMesh && size.Z < minSizeForComplexMesh) | 1138 | m_uniqueReleasedMeshes.TryGetValue(unitKey, out UnitMesh); |
1139 | if (UnitMesh != null) | ||
1112 | { | 1140 | { |
1113 | #if SPAM | 1141 | UnitMesh.RefCount = 1; |
1114 | m_log.Debug("Meshmerizer: prim " + primName + " has a size of " + size.ToString() + " which is below threshold of " + | ||
1115 | minSizeForComplexMesh.ToString() + " - creating simple bounding box"); | ||
1116 | #endif | ||
1117 | mesh = CreateBoundingBoxMesh(mesh); | ||
1118 | mesh.DumpRaw(baseDir, primName, "Z extruded"); | ||
1119 | } | 1142 | } |
1143 | } | ||
1120 | 1144 | ||
1121 | // trim the vertex and triangle lists to free up memory | 1145 | if (UnitMesh == null && primShape.SculptEntry && doMeshFileCache) |
1122 | mesh.TrimExcess(); | 1146 | UnitMesh = GetFromFileCache(unitKey); |
1123 | mesh.Key = key; | ||
1124 | mesh.RefCount = 1; | ||
1125 | 1147 | ||
1126 | lock(m_uniqueMeshes) | 1148 | if (UnitMesh == null) |
1127 | m_uniqueMeshes.Add(key, mesh); | 1149 | { |
1150 | UnitMesh = CreateMeshFromPrimMesher(primName, primShape, lod, convex); | ||
1151 | |||
1152 | if (UnitMesh == null) | ||
1153 | return null; | ||
1154 | |||
1155 | UnitMesh.DumpRaw(baseDir, unitKey.ToString(), "Z"); | ||
1156 | |||
1157 | if (forOde) | ||
1158 | { | ||
1159 | // force pinned mem allocation | ||
1160 | UnitMesh.PrepForOde(); | ||
1161 | } | ||
1162 | else | ||
1163 | UnitMesh.TrimExcess(); | ||
1164 | |||
1165 | UnitMesh.Key = unitKey; | ||
1166 | UnitMesh.RefCount = 1; | ||
1167 | |||
1168 | if (doMeshFileCache && primShape.SculptEntry) | ||
1169 | StoreToFileCache(unitKey, UnitMesh); | ||
1170 | |||
1171 | lock (m_uniqueReleasedMeshes) | ||
1172 | { | ||
1173 | try | ||
1174 | { | ||
1175 | m_uniqueReleasedMeshes.Add(unitKey, UnitMesh); | ||
1176 | } | ||
1177 | catch { } | ||
1178 | } | ||
1128 | } | 1179 | } |
1129 | 1180 | ||
1181 | mesh = UnitMesh.Scale(size); | ||
1182 | mesh.Key = key; | ||
1183 | mesh.RefCount = 1; | ||
1184 | lock (m_uniqueMeshes) | ||
1185 | { | ||
1186 | try | ||
1187 | { | ||
1188 | m_uniqueMeshes.Add(key, mesh); | ||
1189 | } | ||
1190 | catch { } | ||
1191 | } | ||
1192 | |||
1130 | return mesh; | 1193 | return mesh; |
1131 | } | 1194 | } |
1132 | 1195 | ||
@@ -1137,21 +1200,27 @@ namespace OpenSim.Region.Physics.Meshing | |||
1137 | 1200 | ||
1138 | Mesh mesh = (Mesh)imesh; | 1201 | Mesh mesh = (Mesh)imesh; |
1139 | 1202 | ||
1140 | int curRefCount = mesh.RefCount; | ||
1141 | curRefCount--; | ||
1142 | |||
1143 | if (curRefCount > 0) | ||
1144 | { | ||
1145 | mesh.RefCount = curRefCount; | ||
1146 | return; | ||
1147 | } | ||
1148 | |||
1149 | lock (m_uniqueMeshes) | 1203 | lock (m_uniqueMeshes) |
1150 | { | 1204 | { |
1205 | int curRefCount = mesh.RefCount; | ||
1206 | curRefCount--; | ||
1207 | |||
1208 | if (curRefCount > 0) | ||
1209 | { | ||
1210 | mesh.RefCount = curRefCount; | ||
1211 | return; | ||
1212 | } | ||
1213 | |||
1151 | mesh.RefCount = 0; | 1214 | mesh.RefCount = 0; |
1152 | m_uniqueMeshes.Remove(mesh.Key); | 1215 | m_uniqueMeshes.Remove(mesh.Key); |
1153 | lock (m_uniqueReleasedMeshes) | 1216 | lock (m_uniqueReleasedMeshes) |
1154 | m_uniqueReleasedMeshes.Add(mesh.Key, mesh); | 1217 | { |
1218 | try | ||
1219 | { | ||
1220 | m_uniqueReleasedMeshes.Add(mesh.Key, mesh); | ||
1221 | } | ||
1222 | catch { } | ||
1223 | } | ||
1155 | } | 1224 | } |
1156 | } | 1225 | } |
1157 | 1226 | ||
@@ -1178,10 +1247,170 @@ namespace OpenSim.Region.Physics.Meshing | |||
1178 | foreach (Mesh m in meshstodelete) | 1247 | foreach (Mesh m in meshstodelete) |
1179 | { | 1248 | { |
1180 | m_uniqueReleasedMeshes.Remove(m.Key); | 1249 | m_uniqueReleasedMeshes.Remove(m.Key); |
1181 | m.releaseSourceMeshData(); | 1250 | m.releaseBuildingMeshData(); |
1182 | m.releasePinned(); | 1251 | m.releasePinned(); |
1183 | } | 1252 | } |
1184 | } | 1253 | } |
1185 | } | 1254 | } |
1255 | |||
1256 | public void FileNames(AMeshKey key, out string dir,out string fullFileName) | ||
1257 | { | ||
1258 | string id = key.ToString(); | ||
1259 | string init = id.Substring(0, 1); | ||
1260 | dir = System.IO.Path.Combine(cachePath, init); | ||
1261 | fullFileName = System.IO.Path.Combine(dir, id); | ||
1262 | } | ||
1263 | |||
1264 | public string FullFileName(AMeshKey key) | ||
1265 | { | ||
1266 | string id = key.ToString(); | ||
1267 | string init = id.Substring(0,1); | ||
1268 | id = System.IO.Path.Combine(init, id); | ||
1269 | id = System.IO.Path.Combine(cachePath, id); | ||
1270 | return id; | ||
1271 | } | ||
1272 | |||
1273 | private Mesh GetFromFileCache(AMeshKey key) | ||
1274 | { | ||
1275 | Mesh mesh = null; | ||
1276 | string filename = FullFileName(key); | ||
1277 | bool ok = true; | ||
1278 | |||
1279 | lock (diskLock) | ||
1280 | { | ||
1281 | if (File.Exists(filename)) | ||
1282 | { | ||
1283 | FileStream stream = null; | ||
1284 | try | ||
1285 | { | ||
1286 | stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read); | ||
1287 | BinaryFormatter bformatter = new BinaryFormatter(); | ||
1288 | |||
1289 | mesh = Mesh.FromStream(stream, key); | ||
1290 | |||
1291 | } | ||
1292 | catch (Exception e) | ||
1293 | { | ||
1294 | ok = false; | ||
1295 | m_log.ErrorFormat( | ||
1296 | "[MESH CACHE]: Failed to get file {0}. Exception {1} {2}", | ||
1297 | filename, e.Message, e.StackTrace); | ||
1298 | } | ||
1299 | |||
1300 | if (stream != null) | ||
1301 | stream.Close(); | ||
1302 | |||
1303 | if (mesh == null || !ok) | ||
1304 | File.Delete(filename); | ||
1305 | else | ||
1306 | File.SetLastAccessTimeUtc(filename, DateTime.UtcNow); | ||
1307 | } | ||
1308 | } | ||
1309 | |||
1310 | return mesh; | ||
1311 | } | ||
1312 | |||
1313 | private void StoreToFileCache(AMeshKey key, Mesh mesh) | ||
1314 | { | ||
1315 | Stream stream = null; | ||
1316 | bool ok = false; | ||
1317 | |||
1318 | // Make sure the target cache directory exists | ||
1319 | string dir = String.Empty; | ||
1320 | string filename = String.Empty; | ||
1321 | |||
1322 | FileNames(key, out dir, out filename); | ||
1323 | |||
1324 | lock (diskLock) | ||
1325 | { | ||
1326 | try | ||
1327 | { | ||
1328 | if (!Directory.Exists(dir)) | ||
1329 | { | ||
1330 | Directory.CreateDirectory(dir); | ||
1331 | } | ||
1332 | |||
1333 | stream = File.Open(filename, FileMode.Create); | ||
1334 | ok = mesh.ToStream(stream); | ||
1335 | } | ||
1336 | catch (IOException e) | ||
1337 | { | ||
1338 | m_log.ErrorFormat( | ||
1339 | "[MESH CACHE]: Failed to write file {0}. Exception {1} {2}.", | ||
1340 | filename, e.Message, e.StackTrace); | ||
1341 | ok = false; | ||
1342 | } | ||
1343 | |||
1344 | if (stream != null) | ||
1345 | stream.Close(); | ||
1346 | |||
1347 | if (File.Exists(filename)) | ||
1348 | { | ||
1349 | if (ok) | ||
1350 | File.SetLastAccessTimeUtc(filename, DateTime.UtcNow); | ||
1351 | else | ||
1352 | File.Delete(filename); | ||
1353 | } | ||
1354 | } | ||
1355 | } | ||
1356 | |||
1357 | public void ExpireFileCache() | ||
1358 | { | ||
1359 | if (!doCacheExpire) | ||
1360 | return; | ||
1361 | |||
1362 | string controlfile = System.IO.Path.Combine(cachePath, "cntr"); | ||
1363 | |||
1364 | lock (diskLock) | ||
1365 | { | ||
1366 | try | ||
1367 | { | ||
1368 | if (File.Exists(controlfile)) | ||
1369 | { | ||
1370 | int ndeleted = 0; | ||
1371 | int totalfiles = 0; | ||
1372 | int ndirs = 0; | ||
1373 | DateTime OlderTime = File.GetLastAccessTimeUtc(controlfile) - CacheExpire; | ||
1374 | File.SetLastAccessTimeUtc(controlfile, DateTime.UtcNow); | ||
1375 | |||
1376 | foreach (string dir in Directory.GetDirectories(cachePath)) | ||
1377 | { | ||
1378 | try | ||
1379 | { | ||
1380 | foreach (string file in Directory.GetFiles(dir)) | ||
1381 | { | ||
1382 | try | ||
1383 | { | ||
1384 | if (File.GetLastAccessTimeUtc(file) < OlderTime) | ||
1385 | { | ||
1386 | File.Delete(file); | ||
1387 | ndeleted++; | ||
1388 | } | ||
1389 | } | ||
1390 | catch { } | ||
1391 | totalfiles++; | ||
1392 | } | ||
1393 | } | ||
1394 | catch { } | ||
1395 | ndirs++; | ||
1396 | } | ||
1397 | |||
1398 | if (ndeleted == 0) | ||
1399 | m_log.InfoFormat("[MESH CACHE]: {0} Files in {1} cache folders, no expires", | ||
1400 | totalfiles,ndirs); | ||
1401 | else | ||
1402 | m_log.InfoFormat("[MESH CACHE]: {0} Files in {1} cache folders, expired {2} files accessed before {3}", | ||
1403 | totalfiles,ndirs, ndeleted, OlderTime.ToString()); | ||
1404 | } | ||
1405 | else | ||
1406 | { | ||
1407 | m_log.Info("[MESH CACHE]: Expire delayed to next startup"); | ||
1408 | FileStream fs = File.Create(controlfile,4096,FileOptions.WriteThrough); | ||
1409 | fs.Close(); | ||
1410 | } | ||
1411 | } | ||
1412 | catch { } | ||
1413 | } | ||
1414 | } | ||
1186 | } | 1415 | } |
1187 | } | 1416 | } |