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