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