aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/PhysicsModules/UbitMeshing/Meshmerizer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/PhysicsModules/UbitMeshing/Meshmerizer.cs')
-rw-r--r--OpenSim/Region/PhysicsModules/UbitMeshing/Meshmerizer.cs1424
1 files changed, 1424 insertions, 0 deletions
diff --git a/OpenSim/Region/PhysicsModules/UbitMeshing/Meshmerizer.cs b/OpenSim/Region/PhysicsModules/UbitMeshing/Meshmerizer.cs
new file mode 100644
index 0000000..c131c6f
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/UbitMeshing/Meshmerizer.cs
@@ -0,0 +1,1424 @@
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;
43using ComponentAce.Compression.Libs.zlib;
44using OpenSim.Region.Physics.ConvexDecompositionDotNet;
45using System.Runtime.Serialization;
46using System.Runtime.Serialization.Formatters.Binary;
47
48namespace OpenSim.Region.Physics.Meshing
49{
50 public class MeshmerizerPlugin : IMeshingPlugin
51 {
52 public MeshmerizerPlugin()
53 {
54 }
55
56 public string GetName()
57 {
58 return "UbitMeshmerizer";
59 }
60
61 public IMesher GetMesher(IConfigSource config)
62 {
63 return new Meshmerizer(config);
64 }
65 }
66
67 public class Meshmerizer : IMesher
68 {
69 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
70
71 // Setting baseDir to a path will enable the dumping of raw files
72 // raw files can be imported by blender so a visual inspection of the results can be done
73
74 public object diskLock = new object();
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";
84
85 private bool useMeshiesPhysicsMesh = false;
86
87 private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh
88
89 private Dictionary<AMeshKey, Mesh> m_uniqueMeshes = new Dictionary<AMeshKey, Mesh>();
90 private Dictionary<AMeshKey, Mesh> m_uniqueReleasedMeshes = new Dictionary<AMeshKey, Mesh>();
91
92 public Meshmerizer(IConfigSource config)
93 {
94 IConfig start_config = config.Configs["Startup"];
95 IConfig mesh_config = config.Configs["Mesh"];
96
97
98 float fcache = 48.0f;
99// float fcache = 0.02f;
100
101 if(mesh_config != null)
102 {
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 }
116 }
117
118 CacheExpire = TimeSpan.FromHours(fcache);
119
120 }
121
122 /// <summary>
123 /// creates a simple box mesh of the specified size. This mesh is of very low vertex count and may
124 /// be useful as a backup proxy when level of detail is not needed or when more complex meshes fail
125 /// for some reason
126 /// </summary>
127 /// <param name="minX"></param>
128 /// <param name="maxX"></param>
129 /// <param name="minY"></param>
130 /// <param name="maxY"></param>
131 /// <param name="minZ"></param>
132 /// <param name="maxZ"></param>
133 /// <returns></returns>
134 private static Mesh CreateSimpleBoxMesh(float minX, float maxX, float minY, float maxY, float minZ, float maxZ)
135 {
136 Mesh box = new Mesh();
137 List<Vertex> vertices = new List<Vertex>();
138 // bottom
139
140 vertices.Add(new Vertex(minX, maxY, minZ));
141 vertices.Add(new Vertex(maxX, maxY, minZ));
142 vertices.Add(new Vertex(maxX, minY, minZ));
143 vertices.Add(new Vertex(minX, minY, minZ));
144
145 box.Add(new Triangle(vertices[0], vertices[1], vertices[2]));
146 box.Add(new Triangle(vertices[0], vertices[2], vertices[3]));
147
148 // top
149
150 vertices.Add(new Vertex(maxX, maxY, maxZ));
151 vertices.Add(new Vertex(minX, maxY, maxZ));
152 vertices.Add(new Vertex(minX, minY, maxZ));
153 vertices.Add(new Vertex(maxX, minY, maxZ));
154
155 box.Add(new Triangle(vertices[4], vertices[5], vertices[6]));
156 box.Add(new Triangle(vertices[4], vertices[6], vertices[7]));
157
158 // sides
159
160 box.Add(new Triangle(vertices[5], vertices[0], vertices[3]));
161 box.Add(new Triangle(vertices[5], vertices[3], vertices[6]));
162
163 box.Add(new Triangle(vertices[1], vertices[0], vertices[5]));
164 box.Add(new Triangle(vertices[1], vertices[5], vertices[4]));
165
166 box.Add(new Triangle(vertices[7], vertices[1], vertices[4]));
167 box.Add(new Triangle(vertices[7], vertices[2], vertices[1]));
168
169 box.Add(new Triangle(vertices[3], vertices[2], vertices[7]));
170 box.Add(new Triangle(vertices[3], vertices[7], vertices[6]));
171
172 return box;
173 }
174
175 /// <summary>
176 /// Creates a simple bounding box mesh for a complex input mesh
177 /// </summary>
178 /// <param name="meshIn"></param>
179 /// <returns></returns>
180 private static Mesh CreateBoundingBoxMesh(Mesh meshIn)
181 {
182 float minX = float.MaxValue;
183 float maxX = float.MinValue;
184 float minY = float.MaxValue;
185 float maxY = float.MinValue;
186 float minZ = float.MaxValue;
187 float maxZ = float.MinValue;
188
189 foreach (Vector3 v in meshIn.getVertexList())
190 {
191 if (v.X < minX) minX = v.X;
192 if (v.Y < minY) minY = v.Y;
193 if (v.Z < minZ) minZ = v.Z;
194
195 if (v.X > maxX) maxX = v.X;
196 if (v.Y > maxY) maxY = v.Y;
197 if (v.Z > maxZ) maxZ = v.Z;
198 }
199
200 return CreateSimpleBoxMesh(minX, maxX, minY, maxY, minZ, maxZ);
201 }
202
203 private void ReportPrimError(string message, string primName, PrimMesh primMesh)
204 {
205 m_log.Error(message);
206 m_log.Error("\nPrim Name: " + primName);
207 m_log.Error("****** PrimMesh Parameters ******\n" + primMesh.ParamsToDisplayString());
208 }
209
210 /// <summary>
211 /// Add a submesh to an existing list of coords and faces.
212 /// </summary>
213 /// <param name="subMeshData"></param>
214 /// <param name="size">Size of entire object</param>
215 /// <param name="coords"></param>
216 /// <param name="faces"></param>
217 private void AddSubMesh(OSDMap subMeshData, List<Coord> coords, List<Face> faces)
218 {
219 // Console.WriteLine("subMeshMap for {0} - {1}", primName, Util.GetFormattedXml((OSD)subMeshMap));
220
221 // As per http://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format, some Mesh Level
222 // of Detail Blocks (maps) contain just a NoGeometry key to signal there is no
223 // geometry for this submesh.
224 if (subMeshData.ContainsKey("NoGeometry") && ((OSDBoolean)subMeshData["NoGeometry"]))
225 return;
226
227 OpenMetaverse.Vector3 posMax;
228 OpenMetaverse.Vector3 posMin;
229 if (subMeshData.ContainsKey("PositionDomain"))
230 {
231 posMax = ((OSDMap)subMeshData["PositionDomain"])["Max"].AsVector3();
232 posMin = ((OSDMap)subMeshData["PositionDomain"])["Min"].AsVector3();
233 }
234 else
235 {
236 posMax = new Vector3(0.5f, 0.5f, 0.5f);
237 posMin = new Vector3(-0.5f, -0.5f, -0.5f);
238 }
239
240 ushort faceIndexOffset = (ushort)coords.Count;
241
242 byte[] posBytes = subMeshData["Position"].AsBinary();
243 for (int i = 0; i < posBytes.Length; i += 6)
244 {
245 ushort uX = Utils.BytesToUInt16(posBytes, i);
246 ushort uY = Utils.BytesToUInt16(posBytes, i + 2);
247 ushort uZ = Utils.BytesToUInt16(posBytes, i + 4);
248
249 Coord c = new Coord(
250 Utils.UInt16ToFloat(uX, posMin.X, posMax.X),
251 Utils.UInt16ToFloat(uY, posMin.Y, posMax.Y),
252 Utils.UInt16ToFloat(uZ, posMin.Z, posMax.Z));
253
254 coords.Add(c);
255 }
256
257 byte[] triangleBytes = subMeshData["TriangleList"].AsBinary();
258 for (int i = 0; i < triangleBytes.Length; i += 6)
259 {
260 ushort v1 = (ushort)(Utils.BytesToUInt16(triangleBytes, i) + faceIndexOffset);
261 ushort v2 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 2) + faceIndexOffset);
262 ushort v3 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 4) + faceIndexOffset);
263 Face f = new Face(v1, v2, v3);
264 faces.Add(f);
265 }
266 }
267
268 /// <summary>
269 /// Create a physics mesh from data that comes with the prim. The actual data used depends on the prim type.
270 /// </summary>
271 /// <param name="primName"></param>
272 /// <param name="primShape"></param>
273 /// <param name="size"></param>
274 /// <param name="lod"></param>
275 /// <returns></returns>
276 private Mesh CreateMeshFromPrimMesher(string primName, PrimitiveBaseShape primShape, float lod, bool convex)
277 {
278// m_log.DebugFormat(
279// "[MESH]: Creating physics proxy for {0}, shape {1}",
280// primName, (OpenMetaverse.SculptType)primShape.SculptType);
281
282 List<Coord> coords;
283 List<Face> faces;
284
285 if (primShape.SculptEntry)
286 {
287 if (((OpenMetaverse.SculptType)primShape.SculptType) == SculptType.Mesh)
288 {
289 if (!useMeshiesPhysicsMesh)
290 return null;
291
292 if (!GenerateCoordsAndFacesFromPrimMeshData(primName, primShape, out coords, out faces, convex))
293 return null;
294 }
295 else
296 {
297 if (!GenerateCoordsAndFacesFromPrimSculptData(primName, primShape, lod, out coords, out faces))
298 return null;
299 }
300 }
301 else
302 {
303 if (!GenerateCoordsAndFacesFromPrimShapeData(primName, primShape, lod, out coords, out faces))
304 return null;
305 }
306
307 primShape.SculptData = Utils.EmptyBytes;
308
309 int numCoords = coords.Count;
310 int numFaces = faces.Count;
311
312 Mesh mesh = new Mesh();
313 // Add the corresponding triangles to the mesh
314 for (int i = 0; i < numFaces; i++)
315 {
316 Face f = faces[i];
317 mesh.Add(new Triangle(coords[f.v1].X, coords[f.v1].Y, coords[f.v1].Z,
318 coords[f.v2].X, coords[f.v2].Y, coords[f.v2].Z,
319 coords[f.v3].X, coords[f.v3].Y, coords[f.v3].Z));
320 }
321
322 coords.Clear();
323 faces.Clear();
324
325 return mesh;
326 }
327
328 /// <summary>
329 /// Generate the co-ords and faces necessary to construct a mesh from the mesh data the accompanies a prim.
330 /// </summary>
331 /// <param name="primName"></param>
332 /// <param name="primShape"></param>
333 /// <param name="size"></param>
334 /// <param name="coords">Coords are added to this list by the method.</param>
335 /// <param name="faces">Faces are added to this list by the method.</param>
336 /// <returns>true if coords and faces were successfully generated, false if not</returns>
337 private bool GenerateCoordsAndFacesFromPrimMeshData(
338 string primName, PrimitiveBaseShape primShape, out List<Coord> coords, out List<Face> faces, bool convex)
339 {
340// m_log.DebugFormat("[MESH]: experimental mesh proxy generation for {0}", primName);
341
342 bool usemesh = false;
343
344 coords = new List<Coord>();
345 faces = new List<Face>();
346 OSD meshOsd = null;
347
348 if (primShape.SculptData.Length <= 0)
349 {
350// m_log.InfoFormat("[MESH]: asset data for {0} is zero length", primName);
351 return false;
352 }
353
354 long start = 0;
355 using (MemoryStream data = new MemoryStream(primShape.SculptData))
356 {
357 try
358 {
359 OSD osd = OSDParser.DeserializeLLSDBinary(data);
360 if (osd is OSDMap)
361 meshOsd = (OSDMap)osd;
362 else
363 {
364 m_log.Warn("[Mesh}: unable to cast mesh asset to OSDMap");
365 return false;
366 }
367 }
368 catch (Exception e)
369 {
370 m_log.Error("[MESH]: Exception deserializing mesh asset header:" + e.ToString());
371 }
372
373 start = data.Position;
374 }
375
376 if (meshOsd is OSDMap)
377 {
378 OSDMap physicsParms = null;
379 OSDMap map = (OSDMap)meshOsd;
380
381 if (!convex)
382 {
383 if (map.ContainsKey("physics_shape"))
384 physicsParms = (OSDMap)map["physics_shape"]; // old asset format
385 else if (map.ContainsKey("physics_mesh"))
386 physicsParms = (OSDMap)map["physics_mesh"]; // new asset format
387
388 if (physicsParms != null)
389 usemesh = true;
390 }
391
392 if(!usemesh && (map.ContainsKey("physics_convex")))
393 physicsParms = (OSDMap)map["physics_convex"];
394
395
396 if (physicsParms == null)
397 {
398 m_log.Warn("[MESH]: unknown mesh type");
399 return false;
400 }
401
402 int physOffset = physicsParms["offset"].AsInteger() + (int)start;
403 int physSize = physicsParms["size"].AsInteger();
404
405 if (physOffset < 0 || physSize == 0)
406 return false; // no mesh data in asset
407
408 OSD decodedMeshOsd = new OSD();
409 byte[] meshBytes = new byte[physSize];
410 System.Buffer.BlockCopy(primShape.SculptData, physOffset, meshBytes, 0, physSize);
411
412 try
413 {
414 using (MemoryStream inMs = new MemoryStream(meshBytes))
415 {
416 using (MemoryStream outMs = new MemoryStream())
417 {
418 using (ZOutputStream zOut = new ZOutputStream(outMs))
419 {
420 byte[] readBuffer = new byte[2048];
421 int readLen = 0;
422 while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0)
423 {
424 zOut.Write(readBuffer, 0, readLen);
425 }
426 zOut.Flush();
427 outMs.Seek(0, SeekOrigin.Begin);
428
429 byte[] decompressedBuf = outMs.GetBuffer();
430
431 decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
432 }
433 }
434 }
435 }
436 catch (Exception e)
437 {
438 m_log.Error("[MESH]: exception decoding physical mesh: " + e.ToString());
439 return false;
440 }
441
442 if (usemesh)
443 {
444 OSDArray decodedMeshOsdArray = null;
445
446 // physics_shape is an array of OSDMaps, one for each submesh
447 if (decodedMeshOsd is OSDArray)
448 {
449// Console.WriteLine("decodedMeshOsd for {0} - {1}", primName, Util.GetFormattedXml(decodedMeshOsd));
450
451 decodedMeshOsdArray = (OSDArray)decodedMeshOsd;
452 foreach (OSD subMeshOsd in decodedMeshOsdArray)
453 {
454 if (subMeshOsd is OSDMap)
455 AddSubMesh(subMeshOsd as OSDMap, coords, faces);
456 }
457 }
458 }
459 else
460 {
461 OSDMap cmap = (OSDMap)decodedMeshOsd;
462 if (cmap == null)
463 return false;
464
465 byte[] data;
466
467 List<float3> vs = new List<float3>();
468 PHullResult hullr = new PHullResult();
469 float3 f3;
470 Coord c;
471 Face f;
472 Vector3 range;
473 Vector3 min;
474
475 const float invMaxU16 = 1.0f / 65535f;
476 int t1;
477 int t2;
478 int t3;
479 int i;
480 int nverts;
481 int nindexs;
482
483 if (cmap.ContainsKey("Max"))
484 range = cmap["Max"].AsVector3();
485 else
486 range = new Vector3(0.5f, 0.5f, 0.5f);
487
488 if (cmap.ContainsKey("Min"))
489 min = cmap["Min"].AsVector3();
490 else
491 min = new Vector3(-0.5f, -0.5f, -0.5f);
492
493 range = range - min;
494 range *= invMaxU16;
495
496 if (!convex && cmap.ContainsKey("HullList") && cmap.ContainsKey("Positions"))
497 {
498 List<int> hsizes = new List<int>();
499 int totalpoints = 0;
500 data = cmap["HullList"].AsBinary();
501 for (i = 0; i < data.Length; i++)
502 {
503 t1 = data[i];
504 if (t1 == 0)
505 t1 = 256;
506 totalpoints += t1;
507 hsizes.Add(t1);
508 }
509
510 data = cmap["Positions"].AsBinary();
511 int ptr = 0;
512 int vertsoffset = 0;
513
514 if (totalpoints == data.Length / 6) // 2 bytes per coord, 3 coords per point
515 {
516 foreach (int hullsize in hsizes)
517 {
518 for (i = 0; i < hullsize; i++ )
519 {
520 t1 = data[ptr++];
521 t1 += data[ptr++] << 8;
522 t2 = data[ptr++];
523 t2 += data[ptr++] << 8;
524 t3 = data[ptr++];
525 t3 += data[ptr++] << 8;
526
527 f3 = new float3((t1 * range.X + min.X),
528 (t2 * range.Y + min.Y),
529 (t3 * range.Z + min.Z));
530 vs.Add(f3);
531 }
532
533 if(hullsize <3)
534 {
535 vs.Clear();
536 continue;
537 }
538
539 if (hullsize <5)
540 {
541 foreach (float3 point in vs)
542 {
543 c.X = point.x;
544 c.Y = point.y;
545 c.Z = point.z;
546 coords.Add(c);
547 }
548 f = new Face(vertsoffset, vertsoffset + 1, vertsoffset + 2);
549 faces.Add(f);
550
551 if (hullsize == 4)
552 {
553 // not sure about orientation..
554 f = new Face(vertsoffset, vertsoffset + 2, vertsoffset + 3);
555 faces.Add(f);
556 f = new Face(vertsoffset, vertsoffset + 3, vertsoffset + 1);
557 faces.Add(f);
558 f = new Face(vertsoffset + 3, vertsoffset + 2, vertsoffset + 1);
559 faces.Add(f);
560 }
561 vertsoffset += vs.Count;
562 vs.Clear();
563 continue;
564 }
565
566 if (!HullUtils.ComputeHull(vs, ref hullr, 0, 0.0f))
567 {
568 vs.Clear();
569 continue;
570 }
571
572 nverts = hullr.Vertices.Count;
573 nindexs = hullr.Indices.Count;
574
575 if (nindexs % 3 != 0)
576 {
577 vs.Clear();
578 continue;
579 }
580
581 for (i = 0; i < nverts; i++)
582 {
583 c.X = hullr.Vertices[i].x;
584 c.Y = hullr.Vertices[i].y;
585 c.Z = hullr.Vertices[i].z;
586 coords.Add(c);
587 }
588
589 for (i = 0; i < nindexs; i += 3)
590 {
591 t1 = hullr.Indices[i];
592 if (t1 > nverts)
593 break;
594 t2 = hullr.Indices[i + 1];
595 if (t2 > nverts)
596 break;
597 t3 = hullr.Indices[i + 2];
598 if (t3 > nverts)
599 break;
600 f = new Face(vertsoffset + t1, vertsoffset + t2, vertsoffset + t3);
601 faces.Add(f);
602 }
603 vertsoffset += nverts;
604 vs.Clear();
605 }
606 }
607 if (coords.Count > 0 && faces.Count > 0)
608 return true;
609 }
610
611 vs.Clear();
612
613 if (cmap.ContainsKey("BoundingVerts"))
614 {
615 data = cmap["BoundingVerts"].AsBinary();
616
617 for (i = 0; i < data.Length; )
618 {
619 t1 = data[i++];
620 t1 += data[i++] << 8;
621 t2 = data[i++];
622 t2 += data[i++] << 8;
623 t3 = data[i++];
624 t3 += data[i++] << 8;
625
626 f3 = new float3((t1 * range.X + min.X),
627 (t2 * range.Y + min.Y),
628 (t3 * range.Z + min.Z));
629 vs.Add(f3);
630 }
631
632 if (vs.Count < 3)
633 {
634 vs.Clear();
635 return false;
636 }
637
638 if (vs.Count < 5)
639 {
640 foreach (float3 point in vs)
641 {
642 c.X = point.x;
643 c.Y = point.y;
644 c.Z = point.z;
645 coords.Add(c);
646 }
647 f = new Face(0, 1, 2);
648 faces.Add(f);
649
650 if (vs.Count == 4)
651 {
652 f = new Face(0, 2, 3);
653 faces.Add(f);
654 f = new Face(0, 3, 1);
655 faces.Add(f);
656 f = new Face( 3, 2, 1);
657 faces.Add(f);
658 }
659 vs.Clear();
660 return true;
661 }
662
663 if (!HullUtils.ComputeHull(vs, ref hullr, 0, 0.0f))
664 return false;
665
666 nverts = hullr.Vertices.Count;
667 nindexs = hullr.Indices.Count;
668
669 if (nindexs % 3 != 0)
670 return false;
671
672 for (i = 0; i < nverts; i++)
673 {
674 c.X = hullr.Vertices[i].x;
675 c.Y = hullr.Vertices[i].y;
676 c.Z = hullr.Vertices[i].z;
677 coords.Add(c);
678 }
679 for (i = 0; i < nindexs; i += 3)
680 {
681 t1 = hullr.Indices[i];
682 if (t1 > nverts)
683 break;
684 t2 = hullr.Indices[i + 1];
685 if (t2 > nverts)
686 break;
687 t3 = hullr.Indices[i + 2];
688 if (t3 > nverts)
689 break;
690 f = new Face(t1, t2, t3);
691 faces.Add(f);
692 }
693
694 if (coords.Count > 0 && faces.Count > 0)
695 return true;
696 }
697 else
698 return false;
699 }
700 }
701
702 return true;
703 }
704
705 /// <summary>
706 /// Generate the co-ords and faces necessary to construct a mesh from the sculpt data the accompanies a prim.
707 /// </summary>
708 /// <param name="primName"></param>
709 /// <param name="primShape"></param>
710 /// <param name="size"></param>
711 /// <param name="lod"></param>
712 /// <param name="coords">Coords are added to this list by the method.</param>
713 /// <param name="faces">Faces are added to this list by the method.</param>
714 /// <returns>true if coords and faces were successfully generated, false if not</returns>
715 private bool GenerateCoordsAndFacesFromPrimSculptData(
716 string primName, PrimitiveBaseShape primShape, float lod, out List<Coord> coords, out List<Face> faces)
717 {
718 coords = new List<Coord>();
719 faces = new List<Face>();
720 PrimMesher.SculptMesh sculptMesh;
721 Image idata = null;
722
723 if (primShape.SculptData == null || primShape.SculptData.Length == 0)
724 return false;
725
726 try
727 {
728 OpenMetaverse.Imaging.ManagedImage unusedData;
729 OpenMetaverse.Imaging.OpenJPEG.DecodeToImage(primShape.SculptData, out unusedData, out idata);
730
731 unusedData = null;
732
733 if (idata == null)
734 {
735 // In some cases it seems that the decode can return a null bitmap without throwing
736 // an exception
737 m_log.WarnFormat("[PHYSICS]: OpenJPEG decoded sculpt data for {0} to a null bitmap. Ignoring.", primName);
738 return false;
739 }
740 }
741 catch (DllNotFoundException)
742 {
743 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!");
744 return false;
745 }
746 catch (IndexOutOfRangeException)
747 {
748 m_log.Error("[PHYSICS]: OpenJpeg was unable to decode this. Physics Proxy generation failed");
749 return false;
750 }
751 catch (Exception ex)
752 {
753 m_log.Error("[PHYSICS]: Unable to generate a Sculpty physics proxy. Sculpty texture decode failed: " + ex.Message);
754 return false;
755 }
756
757 PrimMesher.SculptMesh.SculptType sculptType;
758 // remove mirror and invert bits
759 OpenMetaverse.SculptType pbsSculptType = ((OpenMetaverse.SculptType)(primShape.SculptType & 0x3f));
760 switch (pbsSculptType)
761 {
762 case OpenMetaverse.SculptType.Cylinder:
763 sculptType = PrimMesher.SculptMesh.SculptType.cylinder;
764 break;
765 case OpenMetaverse.SculptType.Plane:
766 sculptType = PrimMesher.SculptMesh.SculptType.plane;
767 break;
768 case OpenMetaverse.SculptType.Torus:
769 sculptType = PrimMesher.SculptMesh.SculptType.torus;
770 break;
771 case OpenMetaverse.SculptType.Sphere:
772 sculptType = PrimMesher.SculptMesh.SculptType.sphere;
773 break;
774 default:
775 sculptType = PrimMesher.SculptMesh.SculptType.plane;
776 break;
777 }
778
779 bool mirror = ((primShape.SculptType & 128) != 0);
780 bool invert = ((primShape.SculptType & 64) != 0);
781
782 sculptMesh = new PrimMesher.SculptMesh((Bitmap)idata, sculptType, (int)lod, mirror, invert);
783
784 idata.Dispose();
785
786// sculptMesh.DumpRaw(baseDir, primName, "primMesh");
787
788 coords = sculptMesh.coords;
789 faces = sculptMesh.faces;
790
791 return true;
792 }
793
794 /// <summary>
795 /// Generate the co-ords and faces necessary to construct a mesh from the shape data the accompanies a prim.
796 /// </summary>
797 /// <param name="primName"></param>
798 /// <param name="primShape"></param>
799 /// <param name="size"></param>
800 /// <param name="coords">Coords are added to this list by the method.</param>
801 /// <param name="faces">Faces are added to this list by the method.</param>
802 /// <returns>true if coords and faces were successfully generated, false if not</returns>
803 private bool GenerateCoordsAndFacesFromPrimShapeData(
804 string primName, PrimitiveBaseShape primShape, float lod, out List<Coord> coords, out List<Face> faces)
805 {
806 PrimMesh primMesh;
807 coords = new List<Coord>();
808 faces = new List<Face>();
809
810 float pathShearX = primShape.PathShearX < 128 ? (float)primShape.PathShearX * 0.01f : (float)(primShape.PathShearX - 256) * 0.01f;
811 float pathShearY = primShape.PathShearY < 128 ? (float)primShape.PathShearY * 0.01f : (float)(primShape.PathShearY - 256) * 0.01f;
812 float pathBegin = (float)primShape.PathBegin * 2.0e-5f;
813 float pathEnd = 1.0f - (float)primShape.PathEnd * 2.0e-5f;
814 float pathScaleX = (float)(primShape.PathScaleX - 100) * 0.01f;
815 float pathScaleY = (float)(primShape.PathScaleY - 100) * 0.01f;
816
817 float profileBegin = (float)primShape.ProfileBegin * 2.0e-5f;
818 float profileEnd = 1.0f - (float)primShape.ProfileEnd * 2.0e-5f;
819
820 if (profileBegin < 0.0f)
821 profileBegin = 0.0f;
822
823 if (profileEnd < 0.02f)
824 profileEnd = 0.02f;
825 else if (profileEnd > 1.0f)
826 profileEnd = 1.0f;
827
828 if (profileBegin >= profileEnd)
829 profileBegin = profileEnd - 0.02f;
830
831 float profileHollow = (float)primShape.ProfileHollow * 2.0e-5f;
832 if (profileHollow > 0.95f)
833 profileHollow = 0.95f;
834
835 int sides = 4;
836 LevelOfDetail iLOD = (LevelOfDetail)lod;
837 byte profshape = (byte)(primShape.ProfileCurve & 0x07);
838
839 if (profshape == (byte)ProfileShape.EquilateralTriangle
840 || profshape == (byte)ProfileShape.IsometricTriangle
841 || profshape == (byte)ProfileShape.RightTriangle)
842 sides = 3;
843 else if (profshape == (byte)ProfileShape.Circle)
844 {
845 switch (iLOD)
846 {
847 case LevelOfDetail.High: sides = 24; break;
848 case LevelOfDetail.Medium: sides = 12; break;
849 case LevelOfDetail.Low: sides = 6; break;
850 case LevelOfDetail.VeryLow: sides = 3; break;
851 default: sides = 24; break;
852 }
853 }
854 else if (profshape == (byte)ProfileShape.HalfCircle)
855 { // half circle, prim is a sphere
856 switch (iLOD)
857 {
858 case LevelOfDetail.High: sides = 24; break;
859 case LevelOfDetail.Medium: sides = 12; break;
860 case LevelOfDetail.Low: sides = 6; break;
861 case LevelOfDetail.VeryLow: sides = 3; break;
862 default: sides = 24; break;
863 }
864
865 profileBegin = 0.5f * profileBegin + 0.5f;
866 profileEnd = 0.5f * profileEnd + 0.5f;
867 }
868
869 int hollowSides = sides;
870 if (primShape.HollowShape == HollowShape.Circle)
871 {
872 switch (iLOD)
873 {
874 case LevelOfDetail.High: hollowSides = 24; break;
875 case LevelOfDetail.Medium: hollowSides = 12; break;
876 case LevelOfDetail.Low: hollowSides = 6; break;
877 case LevelOfDetail.VeryLow: hollowSides = 3; break;
878 default: hollowSides = 24; break;
879 }
880 }
881 else if (primShape.HollowShape == HollowShape.Square)
882 hollowSides = 4;
883 else if (primShape.HollowShape == HollowShape.Triangle)
884 {
885 if (profshape == (byte)ProfileShape.HalfCircle)
886 hollowSides = 6;
887 else
888 hollowSides = 3;
889 }
890
891 primMesh = new PrimMesh(sides, profileBegin, profileEnd, profileHollow, hollowSides);
892
893 if (primMesh.errorMessage != null)
894 if (primMesh.errorMessage.Length > 0)
895 m_log.Error("[ERROR] " + primMesh.errorMessage);
896
897 primMesh.topShearX = pathShearX;
898 primMesh.topShearY = pathShearY;
899 primMesh.pathCutBegin = pathBegin;
900 primMesh.pathCutEnd = pathEnd;
901
902 if (primShape.PathCurve == (byte)Extrusion.Straight || primShape.PathCurve == (byte) Extrusion.Flexible)
903 {
904 primMesh.twistBegin = (primShape.PathTwistBegin * 18) / 10;
905 primMesh.twistEnd = (primShape.PathTwist * 18) / 10;
906 primMesh.taperX = pathScaleX;
907 primMesh.taperY = pathScaleY;
908
909#if SPAM
910 m_log.Debug("****** PrimMesh Parameters (Linear) ******\n" + primMesh.ParamsToDisplayString());
911#endif
912 try
913 {
914 primMesh.ExtrudeLinear();
915 }
916 catch (Exception ex)
917 {
918 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
919 return false;
920 }
921 }
922 else
923 {
924 primMesh.holeSizeX = (200 - primShape.PathScaleX) * 0.01f;
925 primMesh.holeSizeY = (200 - primShape.PathScaleY) * 0.01f;
926 primMesh.radius = 0.01f * primShape.PathRadiusOffset;
927 primMesh.revolutions = 1.0f + 0.015f * primShape.PathRevolutions;
928 primMesh.skew = 0.01f * primShape.PathSkew;
929 primMesh.twistBegin = (primShape.PathTwistBegin * 36) / 10;
930 primMesh.twistEnd = (primShape.PathTwist * 36) / 10;
931 primMesh.taperX = primShape.PathTaperX * 0.01f;
932 primMesh.taperY = primShape.PathTaperY * 0.01f;
933
934#if SPAM
935 m_log.Debug("****** PrimMesh Parameters (Circular) ******\n" + primMesh.ParamsToDisplayString());
936#endif
937 try
938 {
939 primMesh.ExtrudeCircular();
940 }
941 catch (Exception ex)
942 {
943 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
944 return false;
945 }
946 }
947
948// primMesh.DumpRaw(baseDir, primName, "primMesh");
949
950 coords = primMesh.coords;
951 faces = primMesh.faces;
952
953 return true;
954 }
955
956 public AMeshKey GetMeshUniqueKey(PrimitiveBaseShape primShape, Vector3 size, byte lod, bool convex)
957 {
958 AMeshKey key = new AMeshKey();
959 Byte[] someBytes;
960
961 key.hashB = 5181;
962 key.hashC = 5181;
963 ulong hash = 5381;
964
965 if (primShape.SculptEntry)
966 {
967 key.uuid = primShape.SculptTexture;
968 key.hashC = mdjb2(key.hashC, primShape.SculptType);
969 key.hashC = mdjb2(key.hashC, primShape.PCode);
970 }
971 else
972 {
973 hash = mdjb2(hash, primShape.PathCurve);
974 hash = mdjb2(hash, (byte)primShape.HollowShape);
975 hash = mdjb2(hash, (byte)primShape.ProfileShape);
976 hash = mdjb2(hash, primShape.PathBegin);
977 hash = mdjb2(hash, primShape.PathEnd);
978 hash = mdjb2(hash, primShape.PathScaleX);
979 hash = mdjb2(hash, primShape.PathScaleY);
980 hash = mdjb2(hash, primShape.PathShearX);
981 key.hashA = hash;
982 hash = key.hashB;
983 hash = mdjb2(hash, primShape.PathShearY);
984 hash = mdjb2(hash, (byte)primShape.PathTwist);
985 hash = mdjb2(hash, (byte)primShape.PathTwistBegin);
986 hash = mdjb2(hash, (byte)primShape.PathRadiusOffset);
987 hash = mdjb2(hash, (byte)primShape.PathTaperX);
988 hash = mdjb2(hash, (byte)primShape.PathTaperY);
989 hash = mdjb2(hash, primShape.PathRevolutions);
990 hash = mdjb2(hash, (byte)primShape.PathSkew);
991 hash = mdjb2(hash, primShape.ProfileBegin);
992 hash = mdjb2(hash, primShape.ProfileEnd);
993 hash = mdjb2(hash, primShape.ProfileHollow);
994 hash = mdjb2(hash, primShape.PCode);
995 key.hashB = hash;
996 }
997
998 hash = key.hashC;
999
1000 hash = mdjb2(hash, lod);
1001
1002 if (size == m_MeshUnitSize)
1003 {
1004 hash = hash << 8;
1005 hash |= 8;
1006 }
1007 else
1008 {
1009 someBytes = size.GetBytes();
1010 for (int i = 0; i < someBytes.Length; i++)
1011 hash = mdjb2(hash, someBytes[i]);
1012 hash = hash << 8;
1013 }
1014
1015 if (convex)
1016 hash |= 4;
1017
1018 if (primShape.SculptEntry)
1019 {
1020 hash |= 1;
1021 if (primShape.SculptType == (byte)SculptType.Mesh)
1022 hash |= 2;
1023 }
1024
1025 key.hashC = hash;
1026
1027 return key;
1028 }
1029
1030 private ulong mdjb2(ulong hash, byte c)
1031 {
1032 return ((hash << 5) + hash) + (ulong)c;
1033 }
1034
1035 private ulong mdjb2(ulong hash, ushort c)
1036 {
1037 hash = ((hash << 5) + hash) + (ulong)((byte)c);
1038 return ((hash << 5) + hash) + (ulong)(c >> 8);
1039 }
1040
1041 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
1042 {
1043 return CreateMesh(primName, primShape, size, lod, false,false,false);
1044 }
1045
1046 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
1047 {
1048 return CreateMesh(primName, primShape, size, lod, false,false,false);
1049 }
1050
1051 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache, bool convex, bool forOde)
1052 {
1053 return CreateMesh(primName, primShape, size, lod, false, false, false);
1054 }
1055
1056 public IMesh GetMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex)
1057 {
1058 Mesh mesh = null;
1059
1060 if (size.X < 0.01f) size.X = 0.01f;
1061 if (size.Y < 0.01f) size.Y = 0.01f;
1062 if (size.Z < 0.01f) size.Z = 0.01f;
1063
1064 AMeshKey key = GetMeshUniqueKey(primShape, size, (byte)lod, convex);
1065 lock (m_uniqueMeshes)
1066 {
1067 m_uniqueMeshes.TryGetValue(key, out mesh);
1068
1069 if (mesh != null)
1070 {
1071 mesh.RefCount++;
1072 return mesh;
1073 }
1074
1075 // try to find a identical mesh on meshs recently released
1076 lock (m_uniqueReleasedMeshes)
1077 {
1078 m_uniqueReleasedMeshes.TryGetValue(key, out mesh);
1079 if (mesh != null)
1080 {
1081 m_uniqueReleasedMeshes.Remove(key);
1082 try
1083 {
1084 m_uniqueMeshes.Add(key, mesh);
1085 }
1086 catch { }
1087 mesh.RefCount = 1;
1088 return mesh;
1089 }
1090 }
1091 }
1092 return null;
1093 }
1094
1095 private static Vector3 m_MeshUnitSize = new Vector3(1.0f, 1.0f, 1.0f);
1096
1097 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex, bool forOde)
1098 {
1099#if SPAM
1100 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName);
1101#endif
1102
1103 Mesh mesh = null;
1104
1105 if (size.X < 0.01f) size.X = 0.01f;
1106 if (size.Y < 0.01f) size.Y = 0.01f;
1107 if (size.Z < 0.01f) size.Z = 0.01f;
1108
1109 // try to find a identical mesh on meshs in use
1110
1111 AMeshKey key = GetMeshUniqueKey(primShape,size,(byte)lod, convex);
1112
1113 lock (m_uniqueMeshes)
1114 {
1115 m_uniqueMeshes.TryGetValue(key, out mesh);
1116
1117 if (mesh != null)
1118 {
1119 mesh.RefCount++;
1120 return mesh;
1121 }
1122
1123 // try to find a identical mesh on meshs recently released
1124 lock (m_uniqueReleasedMeshes)
1125 {
1126 m_uniqueReleasedMeshes.TryGetValue(key, out mesh);
1127 if (mesh != null)
1128 {
1129 m_uniqueReleasedMeshes.Remove(key);
1130 try
1131 {
1132 m_uniqueMeshes.Add(key, mesh);
1133 }
1134 catch { }
1135 mesh.RefCount = 1;
1136 return mesh;
1137 }
1138 }
1139 }
1140
1141 Mesh UnitMesh = null;
1142 AMeshKey unitKey = GetMeshUniqueKey(primShape, m_MeshUnitSize, (byte)lod, convex);
1143
1144 lock (m_uniqueReleasedMeshes)
1145 {
1146 m_uniqueReleasedMeshes.TryGetValue(unitKey, out UnitMesh);
1147 if (UnitMesh != null)
1148 {
1149 UnitMesh.RefCount = 1;
1150 }
1151 }
1152
1153 if (UnitMesh == null && primShape.SculptEntry && doMeshFileCache)
1154 UnitMesh = GetFromFileCache(unitKey);
1155
1156 if (UnitMesh == null)
1157 {
1158 UnitMesh = CreateMeshFromPrimMesher(primName, primShape, lod, convex);
1159
1160 if (UnitMesh == null)
1161 return null;
1162
1163 UnitMesh.DumpRaw(baseDir, unitKey.ToString(), "Z");
1164
1165 if (forOde)
1166 {
1167 // force pinned mem allocation
1168 UnitMesh.PrepForOde();
1169 }
1170 else
1171 UnitMesh.TrimExcess();
1172
1173 UnitMesh.Key = unitKey;
1174 UnitMesh.RefCount = 1;
1175
1176 if (doMeshFileCache && primShape.SculptEntry)
1177 StoreToFileCache(unitKey, UnitMesh);
1178
1179 lock (m_uniqueReleasedMeshes)
1180 {
1181 try
1182 {
1183 m_uniqueReleasedMeshes.Add(unitKey, UnitMesh);
1184 }
1185 catch { }
1186 }
1187 }
1188
1189 mesh = UnitMesh.Scale(size);
1190 mesh.Key = key;
1191 mesh.RefCount = 1;
1192 lock (m_uniqueMeshes)
1193 {
1194 try
1195 {
1196 m_uniqueMeshes.Add(key, mesh);
1197 }
1198 catch { }
1199 }
1200
1201 return mesh;
1202 }
1203
1204 public void ReleaseMesh(IMesh imesh)
1205 {
1206 if (imesh == null)
1207 return;
1208
1209 Mesh mesh = (Mesh)imesh;
1210
1211 lock (m_uniqueMeshes)
1212 {
1213 int curRefCount = mesh.RefCount;
1214 curRefCount--;
1215
1216 if (curRefCount > 0)
1217 {
1218 mesh.RefCount = curRefCount;
1219 return;
1220 }
1221
1222 mesh.RefCount = 0;
1223 m_uniqueMeshes.Remove(mesh.Key);
1224 lock (m_uniqueReleasedMeshes)
1225 {
1226 try
1227 {
1228 m_uniqueReleasedMeshes.Add(mesh.Key, mesh);
1229 }
1230 catch { }
1231 }
1232 }
1233 }
1234
1235 public void ExpireReleaseMeshs()
1236 {
1237 if (m_uniqueReleasedMeshes.Count == 0)
1238 return;
1239
1240 List<Mesh> meshstodelete = new List<Mesh>();
1241 int refcntr;
1242
1243 lock (m_uniqueReleasedMeshes)
1244 {
1245 foreach (Mesh m in m_uniqueReleasedMeshes.Values)
1246 {
1247 refcntr = m.RefCount;
1248 refcntr--;
1249 if (refcntr > -6)
1250 m.RefCount = refcntr;
1251 else
1252 meshstodelete.Add(m);
1253 }
1254
1255 foreach (Mesh m in meshstodelete)
1256 {
1257 m_uniqueReleasedMeshes.Remove(m.Key);
1258 m.releaseBuildingMeshData();
1259 m.releasePinned();
1260 }
1261 }
1262 }
1263
1264 public void FileNames(AMeshKey key, out string dir,out string fullFileName)
1265 {
1266 string id = key.ToString();
1267 string init = id.Substring(0, 1);
1268 dir = System.IO.Path.Combine(cachePath, init);
1269 fullFileName = System.IO.Path.Combine(dir, id);
1270 }
1271
1272 public string FullFileName(AMeshKey key)
1273 {
1274 string id = key.ToString();
1275 string init = id.Substring(0,1);
1276 id = System.IO.Path.Combine(init, id);
1277 id = System.IO.Path.Combine(cachePath, id);
1278 return id;
1279 }
1280
1281 private Mesh GetFromFileCache(AMeshKey key)
1282 {
1283 Mesh mesh = null;
1284 string filename = FullFileName(key);
1285 bool ok = true;
1286
1287 lock (diskLock)
1288 {
1289 if (File.Exists(filename))
1290 {
1291 FileStream stream = null;
1292 try
1293 {
1294 stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
1295 BinaryFormatter bformatter = new BinaryFormatter();
1296
1297 mesh = Mesh.FromStream(stream, key);
1298
1299 }
1300 catch (Exception e)
1301 {
1302 ok = false;
1303 m_log.ErrorFormat(
1304 "[MESH CACHE]: Failed to get file {0}. Exception {1} {2}",
1305 filename, e.Message, e.StackTrace);
1306 }
1307
1308 if (stream != null)
1309 stream.Close();
1310
1311 if (mesh == null || !ok)
1312 File.Delete(filename);
1313 else
1314 File.SetLastAccessTimeUtc(filename, DateTime.UtcNow);
1315 }
1316 }
1317
1318 return mesh;
1319 }
1320
1321 private void StoreToFileCache(AMeshKey key, Mesh mesh)
1322 {
1323 Stream stream = null;
1324 bool ok = false;
1325
1326 // Make sure the target cache directory exists
1327 string dir = String.Empty;
1328 string filename = String.Empty;
1329
1330 FileNames(key, out dir, out filename);
1331
1332 lock (diskLock)
1333 {
1334 try
1335 {
1336 if (!Directory.Exists(dir))
1337 {
1338 Directory.CreateDirectory(dir);
1339 }
1340
1341 stream = File.Open(filename, FileMode.Create);
1342 ok = mesh.ToStream(stream);
1343 }
1344 catch (IOException e)
1345 {
1346 m_log.ErrorFormat(
1347 "[MESH CACHE]: Failed to write file {0}. Exception {1} {2}.",
1348 filename, e.Message, e.StackTrace);
1349 ok = false;
1350 }
1351
1352 if (stream != null)
1353 stream.Close();
1354
1355 if (File.Exists(filename))
1356 {
1357 if (ok)
1358 File.SetLastAccessTimeUtc(filename, DateTime.UtcNow);
1359 else
1360 File.Delete(filename);
1361 }
1362 }
1363 }
1364
1365 public void ExpireFileCache()
1366 {
1367 if (!doCacheExpire)
1368 return;
1369
1370 string controlfile = System.IO.Path.Combine(cachePath, "cntr");
1371
1372 lock (diskLock)
1373 {
1374 try
1375 {
1376 if (File.Exists(controlfile))
1377 {
1378 int ndeleted = 0;
1379 int totalfiles = 0;
1380 int ndirs = 0;
1381 DateTime OlderTime = File.GetLastAccessTimeUtc(controlfile) - CacheExpire;
1382 File.SetLastAccessTimeUtc(controlfile, DateTime.UtcNow);
1383
1384 foreach (string dir in Directory.GetDirectories(cachePath))
1385 {
1386 try
1387 {
1388 foreach (string file in Directory.GetFiles(dir))
1389 {
1390 try
1391 {
1392 if (File.GetLastAccessTimeUtc(file) < OlderTime)
1393 {
1394 File.Delete(file);
1395 ndeleted++;
1396 }
1397 }
1398 catch { }
1399 totalfiles++;
1400 }
1401 }
1402 catch { }
1403 ndirs++;
1404 }
1405
1406 if (ndeleted == 0)
1407 m_log.InfoFormat("[MESH CACHE]: {0} Files in {1} cache folders, no expires",
1408 totalfiles,ndirs);
1409 else
1410 m_log.InfoFormat("[MESH CACHE]: {0} Files in {1} cache folders, expired {2} files accessed before {3}",
1411 totalfiles,ndirs, ndeleted, OlderTime.ToString());
1412 }
1413 else
1414 {
1415 m_log.Info("[MESH CACHE]: Expire delayed to next startup");
1416 FileStream fs = File.Create(controlfile,4096,FileOptions.WriteThrough);
1417 fs.Close();
1418 }
1419 }
1420 catch { }
1421 }
1422 }
1423 }
1424}