Meshmerizer.cs 58 KB

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