BSShapeCollection.cs 37 KB

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