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