BSShapeCollection.cs 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015
  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 copyrightD
  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. using System;
  28. using System.Collections.Generic;
  29. using System.Text;
  30. using OMV = OpenMetaverse;
  31. using OpenSim.Framework;
  32. using OpenSim.Region.Physics.Manager;
  33. using OpenSim.Region.Physics.ConvexDecompositionDotNet;
  34. namespace OpenSim.Region.Physics.BulletSNPlugin
  35. {
  36. public sealed class BSShapeCollection : IDisposable
  37. {
  38. private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]";
  39. private BSScene PhysicsScene { get; set; }
  40. private Object m_collectionActivityLock = new Object();
  41. // Description of a Mesh
  42. private struct MeshDesc
  43. {
  44. public Object ptr;
  45. public int referenceCount;
  46. public DateTime lastReferenced;
  47. public UInt64 shapeKey;
  48. }
  49. // Description of a hull.
  50. // Meshes and hulls have the same shape hash key but we only need hulls for efficient collision calculations.
  51. private struct HullDesc
  52. {
  53. public Object ptr;
  54. public int referenceCount;
  55. public DateTime lastReferenced;
  56. public UInt64 shapeKey;
  57. }
  58. // The sharable set of meshes and hulls. Indexed by their shape hash.
  59. private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>();
  60. private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>();
  61. private bool DDetail = false;
  62. public BSShapeCollection(BSScene physScene)
  63. {
  64. PhysicsScene = physScene;
  65. // Set the next to 'true' for very detailed shape update detailed logging (detailed details?)
  66. // While detailed debugging is still active, this is better than commenting out all the
  67. // DetailLog statements. When debugging slows down, this and the protected logging
  68. // statements can be commented/removed.
  69. DDetail = true;
  70. }
  71. public void Dispose()
  72. {
  73. // TODO!!!!!!!!!
  74. }
  75. // Callbacks called just before either the body or shape is destroyed.
  76. // Mostly used for changing bodies out from under Linksets.
  77. // Useful for other cases where parameters need saving.
  78. // Passing 'null' says no callback.
  79. public delegate void ShapeDestructionCallback(BulletShape shape);
  80. public delegate void BodyDestructionCallback(BulletBody body);
  81. // Called to update/change the body and shape for an object.
  82. // First checks the shape and updates that if necessary then makes
  83. // sure the body is of the right type.
  84. // Return 'true' if either the body or the shape changed.
  85. // 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before
  86. // the current shape or body is destroyed. This allows the caller to remove any
  87. // higher level dependencies on the shape or body. Mostly used for LinkSets to
  88. // remove the physical constraints before the body is destroyed.
  89. // Called at taint-time!!
  90. public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim,
  91. ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
  92. {
  93. PhysicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape");
  94. bool ret = false;
  95. // This lock could probably be pushed down lower but building shouldn't take long
  96. lock (m_collectionActivityLock)
  97. {
  98. // Do we have the correct geometry for this type of object?
  99. // Updates prim.BSShape with information/pointers to shape.
  100. // Returns 'true' of BSShape is changed to a new shape.
  101. bool newGeom = CreateGeom(forceRebuild, prim, shapeCallback);
  102. // If we had to select a new shape geometry for the object,
  103. // rebuild the body around it.
  104. // Updates prim.BSBody with information/pointers to requested body
  105. // Returns 'true' if BSBody was changed.
  106. bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World,
  107. prim.PhysShape, bodyCallback);
  108. ret = newGeom || newBody;
  109. }
  110. DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}",
  111. prim.LocalID, forceRebuild, ret, prim.PhysBody, prim.PhysShape);
  112. return ret;
  113. }
  114. public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim)
  115. {
  116. return GetBodyAndShape(forceRebuild, sim, prim, null, null);
  117. }
  118. // Track another user of a body.
  119. // We presume the caller has allocated the body.
  120. // Bodies only have one user so the body is just put into the world if not already there.
  121. public void ReferenceBody(BulletBody body, bool inTaintTime)
  122. {
  123. lock (m_collectionActivityLock)
  124. {
  125. if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body);
  126. PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.ReferenceBody", delegate()
  127. {
  128. if (!BulletSimAPI.IsInWorld2(PhysicsScene.World.ptr, body.ptr))
  129. {
  130. BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr);
  131. if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
  132. }
  133. });
  134. }
  135. }
  136. // Release the usage of a body.
  137. // Called when releasing use of a BSBody. BSShape is handled separately.
  138. public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback )
  139. {
  140. if (!body.HasPhysicalBody)
  141. return;
  142. lock (m_collectionActivityLock)
  143. {
  144. PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceBody", delegate()
  145. {
  146. if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1},inTaintTime={2}",
  147. body.ID, body, inTaintTime);
  148. // If the caller needs to know the old body is going away, pass the event up.
  149. if (bodyCallback != null) bodyCallback(body);
  150. if (BulletSimAPI.IsInWorld2(PhysicsScene.World.ptr, body.ptr))
  151. {
  152. BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
  153. if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body);
  154. }
  155. // Zero any reference to the shape so it is not freed when the body is deleted.
  156. BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, null);
  157. BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
  158. });
  159. }
  160. }
  161. // Track the datastructures and use count for a shape.
  162. // When creating a hull, this is called first to reference the mesh
  163. // and then again to reference the hull.
  164. // Meshes and hulls for the same shape have the same hash key.
  165. // NOTE that native shapes are not added to the mesh list or removed.
  166. // Returns 'true' if this is the initial reference to the shape. Otherwise reused.
  167. public bool ReferenceShape(BulletShape shape)
  168. {
  169. bool ret = false;
  170. switch (shape.type)
  171. {
  172. case BSPhysicsShapeType.SHAPE_MESH:
  173. MeshDesc meshDesc;
  174. if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
  175. {
  176. // There is an existing instance of this mesh.
  177. meshDesc.referenceCount++;
  178. if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}",
  179. BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
  180. }
  181. else
  182. {
  183. // This is a new reference to a mesh
  184. meshDesc.ptr = shape.ptr;
  185. meshDesc.shapeKey = shape.shapeKey;
  186. // We keep a reference to the underlying IMesh data so a hull can be built
  187. meshDesc.referenceCount = 1;
  188. if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}",
  189. BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
  190. ret = true;
  191. }
  192. meshDesc.lastReferenced = System.DateTime.Now;
  193. Meshes[shape.shapeKey] = meshDesc;
  194. break;
  195. case BSPhysicsShapeType.SHAPE_HULL:
  196. HullDesc hullDesc;
  197. if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
  198. {
  199. // There is an existing instance of this hull.
  200. hullDesc.referenceCount++;
  201. if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}",
  202. BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
  203. }
  204. else
  205. {
  206. // This is a new reference to a hull
  207. hullDesc.ptr = shape.ptr;
  208. hullDesc.shapeKey = shape.shapeKey;
  209. hullDesc.referenceCount = 1;
  210. if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}",
  211. BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
  212. ret = true;
  213. }
  214. hullDesc.lastReferenced = System.DateTime.Now;
  215. Hulls[shape.shapeKey] = hullDesc;
  216. break;
  217. case BSPhysicsShapeType.SHAPE_UNKNOWN:
  218. break;
  219. default:
  220. // Native shapes are not tracked and they don't go into any list
  221. break;
  222. }
  223. return ret;
  224. }
  225. // Release the usage of a shape.
  226. public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback)
  227. {
  228. if (!shape.HasPhysicalShape)
  229. return;
  230. PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceShape", delegate()
  231. {
  232. if (shape.HasPhysicalShape)
  233. {
  234. if (shape.isNativeShape)
  235. {
  236. // Native shapes are not tracked and are released immediately
  237. if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
  238. BSScene.DetailLogZero, shape.ptr.ToString(), inTaintTime);
  239. if (shapeCallback != null) shapeCallback(shape);
  240. BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
  241. }
  242. else
  243. {
  244. switch (shape.type)
  245. {
  246. case BSPhysicsShapeType.SHAPE_HULL:
  247. DereferenceHull(shape, shapeCallback);
  248. break;
  249. case BSPhysicsShapeType.SHAPE_MESH:
  250. DereferenceMesh(shape, shapeCallback);
  251. break;
  252. case BSPhysicsShapeType.SHAPE_COMPOUND:
  253. DereferenceCompound(shape, shapeCallback);
  254. break;
  255. case BSPhysicsShapeType.SHAPE_UNKNOWN:
  256. break;
  257. default:
  258. break;
  259. }
  260. }
  261. }
  262. });
  263. }
  264. // Count down the reference count for a mesh shape
  265. // Called at taint-time.
  266. private void DereferenceMesh(BulletShape shape, ShapeDestructionCallback shapeCallback)
  267. {
  268. MeshDesc meshDesc;
  269. if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
  270. {
  271. meshDesc.referenceCount--;
  272. // TODO: release the Bullet storage
  273. if (shapeCallback != null) shapeCallback(shape);
  274. meshDesc.lastReferenced = System.DateTime.Now;
  275. Meshes[shape.shapeKey] = meshDesc;
  276. if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}",
  277. BSScene.DetailLogZero, shape, meshDesc.referenceCount);
  278. }
  279. }
  280. // Count down the reference count for a hull shape
  281. // Called at taint-time.
  282. private void DereferenceHull(BulletShape shape, ShapeDestructionCallback shapeCallback)
  283. {
  284. HullDesc hullDesc;
  285. if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
  286. {
  287. hullDesc.referenceCount--;
  288. // TODO: release the Bullet storage (aging old entries?)
  289. // Tell upper layers that, if they have dependencies on this shape, this link is going away
  290. if (shapeCallback != null) shapeCallback(shape);
  291. hullDesc.lastReferenced = System.DateTime.Now;
  292. Hulls[shape.shapeKey] = hullDesc;
  293. if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}",
  294. BSScene.DetailLogZero, shape, hullDesc.referenceCount);
  295. }
  296. }
  297. // Remove a reference to a compound shape.
  298. // Taking a compound shape apart is a little tricky because if you just delete the
  299. // physical shape, it will free all the underlying children. We can't do that because
  300. // they could be shared. So, this removes each of the children from the compound and
  301. // dereferences them separately before destroying the compound collision object itself.
  302. // Called at taint-time.
  303. private void DereferenceCompound(BulletShape shape, ShapeDestructionCallback shapeCallback)
  304. {
  305. if (!BulletSimAPI.IsCompound2(shape.ptr))
  306. {
  307. // Failed the sanity check!!
  308. PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}",
  309. LogHeader, shape.type, shape.ptr.ToString());
  310. if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}",
  311. BSScene.DetailLogZero, shape.type, shape.ptr.ToString());
  312. return;
  313. }
  314. int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr);
  315. if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren);
  316. for (int ii = numChildren - 1; ii >= 0; ii--)
  317. {
  318. Object childShape = BulletSimAPI.RemoveChildShapeFromCompoundShapeIndex2(shape.ptr, ii);
  319. DereferenceAnonCollisionShape(childShape);
  320. }
  321. BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
  322. }
  323. // Sometimes we have a pointer to a collision shape but don't know what type it is.
  324. // Figure out type and call the correct dereference routine.
  325. // Called at taint-time.
  326. private void DereferenceAnonCollisionShape(Object cShape)
  327. {
  328. MeshDesc meshDesc;
  329. HullDesc hullDesc;
  330. BulletShape shapeInfo = new BulletShape(cShape);
  331. if (TryGetMeshByPtr(cShape, out meshDesc))
  332. {
  333. shapeInfo.type = BSPhysicsShapeType.SHAPE_MESH;
  334. shapeInfo.shapeKey = meshDesc.shapeKey;
  335. }
  336. else
  337. {
  338. if (TryGetHullByPtr(cShape, out hullDesc))
  339. {
  340. shapeInfo.type = BSPhysicsShapeType.SHAPE_HULL;
  341. shapeInfo.shapeKey = hullDesc.shapeKey;
  342. }
  343. else
  344. {
  345. if (BulletSimAPI.IsCompound2(cShape))
  346. {
  347. shapeInfo.type = BSPhysicsShapeType.SHAPE_COMPOUND;
  348. }
  349. else
  350. {
  351. if (BulletSimAPI.IsNativeShape2(cShape))
  352. {
  353. shapeInfo.isNativeShape = true;
  354. shapeInfo.type = BSPhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter)
  355. }
  356. }
  357. }
  358. }
  359. if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo);
  360. if (shapeInfo.type != BSPhysicsShapeType.SHAPE_UNKNOWN)
  361. {
  362. DereferenceShape(shapeInfo, true, null);
  363. }
  364. else
  365. {
  366. PhysicsScene.Logger.ErrorFormat("{0} Could not decypher shape type. Region={1}, addr={2}",
  367. LogHeader, PhysicsScene.RegionName, cShape.ToString());
  368. }
  369. }
  370. // Create the geometry information in Bullet for later use.
  371. // The objects needs a hull if it's physical otherwise a mesh is enough.
  372. // if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls,
  373. // shared geometries will be used. If the parameters of the existing shape are the same
  374. // as this request, the shape is not rebuilt.
  375. // Info in prim.BSShape is updated to the new shape.
  376. // Returns 'true' if the geometry was rebuilt.
  377. // Called at taint-time!
  378. private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
  379. {
  380. bool ret = false;
  381. bool haveShape = false;
  382. if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE)
  383. {
  384. // an avatar capsule is close to a native shape (it is not shared)
  385. GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_CAPSULE,
  386. FixedShapeKey.KEY_CAPSULE, shapeCallback);
  387. if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape);
  388. ret = true;
  389. haveShape = true;
  390. }
  391. // Compound shapes are handled special as they are rebuilt from scratch.
  392. // This isn't too great a hardship since most of the child shapes will have already been created.
  393. if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND)
  394. {
  395. ret = GetReferenceToCompoundShape(prim, shapeCallback);
  396. if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape);
  397. haveShape = true;
  398. }
  399. if (!haveShape)
  400. {
  401. ret = CreateGeomNonSpecial(forceRebuild, prim, shapeCallback);
  402. }
  403. return ret;
  404. }
  405. // Create a mesh/hull shape or a native shape if 'nativeShapePossible' is 'true'.
  406. public bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
  407. {
  408. bool ret = false;
  409. bool haveShape = false;
  410. bool nativeShapePossible = true;
  411. PrimitiveBaseShape pbs = prim.BaseShape;
  412. // If the prim attributes are simple, this could be a simple Bullet native shape
  413. if (!haveShape
  414. && pbs != null
  415. && nativeShapePossible
  416. && ((pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim)
  417. || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
  418. && pbs.ProfileHollow == 0
  419. && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
  420. && pbs.PathBegin == 0 && pbs.PathEnd == 0
  421. && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
  422. && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
  423. && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) )
  424. {
  425. // Get the scale of any existing shape so we can see if the new shape is same native type and same size.
  426. OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero;
  427. if (prim.PhysShape.HasPhysicalShape)
  428. scaleOfExistingShape = BulletSimAPI.GetLocalScaling2(prim.PhysShape.ptr);
  429. if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}",
  430. prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.type);
  431. // It doesn't look like Bullet scales spheres so make sure the scales are all equal
  432. if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
  433. && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)
  434. {
  435. haveShape = true;
  436. if (forceRebuild
  437. || prim.Scale != scaleOfExistingShape
  438. || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_SPHERE
  439. )
  440. {
  441. ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE,
  442. FixedShapeKey.KEY_SPHERE, shapeCallback);
  443. if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
  444. prim.LocalID, forceRebuild, prim.PhysShape);
  445. }
  446. }
  447. if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
  448. {
  449. haveShape = true;
  450. if (forceRebuild
  451. || prim.Scale != scaleOfExistingShape
  452. || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_BOX
  453. )
  454. {
  455. ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX,
  456. FixedShapeKey.KEY_BOX, shapeCallback);
  457. if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
  458. prim.LocalID, forceRebuild, prim.PhysShape);
  459. }
  460. }
  461. }
  462. // If a simple shape is not happening, create a mesh and possibly a hull.
  463. if (!haveShape && pbs != null)
  464. {
  465. ret = CreateGeomMeshOrHull(prim, shapeCallback);
  466. }
  467. return ret;
  468. }
  469. public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
  470. {
  471. bool ret = false;
  472. // Note that if it's a native shape, the check for physical/non-physical is not
  473. // made. Native shapes work in either case.
  474. if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects)
  475. {
  476. // Update prim.BSShape to reference a hull of this shape.
  477. ret = GetReferenceToHull(prim,shapeCallback);
  478. if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}",
  479. prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
  480. }
  481. else
  482. {
  483. ret = GetReferenceToMesh(prim, shapeCallback);
  484. if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}",
  485. prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
  486. }
  487. return ret;
  488. }
  489. // Creates a native shape and assignes it to prim.BSShape.
  490. // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape().
  491. private bool GetReferenceToNativeShape(BSPhysObject prim,
  492. BSPhysicsShapeType shapeType, FixedShapeKey shapeKey,
  493. ShapeDestructionCallback shapeCallback)
  494. {
  495. // release any previous shape
  496. DereferenceShape(prim.PhysShape, true, shapeCallback);
  497. BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey);
  498. // Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
  499. if (DDetail) DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
  500. prim.LocalID, newShape, prim.Scale);
  501. // native shapes are scaled by Bullet
  502. prim.PhysShape = newShape;
  503. return true;
  504. }
  505. private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, BSPhysicsShapeType shapeType,
  506. FixedShapeKey shapeKey)
  507. {
  508. BulletShape newShape;
  509. // Need to make sure the passed shape information is for the native type.
  510. ShapeData nativeShapeData = new ShapeData();
  511. nativeShapeData.Type = shapeType;
  512. nativeShapeData.ID = prim.LocalID;
  513. nativeShapeData.Scale = prim.Scale;
  514. nativeShapeData.Size = prim.Scale; // unneeded, I think.
  515. nativeShapeData.MeshKey = (ulong)shapeKey;
  516. nativeShapeData.HullKey = (ulong)shapeKey;
  517. if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
  518. {
  519. // The proper scale has been calculated in the prim.
  520. newShape = new BulletShape(
  521. BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale)
  522. , shapeType);
  523. if (DDetail) DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
  524. }
  525. else
  526. {
  527. // Native shapes are scaled in Bullet so set the scaling to the size
  528. newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType);
  529. }
  530. if (!newShape.HasPhysicalShape)
  531. {
  532. PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
  533. LogHeader, prim.LocalID, shapeType);
  534. }
  535. newShape.shapeKey = (System.UInt64)shapeKey;
  536. newShape.isNativeShape = true;
  537. return newShape;
  538. }
  539. // Builds a mesh shape in the physical world and updates prim.BSShape.
  540. // Dereferences previous shape in BSShape and adds a reference for this new shape.
  541. // Returns 'true' of a mesh was actually built. Otherwise .
  542. // Called at taint-time!
  543. private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
  544. {
  545. BulletShape newShape = new BulletShape();
  546. float lod;
  547. System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
  548. // if this new shape is the same as last time, don't recreate the mesh
  549. if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_MESH)
  550. return false;
  551. if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}",
  552. prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"));
  553. // Since we're recreating new, get rid of the reference to the previous shape
  554. DereferenceShape(prim.PhysShape, true, shapeCallback);
  555. newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, prim.BaseShape, prim.Size, lod);
  556. // Take evasive action if the mesh was not constructed.
  557. newShape = VerifyMeshCreated(newShape, prim);
  558. ReferenceShape(newShape);
  559. prim.PhysShape = newShape;
  560. return true; // 'true' means a new shape has been added to this prim
  561. }
  562. private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
  563. {
  564. IMesh meshData = null;
  565. Object meshPtr = null;
  566. MeshDesc meshDesc;
  567. if (Meshes.TryGetValue(newMeshKey, out meshDesc))
  568. {
  569. // If the mesh has already been built just use it.
  570. meshPtr = meshDesc.ptr;
  571. }
  572. else
  573. {
  574. meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false);
  575. if (meshData != null)
  576. {
  577. int[] indices = meshData.getIndexListAsInt();
  578. List<OMV.Vector3> vertices = meshData.getVertexList();
  579. float[] verticesAsFloats = new float[vertices.Count * 3];
  580. int vi = 0;
  581. foreach (OMV.Vector3 vv in vertices)
  582. {
  583. verticesAsFloats[vi++] = vv.X;
  584. verticesAsFloats[vi++] = vv.Y;
  585. verticesAsFloats[vi++] = vv.Z;
  586. }
  587. // m_log.DebugFormat("{0}: BSShapeCollection.CreatePhysicalMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}",
  588. // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count);
  589. meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
  590. indices.GetLength(0), indices, vertices.Count, verticesAsFloats);
  591. }
  592. }
  593. BulletShape newShape = new BulletShape(meshPtr, BSPhysicsShapeType.SHAPE_MESH);
  594. newShape.shapeKey = newMeshKey;
  595. return newShape;
  596. }
  597. // See that hull shape exists in the physical world and update prim.BSShape.
  598. // We could be creating the hull because scale changed or whatever.
  599. private bool GetReferenceToHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
  600. {
  601. BulletShape newShape;
  602. float lod;
  603. System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
  604. // if the hull hasn't changed, don't rebuild it
  605. if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_HULL)
  606. return false;
  607. if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}",
  608. prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X"));
  609. // Remove usage of the previous shape.
  610. DereferenceShape(prim.PhysShape, true, shapeCallback);
  611. newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, prim.BaseShape, prim.Size, lod);
  612. newShape = VerifyMeshCreated(newShape, prim);
  613. ReferenceShape(newShape);
  614. prim.PhysShape = newShape;
  615. return true; // 'true' means a new shape has been added to this prim
  616. }
  617. List<ConvexResult> m_hulls;
  618. private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
  619. {
  620. Object hullPtr = null;
  621. HullDesc hullDesc;
  622. if (Hulls.TryGetValue(newHullKey, out hullDesc))
  623. {
  624. // If the hull shape already is created, just use it.
  625. hullPtr = hullDesc.ptr;
  626. }
  627. else
  628. {
  629. // Build a new hull in the physical world
  630. // Pass true for physicalness as this creates some sort of bounding box which we don't need
  631. IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false);
  632. if (meshData != null)
  633. {
  634. int[] indices = meshData.getIndexListAsInt();
  635. List<OMV.Vector3> vertices = meshData.getVertexList();
  636. //format conversion from IMesh format to DecompDesc format
  637. List<int> convIndices = new List<int>();
  638. List<float3> convVertices = new List<float3>();
  639. for (int ii = 0; ii < indices.GetLength(0); ii++)
  640. {
  641. convIndices.Add(indices[ii]);
  642. }
  643. foreach (OMV.Vector3 vv in vertices)
  644. {
  645. convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
  646. }
  647. // setup and do convex hull conversion
  648. m_hulls = new List<ConvexResult>();
  649. DecompDesc dcomp = new DecompDesc();
  650. dcomp.mIndices = convIndices;
  651. dcomp.mVertices = convVertices;
  652. ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
  653. // create the hull into the _hulls variable
  654. convexBuilder.process(dcomp);
  655. // Convert the vertices and indices for passing to unmanaged.
  656. // The hull information is passed as a large floating point array.
  657. // The format is:
  658. // convHulls[0] = number of hulls
  659. // convHulls[1] = number of vertices in first hull
  660. // convHulls[2] = hull centroid X coordinate
  661. // convHulls[3] = hull centroid Y coordinate
  662. // convHulls[4] = hull centroid Z coordinate
  663. // convHulls[5] = first hull vertex X
  664. // convHulls[6] = first hull vertex Y
  665. // convHulls[7] = first hull vertex Z
  666. // convHulls[8] = second hull vertex X
  667. // ...
  668. // convHulls[n] = number of vertices in second hull
  669. // convHulls[n+1] = second hull centroid X coordinate
  670. // ...
  671. //
  672. // TODO: is is very inefficient. Someday change the convex hull generator to return
  673. // data structures that do not need to be converted in order to pass to Bullet.
  674. // And maybe put the values directly into pinned memory rather than marshaling.
  675. int hullCount = m_hulls.Count;
  676. int totalVertices = 1; // include one for the count of the hulls
  677. foreach (ConvexResult cr in m_hulls)
  678. {
  679. totalVertices += 4; // add four for the vertex count and centroid
  680. totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
  681. }
  682. float[] convHulls = new float[totalVertices];
  683. convHulls[0] = (float)hullCount;
  684. int jj = 1;
  685. foreach (ConvexResult cr in m_hulls)
  686. {
  687. // copy vertices for index access
  688. float3[] verts = new float3[cr.HullVertices.Count];
  689. int kk = 0;
  690. foreach (float3 ff in cr.HullVertices)
  691. {
  692. verts[kk++] = ff;
  693. }
  694. // add to the array one hull's worth of data
  695. convHulls[jj++] = cr.HullIndices.Count;
  696. convHulls[jj++] = 0f; // centroid x,y,z
  697. convHulls[jj++] = 0f;
  698. convHulls[jj++] = 0f;
  699. foreach (int ind in cr.HullIndices)
  700. {
  701. convHulls[jj++] = verts[ind].x;
  702. convHulls[jj++] = verts[ind].y;
  703. convHulls[jj++] = verts[ind].z;
  704. }
  705. }
  706. // create the hull data structure in Bullet
  707. hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
  708. }
  709. }
  710. BulletShape newShape = new BulletShape(hullPtr, BSPhysicsShapeType.SHAPE_HULL);
  711. newShape.shapeKey = newHullKey;
  712. return newShape;
  713. }
  714. // Callback from convex hull creater with a newly created hull.
  715. // Just add it to our collection of hulls for this shape.
  716. private void HullReturn(ConvexResult result)
  717. {
  718. m_hulls.Add(result);
  719. return;
  720. }
  721. // Compound shapes are always built from scratch.
  722. // This shouldn't be to bad since most of the parts will be meshes that had been built previously.
  723. private bool GetReferenceToCompoundShape(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
  724. {
  725. // Remove reference to the old shape
  726. // Don't need to do this as the shape is freed when the new root shape is created below.
  727. // DereferenceShape(prim.PhysShape, true, shapeCallback);
  728. BulletShape cShape = new BulletShape(
  729. BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr, false), BSPhysicsShapeType.SHAPE_COMPOUND);
  730. // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape.
  731. CreateGeomMeshOrHull(prim, shapeCallback);
  732. BulletSimAPI.AddChildShapeToCompoundShape2(cShape.ptr, prim.PhysShape.ptr, OMV.Vector3.Zero, OMV.Quaternion.Identity);
  733. if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}",
  734. prim.LocalID, cShape, prim.PhysShape);
  735. prim.PhysShape = cShape;
  736. return true;
  737. }
  738. // Create a hash of all the shape parameters to be used as a key
  739. // for this particular shape.
  740. private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod)
  741. {
  742. // level of detail based on size and type of the object
  743. float lod = BSParam.MeshLOD;
  744. if (pbs.SculptEntry)
  745. lod = BSParam.SculptLOD;
  746. // Mega prims usually get more detail because one can interact with shape approximations at this size.
  747. float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z));
  748. if (maxAxis > BSParam.MeshMegaPrimThreshold)
  749. lod = BSParam.MeshMegaPrimLOD;
  750. retLod = lod;
  751. return pbs.GetMeshKey(size, lod);
  752. }
  753. // For those who don't want the LOD
  754. private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs)
  755. {
  756. float lod;
  757. return ComputeShapeKey(size, pbs, out lod);
  758. }
  759. // The creation of a mesh or hull can fail if an underlying asset is not available.
  760. // There are two cases: 1) the asset is not in the cache and it needs to be fetched;
  761. // and 2) the asset cannot be converted (like failed decompression of JPEG2000s).
  762. // The first case causes the asset to be fetched. The second case requires
  763. // us to not loop forever.
  764. // Called after creating a physical mesh or hull. If the physical shape was created,
  765. // just return.
  766. private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim)
  767. {
  768. // If the shape was successfully created, nothing more to do
  769. if (newShape.HasPhysicalShape)
  770. return newShape;
  771. // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
  772. if (prim.BaseShape.SculptEntry && !prim.LastAssetBuildFailed && prim.BaseShape.SculptTexture != OMV.UUID.Zero)
  773. {
  774. prim.LastAssetBuildFailed = true;
  775. BSPhysObject xprim = prim;
  776. DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lID={1},lastFailed={2}",
  777. LogHeader, prim.LocalID, prim.LastAssetBuildFailed);
  778. Util.FireAndForget(delegate
  779. {
  780. RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod;
  781. if (assetProvider != null)
  782. {
  783. BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
  784. assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
  785. {
  786. if (!yprim.BaseShape.SculptEntry)
  787. return;
  788. if (yprim.BaseShape.SculptTexture.ToString() != asset.ID)
  789. return;
  790. yprim.BaseShape.SculptData = asset.Data;
  791. // This will cause the prim to see that the filler shape is not the right
  792. // one and try again to build the object.
  793. // No race condition with the normal shape setting since the rebuild is at taint time.
  794. yprim.ForceBodyShapeRebuild(false);
  795. });
  796. }
  797. });
  798. }
  799. else
  800. {
  801. if (prim.LastAssetBuildFailed)
  802. {
  803. PhysicsScene.Logger.ErrorFormat("{0} Mesh failed to fetch asset. lID={1}, texture={2}",
  804. LogHeader, prim.LocalID, prim.BaseShape.SculptTexture);
  805. }
  806. }
  807. // While we figure out the real problem, stick a simple native shape on the object.
  808. BulletShape fillinShape =
  809. BuildPhysicalNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
  810. return fillinShape;
  811. }
  812. // Create a body object in Bullet.
  813. // Updates prim.BSBody with the information about the new body if one is created.
  814. // Returns 'true' if an object was actually created.
  815. // Called at taint-time.
  816. private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, BulletShape shape,
  817. BodyDestructionCallback bodyCallback)
  818. {
  819. bool ret = false;
  820. // the mesh, hull or native shape must have already been created in Bullet
  821. bool mustRebuild = !prim.PhysBody.HasPhysicalBody;
  822. // If there is an existing body, verify it's of an acceptable type.
  823. // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
  824. if (!mustRebuild)
  825. {
  826. CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.PhysBody.ptr);
  827. if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY
  828. || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT)
  829. {
  830. // If the collisionObject is not the correct type for solidness, rebuild what's there
  831. mustRebuild = true;
  832. }
  833. }
  834. if (mustRebuild || forceRebuild)
  835. {
  836. // Free any old body
  837. DereferenceBody(prim.PhysBody, true, bodyCallback);
  838. BulletBody aBody;
  839. Object bodyPtr = null;
  840. if (prim.IsSolid)
  841. {
  842. bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
  843. prim.LocalID, prim.RawPosition, prim.RawOrientation);
  844. if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString());
  845. }
  846. else
  847. {
  848. bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
  849. prim.LocalID, prim.RawPosition, prim.RawOrientation);
  850. if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString());
  851. }
  852. aBody = new BulletBody(prim.LocalID, bodyPtr);
  853. ReferenceBody(aBody, true);
  854. prim.PhysBody = aBody;
  855. ret = true;
  856. }
  857. return ret;
  858. }
  859. private bool TryGetMeshByPtr(Object addr, out MeshDesc outDesc)
  860. {
  861. bool ret = false;
  862. MeshDesc foundDesc = new MeshDesc();
  863. foreach (MeshDesc md in Meshes.Values)
  864. {
  865. if (md.ptr == addr)
  866. {
  867. foundDesc = md;
  868. ret = true;
  869. break;
  870. }
  871. }
  872. outDesc = foundDesc;
  873. return ret;
  874. }
  875. private bool TryGetHullByPtr(Object addr, out HullDesc outDesc)
  876. {
  877. bool ret = false;
  878. HullDesc foundDesc = new HullDesc();
  879. foreach (HullDesc hd in Hulls.Values)
  880. {
  881. if (hd.ptr == addr)
  882. {
  883. foundDesc = hd;
  884. ret = true;
  885. break;
  886. }
  887. }
  888. outDesc = foundDesc;
  889. return ret;
  890. }
  891. private void DetailLog(string msg, params Object[] args)
  892. {
  893. if (PhysicsScene.PhysicsLogging.Enabled)
  894. PhysicsScene.DetailLog(msg, args);
  895. }
  896. }
  897. }