1
0

BSShapes.cs 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084
  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 OpenSim.Framework;
  31. using OpenSim.Region.Physics.Manager;
  32. using OpenSim.Region.Physics.ConvexDecompositionDotNet;
  33. using OMV = OpenMetaverse;
  34. namespace OpenSim.Region.Physics.BulletSPlugin
  35. {
  36. public abstract class BSShape
  37. {
  38. private static string LogHeader = "[BULLETSIM SHAPE]";
  39. public int referenceCount { get; set; }
  40. public DateTime lastReferenced { get; set; }
  41. public BulletShape physShapeInfo { get; set; }
  42. public BSShape()
  43. {
  44. referenceCount = 1;
  45. lastReferenced = DateTime.Now;
  46. physShapeInfo = new BulletShape();
  47. }
  48. public BSShape(BulletShape pShape)
  49. {
  50. referenceCount = 1;
  51. lastReferenced = DateTime.Now;
  52. physShapeInfo = pShape;
  53. }
  54. // Get another reference to this shape.
  55. public abstract BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim);
  56. // Called when this shape is being used again.
  57. // Used internally. External callers should call instance.GetReference() to properly copy/reference
  58. // the shape.
  59. protected virtual void IncrementReference()
  60. {
  61. referenceCount++;
  62. lastReferenced = DateTime.Now;
  63. }
  64. // Called when this shape is being used again.
  65. protected virtual void DecrementReference()
  66. {
  67. referenceCount--;
  68. lastReferenced = DateTime.Now;
  69. }
  70. // Release the use of a physical shape.
  71. public abstract void Dereference(BSScene physicsScene);
  72. // Return 'true' if there is an allocated physics physical shape under this class instance.
  73. public virtual bool HasPhysicalShape
  74. {
  75. get
  76. {
  77. if (physShapeInfo != null)
  78. return physShapeInfo.HasPhysicalShape;
  79. return false;
  80. }
  81. }
  82. public virtual BSPhysicsShapeType ShapeType
  83. {
  84. get
  85. {
  86. BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN;
  87. if (physShapeInfo != null && physShapeInfo.HasPhysicalShape)
  88. ret = physShapeInfo.shapeType;
  89. return ret;
  90. }
  91. }
  92. // Returns a string for debugging that uniquily identifies the memory used by this instance
  93. public virtual string AddrString
  94. {
  95. get
  96. {
  97. if (physShapeInfo != null)
  98. return physShapeInfo.AddrString;
  99. return "unknown";
  100. }
  101. }
  102. public override string ToString()
  103. {
  104. StringBuilder buff = new StringBuilder();
  105. if (physShapeInfo == null)
  106. {
  107. buff.Append("<noPhys");
  108. }
  109. else
  110. {
  111. buff.Append("<phy=");
  112. buff.Append(physShapeInfo.ToString());
  113. }
  114. buff.Append(",c=");
  115. buff.Append(referenceCount.ToString());
  116. buff.Append(">");
  117. return buff.ToString();
  118. }
  119. #region Common shape routines
  120. // Create a hash of all the shape parameters to be used as a key for this particular shape.
  121. public static System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod)
  122. {
  123. // level of detail based on size and type of the object
  124. float lod = BSParam.MeshLOD;
  125. if (pbs.SculptEntry)
  126. lod = BSParam.SculptLOD;
  127. // Mega prims usually get more detail because one can interact with shape approximations at this size.
  128. float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z));
  129. if (maxAxis > BSParam.MeshMegaPrimThreshold)
  130. lod = BSParam.MeshMegaPrimLOD;
  131. retLod = lod;
  132. return pbs.GetMeshKey(size, lod);
  133. }
  134. // The creation of a mesh or hull can fail if an underlying asset is not available.
  135. // There are two cases: 1) the asset is not in the cache and it needs to be fetched;
  136. // and 2) the asset cannot be converted (like failed decompression of JPEG2000s).
  137. // The first case causes the asset to be fetched. The second case requires
  138. // us to not loop forever.
  139. // Called after creating a physical mesh or hull. If the physical shape was created,
  140. // just return.
  141. public static BulletShape VerifyMeshCreated(BSScene physicsScene, BulletShape newShape, BSPhysObject prim)
  142. {
  143. // If the shape was successfully created, nothing more to do
  144. if (newShape.HasPhysicalShape)
  145. return newShape;
  146. // VerifyMeshCreated is called after trying to create the mesh. If we think the asset had been
  147. // fetched but we end up here again, the meshing of the asset must have failed.
  148. // Prevent trying to keep fetching the mesh by declaring failure.
  149. if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched)
  150. {
  151. prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed;
  152. physicsScene.Logger.WarnFormat("{0} Fetched asset would not mesh. {1}, texture={2}",
  153. LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture);
  154. physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,setFailed,objNam={1},tex={2}",
  155. prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture);
  156. }
  157. else
  158. {
  159. // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
  160. if (prim.BaseShape.SculptEntry
  161. && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Failed
  162. && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting
  163. && prim.BaseShape.SculptTexture != OMV.UUID.Zero
  164. )
  165. {
  166. physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAsset,objNam={1},tex={2}",
  167. prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture);
  168. // Multiple requestors will know we're waiting for this asset
  169. prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting;
  170. BSPhysObject xprim = prim;
  171. Util.FireAndForget(delegate
  172. {
  173. // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,inFireAndForget", xprim.LocalID);
  174. RequestAssetDelegate assetProvider = physicsScene.RequestAssetMethod;
  175. if (assetProvider != null)
  176. {
  177. BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
  178. assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
  179. {
  180. // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,assetProviderCallback", xprim.LocalID);
  181. bool assetFound = false;
  182. string mismatchIDs = String.Empty; // DEBUG DEBUG
  183. if (asset != null && yprim.BaseShape.SculptEntry)
  184. {
  185. if (yprim.BaseShape.SculptTexture.ToString() == asset.ID)
  186. {
  187. yprim.BaseShape.SculptData = asset.Data;
  188. // This will cause the prim to see that the filler shape is not the right
  189. // one and try again to build the object.
  190. // No race condition with the normal shape setting since the rebuild is at taint time.
  191. yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched;
  192. yprim.ForceBodyShapeRebuild(false /* inTaintTime */);
  193. assetFound = true;
  194. }
  195. else
  196. {
  197. mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID;
  198. }
  199. }
  200. if (!assetFound)
  201. {
  202. yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed;
  203. }
  204. physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAssetCallback,found={1},isSculpt={2},ids={3}",
  205. yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs );
  206. });
  207. }
  208. else
  209. {
  210. xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed;
  211. physicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}",
  212. LogHeader, physicsScene.Name);
  213. }
  214. });
  215. }
  216. else
  217. {
  218. if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Failed)
  219. {
  220. physicsScene.Logger.WarnFormat("{0} Mesh failed to fetch asset. obj={1}, texture={2}",
  221. LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture);
  222. physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,wasFailed,objNam={1},tex={2}",
  223. prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture);
  224. }
  225. }
  226. }
  227. // While we wait for the mesh defining asset to be loaded, stick in a simple box for the object.
  228. BSShape fillShape = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
  229. physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,boxTempShape", prim.LocalID);
  230. return fillShape.physShapeInfo;
  231. }
  232. #endregion // Common shape routines
  233. }
  234. // ============================================================================================================
  235. public class BSShapeNull : BSShape
  236. {
  237. public BSShapeNull() : base()
  238. {
  239. }
  240. public static BSShape GetReference() { return new BSShapeNull(); }
  241. public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) { return new BSShapeNull(); }
  242. public override void Dereference(BSScene physicsScene) { /* The magic of garbage collection will make this go away */ }
  243. }
  244. // ============================================================================================================
  245. public class BSShapeNative : BSShape
  246. {
  247. private static string LogHeader = "[BULLETSIM SHAPE NATIVE]";
  248. public BSShapeNative(BulletShape pShape) : base(pShape)
  249. {
  250. }
  251. public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim,
  252. BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
  253. {
  254. // Native shapes are not shared and are always built anew.
  255. return new BSShapeNative(CreatePhysicalNativeShape(physicsScene, prim, shapeType, shapeKey));
  256. }
  257. public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim)
  258. {
  259. // Native shapes are not shared so we return a new shape.
  260. BSShape ret = null;
  261. lock (physShapeInfo)
  262. {
  263. ret = new BSShapeNative(CreatePhysicalNativeShape(pPhysicsScene, pPrim,
  264. physShapeInfo.shapeType, (FixedShapeKey)physShapeInfo.shapeKey));
  265. }
  266. return ret;
  267. }
  268. // Make this reference to the physical shape go away since native shapes are not shared.
  269. public override void Dereference(BSScene physicsScene)
  270. {
  271. // Native shapes are not tracked and are released immediately
  272. lock (physShapeInfo)
  273. {
  274. if (physShapeInfo.HasPhysicalShape)
  275. {
  276. physicsScene.DetailLog("{0},BSShapeNative.Dereference,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this);
  277. physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo);
  278. }
  279. physShapeInfo.Clear();
  280. // Garbage collection will free up this instance.
  281. }
  282. }
  283. private static BulletShape CreatePhysicalNativeShape(BSScene physicsScene, BSPhysObject prim,
  284. BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
  285. {
  286. BulletShape newShape;
  287. ShapeData nativeShapeData = new ShapeData();
  288. nativeShapeData.Type = shapeType;
  289. nativeShapeData.ID = prim.LocalID;
  290. nativeShapeData.Scale = prim.Scale;
  291. nativeShapeData.Size = prim.Scale;
  292. nativeShapeData.MeshKey = (ulong)shapeKey;
  293. nativeShapeData.HullKey = (ulong)shapeKey;
  294. if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
  295. {
  296. newShape = physicsScene.PE.BuildCapsuleShape(physicsScene.World, 1f, 1f, prim.Scale);
  297. physicsScene.DetailLog("{0},BSShapeNative,capsule,scale={1}", prim.LocalID, prim.Scale);
  298. }
  299. else
  300. {
  301. newShape = physicsScene.PE.BuildNativeShape(physicsScene.World, nativeShapeData);
  302. }
  303. if (!newShape.HasPhysicalShape)
  304. {
  305. physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
  306. LogHeader, prim.LocalID, shapeType);
  307. }
  308. newShape.shapeType = shapeType;
  309. newShape.isNativeShape = true;
  310. newShape.shapeKey = (UInt64)shapeKey;
  311. return newShape;
  312. }
  313. }
  314. // ============================================================================================================
  315. public class BSShapeMesh : BSShape
  316. {
  317. private static string LogHeader = "[BULLETSIM SHAPE MESH]";
  318. public static Dictionary<System.UInt64, BSShapeMesh> Meshes = new Dictionary<System.UInt64, BSShapeMesh>();
  319. public BSShapeMesh(BulletShape pShape) : base(pShape)
  320. {
  321. }
  322. public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
  323. {
  324. float lod;
  325. System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
  326. BSShapeMesh retMesh = null;
  327. lock (Meshes)
  328. {
  329. if (Meshes.TryGetValue(newMeshKey, out retMesh))
  330. {
  331. // The mesh has already been created. Return a new reference to same.
  332. retMesh.IncrementReference();
  333. }
  334. else
  335. {
  336. retMesh = new BSShapeMesh(new BulletShape());
  337. // An instance of this mesh has not been created. Build and remember same.
  338. BulletShape newShape = retMesh.CreatePhysicalMesh(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod);
  339. // Check to see if mesh was created (might require an asset).
  340. newShape = VerifyMeshCreated(physicsScene, newShape, prim);
  341. if (!newShape.isNativeShape || prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Failed)
  342. {
  343. // If a mesh was what was created, remember the built shape for later sharing.
  344. // Also note that if meshing failed we put it in the mesh list as there is nothing else to do about the mesh.
  345. Meshes.Add(newMeshKey, retMesh);
  346. }
  347. retMesh.physShapeInfo = newShape;
  348. }
  349. }
  350. physicsScene.DetailLog("{0},BSShapeMesh,getReference,mesh={1},size={2},lod={3}", prim.LocalID, retMesh, prim.Size, lod);
  351. return retMesh;
  352. }
  353. public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim)
  354. {
  355. // Another reference to this shape is just counted.
  356. IncrementReference();
  357. return this;
  358. }
  359. public override void Dereference(BSScene physicsScene)
  360. {
  361. lock (Meshes)
  362. {
  363. this.DecrementReference();
  364. physicsScene.DetailLog("{0},BSShapeMesh.Dereference,shape={1}", BSScene.DetailLogZero, this);
  365. // TODO: schedule aging and destruction of unused meshes.
  366. }
  367. }
  368. // Loop through all the known meshes and return the description based on the physical address.
  369. public static bool TryGetMeshByPtr(BulletShape pShape, out BSShapeMesh outMesh)
  370. {
  371. bool ret = false;
  372. BSShapeMesh foundDesc = null;
  373. lock (Meshes)
  374. {
  375. foreach (BSShapeMesh sm in Meshes.Values)
  376. {
  377. if (sm.physShapeInfo.ReferenceSame(pShape))
  378. {
  379. foundDesc = sm;
  380. ret = true;
  381. break;
  382. }
  383. }
  384. }
  385. outMesh = foundDesc;
  386. return ret;
  387. }
  388. private BulletShape CreatePhysicalMesh(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey,
  389. PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
  390. {
  391. BulletShape newShape = new BulletShape();
  392. IMesh meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod,
  393. false, // say it is not physical so a bounding box is not built
  394. false // do not cache the mesh and do not use previously built versions
  395. );
  396. if (meshData != null)
  397. {
  398. if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched)
  399. {
  400. // Release the fetched asset data once it has been used.
  401. pbs.SculptData = new byte[0];
  402. prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown;
  403. }
  404. int[] indices = meshData.getIndexListAsInt();
  405. int realIndicesIndex = indices.Length;
  406. float[] verticesAsFloats = meshData.getVertexListAsFloat();
  407. if (BSParam.ShouldRemoveZeroWidthTriangles)
  408. {
  409. // Remove degenerate triangles. These are triangles with two of the vertices
  410. // are the same. This is complicated by the problem that vertices are not
  411. // made unique in sculpties so we have to compare the values in the vertex.
  412. realIndicesIndex = 0;
  413. for (int tri = 0; tri < indices.Length; tri += 3)
  414. {
  415. // Compute displacements into vertex array for each vertex of the triangle
  416. int v1 = indices[tri + 0] * 3;
  417. int v2 = indices[tri + 1] * 3;
  418. int v3 = indices[tri + 2] * 3;
  419. // Check to see if any two of the vertices are the same
  420. if (!( ( verticesAsFloats[v1 + 0] == verticesAsFloats[v2 + 0]
  421. && verticesAsFloats[v1 + 1] == verticesAsFloats[v2 + 1]
  422. && verticesAsFloats[v1 + 2] == verticesAsFloats[v2 + 2])
  423. || ( verticesAsFloats[v2 + 0] == verticesAsFloats[v3 + 0]
  424. && verticesAsFloats[v2 + 1] == verticesAsFloats[v3 + 1]
  425. && verticesAsFloats[v2 + 2] == verticesAsFloats[v3 + 2])
  426. || ( verticesAsFloats[v1 + 0] == verticesAsFloats[v3 + 0]
  427. && verticesAsFloats[v1 + 1] == verticesAsFloats[v3 + 1]
  428. && verticesAsFloats[v1 + 2] == verticesAsFloats[v3 + 2]) )
  429. )
  430. {
  431. // None of the vertices of the triangles are the same. This is a good triangle;
  432. indices[realIndicesIndex + 0] = indices[tri + 0];
  433. indices[realIndicesIndex + 1] = indices[tri + 1];
  434. indices[realIndicesIndex + 2] = indices[tri + 2];
  435. realIndicesIndex += 3;
  436. }
  437. }
  438. }
  439. physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,key={1},origTri={2},realTri={3},numVerts={4}",
  440. BSScene.DetailLogZero, newMeshKey.ToString("X"), indices.Length / 3, realIndicesIndex / 3, verticesAsFloats.Length / 3);
  441. if (realIndicesIndex != 0)
  442. {
  443. newShape = physicsScene.PE.CreateMeshShape(physicsScene.World,
  444. realIndicesIndex, indices, verticesAsFloats.Length / 3, verticesAsFloats);
  445. }
  446. else
  447. {
  448. // Force the asset condition to 'failed' so we won't try to keep fetching and processing this mesh.
  449. prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed;
  450. physicsScene.Logger.DebugFormat("{0} All mesh triangles degenerate. Prim {1} at {2} in {3}",
  451. LogHeader, prim.PhysObjectName, prim.RawPosition, physicsScene.Name);
  452. physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,allDegenerate,key={1}", prim.LocalID, newMeshKey);
  453. }
  454. }
  455. newShape.shapeKey = newMeshKey;
  456. return newShape;
  457. }
  458. }
  459. // ============================================================================================================
  460. public class BSShapeHull : BSShape
  461. {
  462. private static string LogHeader = "[BULLETSIM SHAPE HULL]";
  463. public static Dictionary<System.UInt64, BSShapeHull> Hulls = new Dictionary<System.UInt64, BSShapeHull>();
  464. public BSShapeHull(BulletShape pShape) : base(pShape)
  465. {
  466. }
  467. public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
  468. {
  469. float lod;
  470. System.UInt64 newHullKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
  471. BSShapeHull retHull = null;
  472. lock (Hulls)
  473. {
  474. if (Hulls.TryGetValue(newHullKey, out retHull))
  475. {
  476. // The mesh has already been created. Return a new reference to same.
  477. retHull.IncrementReference();
  478. }
  479. else
  480. {
  481. retHull = new BSShapeHull(new BulletShape());
  482. // An instance of this mesh has not been created. Build and remember same.
  483. BulletShape newShape = retHull.CreatePhysicalHull(physicsScene, prim, newHullKey, prim.BaseShape, prim.Size, lod);
  484. // Check to see if hull was created (might require an asset).
  485. newShape = VerifyMeshCreated(physicsScene, newShape, prim);
  486. if (!newShape.isNativeShape || prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Failed)
  487. {
  488. // If a mesh was what was created, remember the built shape for later sharing.
  489. Hulls.Add(newHullKey, retHull);
  490. }
  491. retHull.physShapeInfo = newShape;
  492. }
  493. }
  494. physicsScene.DetailLog("{0},BSShapeHull,getReference,hull={1},size={2},lod={3}", prim.LocalID, retHull, prim.Size, lod);
  495. return retHull;
  496. }
  497. public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim)
  498. {
  499. // Another reference to this shape is just counted.
  500. IncrementReference();
  501. return this;
  502. }
  503. public override void Dereference(BSScene physicsScene)
  504. {
  505. lock (Hulls)
  506. {
  507. this.DecrementReference();
  508. physicsScene.DetailLog("{0},BSShapeHull.Dereference,shape={1}", BSScene.DetailLogZero, this);
  509. // TODO: schedule aging and destruction of unused meshes.
  510. }
  511. }
  512. List<ConvexResult> m_hulls;
  513. private BulletShape CreatePhysicalHull(BSScene physicsScene, BSPhysObject prim, System.UInt64 newHullKey,
  514. PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
  515. {
  516. BulletShape newShape = new BulletShape();
  517. IntPtr hullPtr = IntPtr.Zero;
  518. if (BSParam.ShouldUseBulletHACD)
  519. {
  520. // Build the hull shape from an existing mesh shape.
  521. // The mesh should have already been created in Bullet.
  522. physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,shouldUseBulletHACD,entry", prim.LocalID);
  523. BSShape meshShape = BSShapeMesh.GetReference(physicsScene, true, prim);
  524. if (meshShape.physShapeInfo.HasPhysicalShape)
  525. {
  526. HACDParams parms;
  527. parms.maxVerticesPerHull = BSParam.BHullMaxVerticesPerHull;
  528. parms.minClusters = BSParam.BHullMinClusters;
  529. parms.compacityWeight = BSParam.BHullCompacityWeight;
  530. parms.volumeWeight = BSParam.BHullVolumeWeight;
  531. parms.concavity = BSParam.BHullConcavity;
  532. parms.addExtraDistPoints = BSParam.NumericBool(BSParam.BHullAddExtraDistPoints);
  533. parms.addNeighboursDistPoints = BSParam.NumericBool(BSParam.BHullAddNeighboursDistPoints);
  534. parms.addFacesPoints = BSParam.NumericBool(BSParam.BHullAddFacesPoints);
  535. parms.shouldAdjustCollisionMargin = BSParam.NumericBool(BSParam.BHullShouldAdjustCollisionMargin);
  536. physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,beforeCall", prim.LocalID, newShape.HasPhysicalShape);
  537. newShape = physicsScene.PE.BuildHullShapeFromMesh(physicsScene.World, meshShape.physShapeInfo, parms);
  538. physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape);
  539. // Now done with the mesh shape.
  540. meshShape.Dereference(physicsScene);
  541. }
  542. physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,shouldUseBulletHACD,exit,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape);
  543. }
  544. if (!newShape.HasPhysicalShape)
  545. {
  546. // Build a new hull in the physical world using the C# HACD algorigthm.
  547. // Pass true for physicalness as this prevents the creation of bounding box which is not needed
  548. IMesh meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, true /* isPhysical */, false /* shouldCache */);
  549. if (meshData != null)
  550. {
  551. if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched)
  552. {
  553. // Release the fetched asset data once it has been used.
  554. pbs.SculptData = new byte[0];
  555. prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown;
  556. }
  557. int[] indices = meshData.getIndexListAsInt();
  558. List<OMV.Vector3> vertices = meshData.getVertexList();
  559. //format conversion from IMesh format to DecompDesc format
  560. List<int> convIndices = new List<int>();
  561. List<float3> convVertices = new List<float3>();
  562. for (int ii = 0; ii < indices.GetLength(0); ii++)
  563. {
  564. convIndices.Add(indices[ii]);
  565. }
  566. foreach (OMV.Vector3 vv in vertices)
  567. {
  568. convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
  569. }
  570. uint maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplit;
  571. if (BSParam.CSHullMaxDepthSplit != BSParam.CSHullMaxDepthSplitForSimpleShapes)
  572. {
  573. // Simple primitive shapes we know are convex so they are better implemented with
  574. // fewer hulls.
  575. // Check for simple shape (prim without cuts) and reduce split parameter if so.
  576. if (BSShapeCollection.PrimHasNoCuts(pbs))
  577. {
  578. maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplitForSimpleShapes;
  579. }
  580. }
  581. // setup and do convex hull conversion
  582. m_hulls = new List<ConvexResult>();
  583. DecompDesc dcomp = new DecompDesc();
  584. dcomp.mIndices = convIndices;
  585. dcomp.mVertices = convVertices;
  586. dcomp.mDepth = maxDepthSplit;
  587. dcomp.mCpercent = BSParam.CSHullConcavityThresholdPercent;
  588. dcomp.mPpercent = BSParam.CSHullVolumeConservationThresholdPercent;
  589. dcomp.mMaxVertices = (uint)BSParam.CSHullMaxVertices;
  590. dcomp.mSkinWidth = BSParam.CSHullMaxSkinWidth;
  591. ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
  592. // create the hull into the _hulls variable
  593. convexBuilder.process(dcomp);
  594. physicsScene.DetailLog("{0},BSShapeCollection.CreatePhysicalHull,key={1},inVert={2},inInd={3},split={4},hulls={5}",
  595. BSScene.DetailLogZero, newHullKey, indices.GetLength(0), vertices.Count, maxDepthSplit, m_hulls.Count);
  596. // Convert the vertices and indices for passing to unmanaged.
  597. // The hull information is passed as a large floating point array.
  598. // The format is:
  599. // convHulls[0] = number of hulls
  600. // convHulls[1] = number of vertices in first hull
  601. // convHulls[2] = hull centroid X coordinate
  602. // convHulls[3] = hull centroid Y coordinate
  603. // convHulls[4] = hull centroid Z coordinate
  604. // convHulls[5] = first hull vertex X
  605. // convHulls[6] = first hull vertex Y
  606. // convHulls[7] = first hull vertex Z
  607. // convHulls[8] = second hull vertex X
  608. // ...
  609. // convHulls[n] = number of vertices in second hull
  610. // convHulls[n+1] = second hull centroid X coordinate
  611. // ...
  612. //
  613. // TODO: is is very inefficient. Someday change the convex hull generator to return
  614. // data structures that do not need to be converted in order to pass to Bullet.
  615. // And maybe put the values directly into pinned memory rather than marshaling.
  616. int hullCount = m_hulls.Count;
  617. int totalVertices = 1; // include one for the count of the hulls
  618. foreach (ConvexResult cr in m_hulls)
  619. {
  620. totalVertices += 4; // add four for the vertex count and centroid
  621. totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
  622. }
  623. float[] convHulls = new float[totalVertices];
  624. convHulls[0] = (float)hullCount;
  625. int jj = 1;
  626. foreach (ConvexResult cr in m_hulls)
  627. {
  628. // copy vertices for index access
  629. float3[] verts = new float3[cr.HullVertices.Count];
  630. int kk = 0;
  631. foreach (float3 ff in cr.HullVertices)
  632. {
  633. verts[kk++] = ff;
  634. }
  635. // add to the array one hull's worth of data
  636. convHulls[jj++] = cr.HullIndices.Count;
  637. convHulls[jj++] = 0f; // centroid x,y,z
  638. convHulls[jj++] = 0f;
  639. convHulls[jj++] = 0f;
  640. foreach (int ind in cr.HullIndices)
  641. {
  642. convHulls[jj++] = verts[ind].x;
  643. convHulls[jj++] = verts[ind].y;
  644. convHulls[jj++] = verts[ind].z;
  645. }
  646. }
  647. // create the hull data structure in Bullet
  648. newShape = physicsScene.PE.CreateHullShape(physicsScene.World, hullCount, convHulls);
  649. }
  650. newShape.shapeKey = newHullKey;
  651. }
  652. return newShape;
  653. }
  654. // Callback from convex hull creater with a newly created hull.
  655. // Just add it to our collection of hulls for this shape.
  656. private void HullReturn(ConvexResult result)
  657. {
  658. m_hulls.Add(result);
  659. return;
  660. }
  661. // Loop through all the known hulls and return the description based on the physical address.
  662. public static bool TryGetHullByPtr(BulletShape pShape, out BSShapeHull outHull)
  663. {
  664. bool ret = false;
  665. BSShapeHull foundDesc = null;
  666. lock (Hulls)
  667. {
  668. foreach (BSShapeHull sh in Hulls.Values)
  669. {
  670. if (sh.physShapeInfo.ReferenceSame(pShape))
  671. {
  672. foundDesc = sh;
  673. ret = true;
  674. break;
  675. }
  676. }
  677. }
  678. outHull = foundDesc;
  679. return ret;
  680. }
  681. }
  682. // ============================================================================================================
  683. public class BSShapeCompound : BSShape
  684. {
  685. private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]";
  686. public BSShapeCompound(BulletShape pShape) : base(pShape)
  687. {
  688. }
  689. public static BSShape GetReference(BSScene physicsScene)
  690. {
  691. // Base compound shapes are not shared so this returns a raw shape.
  692. // A built compound shape can be reused in linksets.
  693. return new BSShapeCompound(CreatePhysicalCompoundShape(physicsScene));
  694. }
  695. public override BSShape GetReference(BSScene physicsScene, BSPhysObject prim)
  696. {
  697. // Calling this reference means we want another handle to an existing compound shape
  698. // (usually linksets) so return this copy.
  699. IncrementReference();
  700. return this;
  701. }
  702. // Dereferencing a compound shape releases the hold on all the child shapes.
  703. public override void Dereference(BSScene physicsScene)
  704. {
  705. lock (physShapeInfo)
  706. {
  707. this.DecrementReference();
  708. physicsScene.DetailLog("{0},BSShapeCompound.Dereference,shape={1}", BSScene.DetailLogZero, this);
  709. if (referenceCount <= 0)
  710. {
  711. if (!physicsScene.PE.IsCompound(physShapeInfo))
  712. {
  713. // Failed the sanity check!!
  714. physicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}",
  715. LogHeader, physShapeInfo.shapeType, physShapeInfo.AddrString);
  716. physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}",
  717. BSScene.DetailLogZero, physShapeInfo.shapeType, physShapeInfo.AddrString);
  718. return;
  719. }
  720. int numChildren = physicsScene.PE.GetNumberOfCompoundChildren(physShapeInfo);
  721. physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}",
  722. BSScene.DetailLogZero, physShapeInfo, numChildren);
  723. // Loop through all the children dereferencing each.
  724. for (int ii = numChildren - 1; ii >= 0; ii--)
  725. {
  726. BulletShape childShape = physicsScene.PE.RemoveChildShapeFromCompoundShapeIndex(physShapeInfo, ii);
  727. DereferenceAnonCollisionShape(physicsScene, childShape);
  728. }
  729. physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo);
  730. }
  731. }
  732. }
  733. private static BulletShape CreatePhysicalCompoundShape(BSScene physicsScene)
  734. {
  735. BulletShape cShape = physicsScene.PE.CreateCompoundShape(physicsScene.World, false);
  736. return cShape;
  737. }
  738. // Sometimes we have a pointer to a collision shape but don't know what type it is.
  739. // Figure out type and call the correct dereference routine.
  740. // Called at taint-time.
  741. private void DereferenceAnonCollisionShape(BSScene physicsScene, BulletShape pShape)
  742. {
  743. BSShapeMesh meshDesc;
  744. if (BSShapeMesh.TryGetMeshByPtr(pShape, out meshDesc))
  745. {
  746. meshDesc.Dereference(physicsScene);
  747. }
  748. else
  749. {
  750. BSShapeHull hullDesc;
  751. if (BSShapeHull.TryGetHullByPtr(pShape, out hullDesc))
  752. {
  753. hullDesc.Dereference(physicsScene);
  754. }
  755. else
  756. {
  757. BSShapeConvexHull chullDesc;
  758. if (BSShapeConvexHull.TryGetHullByPtr(pShape, out chullDesc))
  759. {
  760. chullDesc.Dereference(physicsScene);
  761. }
  762. else
  763. {
  764. if (physicsScene.PE.IsCompound(pShape))
  765. {
  766. BSShapeCompound recursiveCompound = new BSShapeCompound(pShape);
  767. recursiveCompound.Dereference(physicsScene);
  768. }
  769. else
  770. {
  771. if (physicsScene.PE.IsNativeShape(pShape))
  772. {
  773. BSShapeNative nativeShape = new BSShapeNative(pShape);
  774. nativeShape.Dereference(physicsScene);
  775. }
  776. }
  777. }
  778. }
  779. }
  780. }
  781. }
  782. // ============================================================================================================
  783. public class BSShapeConvexHull : BSShape
  784. {
  785. private static string LogHeader = "[BULLETSIM SHAPE CONVEX HULL]";
  786. public static Dictionary<System.UInt64, BSShapeConvexHull> ConvexHulls = new Dictionary<System.UInt64, BSShapeConvexHull>();
  787. public BSShapeConvexHull(BulletShape pShape) : base(pShape)
  788. {
  789. }
  790. public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
  791. {
  792. float lod;
  793. System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
  794. physicsScene.DetailLog("{0},BSShapeMesh,getReference,newKey={1},size={2},lod={3}",
  795. prim.LocalID, newMeshKey.ToString("X"), prim.Size, lod);
  796. BSShapeConvexHull retConvexHull = null;
  797. lock (ConvexHulls)
  798. {
  799. if (ConvexHulls.TryGetValue(newMeshKey, out retConvexHull))
  800. {
  801. // The mesh has already been created. Return a new reference to same.
  802. retConvexHull.IncrementReference();
  803. }
  804. else
  805. {
  806. retConvexHull = new BSShapeConvexHull(new BulletShape());
  807. BulletShape convexShape = null;
  808. // Get a handle to a mesh to build the hull from
  809. BSShape baseMesh = BSShapeMesh.GetReference(physicsScene, false /* forceRebuild */, prim);
  810. if (baseMesh.physShapeInfo.isNativeShape)
  811. {
  812. // We get here if the mesh was not creatable. Could be waiting for an asset from the disk.
  813. // In the short term, we return the native shape and a later ForceBodyShapeRebuild should
  814. // get back to this code with a buildable mesh.
  815. // TODO: not sure the temp native shape is freed when the mesh is rebuilt. When does this get freed?
  816. convexShape = baseMesh.physShapeInfo;
  817. }
  818. else
  819. {
  820. convexShape = physicsScene.PE.BuildConvexHullShapeFromMesh(physicsScene.World, baseMesh.physShapeInfo);
  821. convexShape.shapeKey = newMeshKey;
  822. ConvexHulls.Add(convexShape.shapeKey, retConvexHull);
  823. }
  824. // Done with the base mesh
  825. baseMesh.Dereference(physicsScene);
  826. retConvexHull.physShapeInfo = convexShape;
  827. }
  828. }
  829. return retConvexHull;
  830. }
  831. public override BSShape GetReference(BSScene physicsScene, BSPhysObject prim)
  832. {
  833. // Calling this reference means we want another handle to an existing shape
  834. // (usually linksets) so return this copy.
  835. IncrementReference();
  836. return this;
  837. }
  838. // Dereferencing a compound shape releases the hold on all the child shapes.
  839. public override void Dereference(BSScene physicsScene)
  840. {
  841. lock (ConvexHulls)
  842. {
  843. this.DecrementReference();
  844. physicsScene.DetailLog("{0},BSShapeConvexHull.Dereference,shape={1}", BSScene.DetailLogZero, this);
  845. // TODO: schedule aging and destruction of unused meshes.
  846. }
  847. }
  848. // Loop through all the known hulls and return the description based on the physical address.
  849. public static bool TryGetHullByPtr(BulletShape pShape, out BSShapeConvexHull outHull)
  850. {
  851. bool ret = false;
  852. BSShapeConvexHull foundDesc = null;
  853. lock (ConvexHulls)
  854. {
  855. foreach (BSShapeConvexHull sh in ConvexHulls.Values)
  856. {
  857. if (sh.physShapeInfo.ReferenceSame(pShape))
  858. {
  859. foundDesc = sh;
  860. ret = true;
  861. break;
  862. }
  863. }
  864. }
  865. outHull = foundDesc;
  866. return ret;
  867. }
  868. }
  869. // ============================================================================================================
  870. public class BSShapeAvatar : BSShape
  871. {
  872. private static string LogHeader = "[BULLETSIM SHAPE AVATAR]";
  873. public BSShapeAvatar() : base()
  874. {
  875. }
  876. public static BSShape GetReference(BSPhysObject prim)
  877. {
  878. return new BSShapeNull();
  879. }
  880. public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim)
  881. {
  882. return new BSShapeNull();
  883. }
  884. public override void Dereference(BSScene physicsScene) { }
  885. // From the front:
  886. // A---A
  887. // / \
  888. // B-------B
  889. // / \ +Z
  890. // C-----------C |
  891. // \ / -Y --+-- +Y
  892. // \ / |
  893. // \ / -Z
  894. // D-----D
  895. // \ /
  896. // E-E
  897. // From the top A and E are just lines.
  898. // B, C and D are hexagons:
  899. //
  900. // C1--C2 +X
  901. // / \ |
  902. // C0 C3 -Y --+-- +Y
  903. // \ / |
  904. // C5--C4 -X
  905. // Zero goes directly through the middle so the offsets are from that middle axis
  906. // and up and down from a middle horizon (A and E are the same distance from the zero).
  907. // The height, width and depth is one. All scaling is done by the simulator.
  908. // Z component -- how far the level is from the middle zero
  909. private const float Aup = 0.5f;
  910. private const float Bup = 0.4f;
  911. private const float Cup = 0.3f;
  912. private const float Dup = -0.4f;
  913. private const float Eup = -0.5f;
  914. // Y component -- distance from center to x0 and x3
  915. private const float Awid = 0.25f;
  916. private const float Bwid = 0.3f;
  917. private const float Cwid = 0.5f;
  918. private const float Dwid = 0.3f;
  919. private const float Ewid = 0.2f;
  920. // Y component -- distance from center to x1, x2, x4 and x5
  921. private const float Afwid = 0.0f;
  922. private const float Bfwid = 0.2f;
  923. private const float Cfwid = 0.4f;
  924. private const float Dfwid = 0.2f;
  925. private const float Efwid = 0.0f;
  926. // X component -- distance from zero to the front or back of a level
  927. private const float Adep = 0f;
  928. private const float Bdep = 0.3f;
  929. private const float Cdep = 0.5f;
  930. private const float Ddep = 0.2f;
  931. private const float Edep = 0f;
  932. private OMV.Vector3[] avatarVertices = {
  933. new OMV.Vector3( 0.0f, -Awid, Aup), // A0
  934. new OMV.Vector3( 0.0f, +Awid, Aup), // A3
  935. new OMV.Vector3( 0.0f, -Bwid, Bup), // B0
  936. new OMV.Vector3(+Bdep, -Bfwid, Bup), // B1
  937. new OMV.Vector3(+Bdep, +Bfwid, Bup), // B2
  938. new OMV.Vector3( 0.0f, +Bwid, Bup), // B3
  939. new OMV.Vector3(-Bdep, +Bfwid, Bup), // B4
  940. new OMV.Vector3(-Bdep, -Bfwid, Bup), // B5
  941. new OMV.Vector3( 0.0f, -Cwid, Cup), // C0
  942. new OMV.Vector3(+Cdep, -Cfwid, Cup), // C1
  943. new OMV.Vector3(+Cdep, +Cfwid, Cup), // C2
  944. new OMV.Vector3( 0.0f, +Cwid, Cup), // C3
  945. new OMV.Vector3(-Cdep, +Cfwid, Cup), // C4
  946. new OMV.Vector3(-Cdep, -Cfwid, Cup), // C5
  947. new OMV.Vector3( 0.0f, -Dwid, Dup), // D0
  948. new OMV.Vector3(+Ddep, -Dfwid, Dup), // D1
  949. new OMV.Vector3(+Ddep, +Dfwid, Dup), // D2
  950. new OMV.Vector3( 0.0f, +Dwid, Dup), // D3
  951. new OMV.Vector3(-Ddep, +Dfwid, Dup), // D4
  952. new OMV.Vector3(-Ddep, -Dfwid, Dup), // D5
  953. new OMV.Vector3( 0.0f, -Ewid, Eup), // E0
  954. new OMV.Vector3( 0.0f, +Ewid, Eup), // E3
  955. };
  956. // Offsets of the vertices in the vertices array
  957. private enum Ind : int
  958. {
  959. A0, A3,
  960. B0, B1, B2, B3, B4, B5,
  961. C0, C1, C2, C3, C4, C5,
  962. D0, D1, D2, D3, D4, D5,
  963. E0, E3
  964. }
  965. // Comments specify trianges and quads in clockwise direction
  966. private Ind[] avatarIndices = {
  967. Ind.A0, Ind.B0, Ind.B1, // A0,B0,B1
  968. Ind.A0, Ind.B1, Ind.B2, Ind.B2, Ind.A3, Ind.A0, // A0,B1,B2,A3
  969. Ind.A3, Ind.B2, Ind.B3, // A3,B2,B3
  970. Ind.A3, Ind.B3, Ind.B4, // A3,B3,B4
  971. Ind.A3, Ind.B4, Ind.B5, Ind.B5, Ind.A0, Ind.A3, // A3,B4,B5,A0
  972. Ind.A0, Ind.B5, Ind.B0, // A0,B5,B0
  973. Ind.B0, Ind.C0, Ind.C1, Ind.C1, Ind.B1, Ind.B0, // B0,C0,C1,B1
  974. Ind.B1, Ind.C1, Ind.C2, Ind.C2, Ind.B2, Ind.B1, // B1,C1,C2,B2
  975. Ind.B2, Ind.C2, Ind.C3, Ind.C3, Ind.B3, Ind.B2, // B2,C2,C3,B3
  976. Ind.B3, Ind.C3, Ind.C4, Ind.C4, Ind.B4, Ind.B3, // B3,C3,C4,B4
  977. Ind.B4, Ind.C4, Ind.C5, Ind.C5, Ind.B5, Ind.B4, // B4,C4,C5,B5
  978. Ind.B5, Ind.C5, Ind.C0, Ind.C0, Ind.B0, Ind.B5, // B5,C5,C0,B0
  979. Ind.C0, Ind.D0, Ind.D1, Ind.D1, Ind.C1, Ind.C0, // C0,D0,D1,C1
  980. Ind.C1, Ind.D1, Ind.D2, Ind.D2, Ind.C2, Ind.C1, // C1,D1,D2,C2
  981. Ind.C2, Ind.D2, Ind.D3, Ind.D3, Ind.C3, Ind.C2, // C2,D2,D3,C3
  982. Ind.C3, Ind.D3, Ind.D4, Ind.D4, Ind.C4, Ind.C3, // C3,D3,D4,C4
  983. Ind.C4, Ind.D4, Ind.D5, Ind.D5, Ind.C5, Ind.C4, // C4,D4,D5,C5
  984. Ind.C5, Ind.D5, Ind.D0, Ind.D0, Ind.C0, Ind.C5, // C5,D5,D0,C0
  985. Ind.E0, Ind.D0, Ind.D1, // E0,D0,D1
  986. Ind.E0, Ind.D1, Ind.D2, Ind.D2, Ind.E3, Ind.E0, // E0,D1,D2,E3
  987. Ind.E3, Ind.D2, Ind.D3, // E3,D2,D3
  988. Ind.E3, Ind.D3, Ind.D4, // E3,D3,D4
  989. Ind.E3, Ind.D4, Ind.D5, Ind.D5, Ind.E0, Ind.E3, // E3,D4,D5,E0
  990. Ind.E0, Ind.D5, Ind.D0, // E0,D5,D0
  991. };
  992. }
  993. }