diff options
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs')
-rwxr-xr-x | OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs | 1163 |
1 files changed, 1078 insertions, 85 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs index ee18379..fe5ff6c 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapes.cs | |||
@@ -29,115 +29,312 @@ using System; | |||
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.Text; | 30 | using System.Text; |
31 | 31 | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Region.Physics.Manager; | ||
34 | using OpenSim.Region.Physics.Meshing; | ||
35 | using OpenSim.Region.Physics.ConvexDecompositionDotNet; | ||
36 | |||
32 | using OMV = OpenMetaverse; | 37 | using OMV = OpenMetaverse; |
33 | 38 | ||
34 | namespace OpenSim.Region.Physics.BulletSPlugin | 39 | namespace OpenSim.Region.Physics.BulletSPlugin |
35 | { | 40 | { |
36 | public abstract class BSShape | 41 | public abstract class BSShape |
37 | { | 42 | { |
43 | private static string LogHeader = "[BULLETSIM SHAPE]"; | ||
44 | |||
38 | public int referenceCount { get; set; } | 45 | public int referenceCount { get; set; } |
39 | public DateTime lastReferenced { get; set; } | 46 | public DateTime lastReferenced { get; set; } |
47 | public BulletShape physShapeInfo { get; set; } | ||
40 | 48 | ||
41 | public BSShape() | 49 | public BSShape() |
42 | { | 50 | { |
43 | referenceCount = 0; | 51 | referenceCount = 1; |
44 | lastReferenced = DateTime.Now; | 52 | lastReferenced = DateTime.Now; |
53 | physShapeInfo = new BulletShape(); | ||
45 | } | 54 | } |
46 | 55 | public BSShape(BulletShape pShape) | |
47 | // Get a reference to a physical shape. Create if it doesn't exist | ||
48 | public static BSShape GetShapeReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) | ||
49 | { | 56 | { |
50 | BSShape ret = null; | 57 | referenceCount = 1; |
51 | 58 | lastReferenced = DateTime.Now; | |
52 | if (prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE) | 59 | physShapeInfo = pShape; |
53 | { | 60 | } |
54 | // an avatar capsule is close to a native shape (it is not shared) | ||
55 | ret = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_CAPSULE, | ||
56 | FixedShapeKey.KEY_CAPSULE); | ||
57 | physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret); | ||
58 | } | ||
59 | |||
60 | // Compound shapes are handled special as they are rebuilt from scratch. | ||
61 | // This isn't too great a hardship since most of the child shapes will have already been created. | ||
62 | if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND) | ||
63 | { | ||
64 | // Getting a reference to a compound shape gets you the compound shape with the root prim shape added | ||
65 | ret = BSShapeCompound.GetReference(prim); | ||
66 | physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, ret); | ||
67 | } | ||
68 | |||
69 | // Avatars have their own unique shape | ||
70 | if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_AVATAR) | ||
71 | { | ||
72 | // Getting a reference to a compound shape gets you the compound shape with the root prim shape added | ||
73 | ret = BSShapeAvatar.GetReference(prim); | ||
74 | physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,avatarShape,shape={1}", prim.LocalID, ret); | ||
75 | } | ||
76 | 61 | ||
77 | if (ret == null) | 62 | // Get another reference to this shape. |
78 | ret = GetShapeReferenceNonSpecial(physicsScene, forceRebuild, prim); | 63 | public abstract BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim); |
79 | 64 | ||
80 | return ret; | 65 | // Called when this shape is being used again. |
81 | } | 66 | // Used internally. External callers should call instance.GetReference() to properly copy/reference |
82 | public static BSShape GetShapeReferenceNonSpecial(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) | 67 | // the shape. |
68 | protected virtual void IncrementReference() | ||
83 | { | 69 | { |
84 | return null; | 70 | referenceCount++; |
71 | lastReferenced = DateTime.Now; | ||
85 | } | 72 | } |
86 | public static BSShape GetShapeReferenceNonNative(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) | 73 | |
74 | // Called when this shape is done being used. | ||
75 | protected virtual void DecrementReference() | ||
87 | { | 76 | { |
88 | return null; | 77 | referenceCount--; |
78 | lastReferenced = DateTime.Now; | ||
89 | } | 79 | } |
90 | 80 | ||
91 | // Release the use of a physical shape. | 81 | // Release the use of a physical shape. |
92 | public abstract void Dereference(BSScene physicsScene); | 82 | public abstract void Dereference(BSScene physicsScene); |
93 | 83 | ||
94 | // All shapes have a static call to get a reference to the physical shape | 84 | // Return 'true' if there is an allocated physics physical shape under this class instance. |
95 | // protected abstract static BSShape GetReference(); | 85 | public virtual bool HasPhysicalShape |
86 | { | ||
87 | get | ||
88 | { | ||
89 | if (physShapeInfo != null) | ||
90 | return physShapeInfo.HasPhysicalShape; | ||
91 | return false; | ||
92 | } | ||
93 | } | ||
94 | public virtual BSPhysicsShapeType ShapeType | ||
95 | { | ||
96 | get | ||
97 | { | ||
98 | BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; | ||
99 | if (physShapeInfo != null && physShapeInfo.HasPhysicalShape) | ||
100 | ret = physShapeInfo.shapeType; | ||
101 | return ret; | ||
102 | } | ||
103 | } | ||
96 | 104 | ||
97 | // Returns a string for debugging that uniquily identifies the memory used by this instance | 105 | // Returns a string for debugging that uniquily identifies the memory used by this instance |
98 | public virtual string AddrString | 106 | public virtual string AddrString |
99 | { | 107 | { |
100 | get { return "unknown"; } | 108 | get |
109 | { | ||
110 | if (physShapeInfo != null) | ||
111 | return physShapeInfo.AddrString; | ||
112 | return "unknown"; | ||
113 | } | ||
101 | } | 114 | } |
102 | 115 | ||
103 | public override string ToString() | 116 | public override string ToString() |
104 | { | 117 | { |
105 | StringBuilder buff = new StringBuilder(); | 118 | StringBuilder buff = new StringBuilder(); |
106 | buff.Append("<p="); | 119 | if (physShapeInfo == null) |
107 | buff.Append(AddrString); | 120 | { |
121 | buff.Append("<noPhys"); | ||
122 | } | ||
123 | else | ||
124 | { | ||
125 | buff.Append("<phy="); | ||
126 | buff.Append(physShapeInfo.ToString()); | ||
127 | } | ||
108 | buff.Append(",c="); | 128 | buff.Append(",c="); |
109 | buff.Append(referenceCount.ToString()); | 129 | buff.Append(referenceCount.ToString()); |
110 | buff.Append(">"); | 130 | buff.Append(">"); |
111 | return buff.ToString(); | 131 | return buff.ToString(); |
112 | } | 132 | } |
133 | |||
134 | #region Common shape routines | ||
135 | // Create a hash of all the shape parameters to be used as a key for this particular shape. | ||
136 | public static System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod) | ||
137 | { | ||
138 | // level of detail based on size and type of the object | ||
139 | float lod = BSParam.MeshLOD; | ||
140 | if (pbs.SculptEntry) | ||
141 | lod = BSParam.SculptLOD; | ||
142 | |||
143 | // Mega prims usually get more detail because one can interact with shape approximations at this size. | ||
144 | float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z)); | ||
145 | if (maxAxis > BSParam.MeshMegaPrimThreshold) | ||
146 | lod = BSParam.MeshMegaPrimLOD; | ||
147 | |||
148 | retLod = lod; | ||
149 | return pbs.GetMeshKey(size, lod); | ||
150 | } | ||
151 | |||
152 | // The creation of a mesh or hull can fail if an underlying asset is not available. | ||
153 | // There are two cases: 1) the asset is not in the cache and it needs to be fetched; | ||
154 | // and 2) the asset cannot be converted (like failed decompression of JPEG2000s). | ||
155 | // The first case causes the asset to be fetched. The second case requires | ||
156 | // us to not loop forever. | ||
157 | // Called after creating a physical mesh or hull. If the physical shape was created, | ||
158 | // just return. | ||
159 | public static BulletShape VerifyMeshCreated(BSScene physicsScene, BulletShape newShape, BSPhysObject prim) | ||
160 | { | ||
161 | // If the shape was successfully created, nothing more to do | ||
162 | if (newShape.HasPhysicalShape) | ||
163 | return newShape; | ||
164 | |||
165 | // VerifyMeshCreated is called after trying to create the mesh. If we think the asset had been | ||
166 | // fetched but we end up here again, the meshing of the asset must have failed. | ||
167 | // Prevent trying to keep fetching the mesh by declaring failure. | ||
168 | if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) | ||
169 | { | ||
170 | prim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedMeshing; | ||
171 | physicsScene.Logger.WarnFormat("{0} Fetched asset would not mesh. prim={1}, texture={2}", | ||
172 | LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture); | ||
173 | physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,setFailed,prim={1},tex={2}", | ||
174 | prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture); | ||
175 | } | ||
176 | else | ||
177 | { | ||
178 | // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset | ||
179 | if (prim.BaseShape.SculptEntry | ||
180 | && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.FailedAssetFetch | ||
181 | && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.FailedMeshing | ||
182 | && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting | ||
183 | && prim.BaseShape.SculptTexture != OMV.UUID.Zero | ||
184 | ) | ||
185 | { | ||
186 | physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAsset,objNam={1},tex={2}", | ||
187 | prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture); | ||
188 | // Multiple requestors will know we're waiting for this asset | ||
189 | prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting; | ||
190 | |||
191 | BSPhysObject xprim = prim; | ||
192 | Util.FireAndForget(delegate | ||
193 | { | ||
194 | // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,inFireAndForget", xprim.LocalID); | ||
195 | RequestAssetDelegate assetProvider = physicsScene.RequestAssetMethod; | ||
196 | if (assetProvider != null) | ||
197 | { | ||
198 | BSPhysObject yprim = xprim; // probably not necessary, but, just in case. | ||
199 | assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) | ||
200 | { | ||
201 | // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,assetProviderCallback", xprim.LocalID); | ||
202 | bool assetFound = false; | ||
203 | string mismatchIDs = String.Empty; // DEBUG DEBUG | ||
204 | if (asset != null && yprim.BaseShape.SculptEntry) | ||
205 | { | ||
206 | if (yprim.BaseShape.SculptTexture.ToString() == asset.ID) | ||
207 | { | ||
208 | yprim.BaseShape.SculptData = asset.Data; | ||
209 | // This will cause the prim to see that the filler shape is not the right | ||
210 | // one and try again to build the object. | ||
211 | // No race condition with the normal shape setting since the rebuild is at taint time. | ||
212 | yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched; | ||
213 | yprim.ForceBodyShapeRebuild(false /* inTaintTime */); | ||
214 | assetFound = true; | ||
215 | } | ||
216 | else | ||
217 | { | ||
218 | mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID; | ||
219 | } | ||
220 | } | ||
221 | if (!assetFound) | ||
222 | { | ||
223 | yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedAssetFetch; | ||
224 | } | ||
225 | physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAssetCallback,found={1},isSculpt={2},ids={3}", | ||
226 | yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs ); | ||
227 | }); | ||
228 | } | ||
229 | else | ||
230 | { | ||
231 | xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedAssetFetch; | ||
232 | physicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}", | ||
233 | LogHeader, physicsScene.Name); | ||
234 | } | ||
235 | }); | ||
236 | } | ||
237 | else | ||
238 | { | ||
239 | if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedAssetFetch) | ||
240 | { | ||
241 | physicsScene.Logger.WarnFormat("{0} Mesh failed to fetch asset. prim={1}, texture={2}", | ||
242 | LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture); | ||
243 | physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,wasFailed,prim={1},tex={2}", | ||
244 | prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture); | ||
245 | } | ||
246 | if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedMeshing) | ||
247 | { | ||
248 | physicsScene.Logger.WarnFormat("{0} Mesh asset would not mesh. prim={1}, texture={2}", | ||
249 | LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture); | ||
250 | physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,wasFailedMeshing,prim={1},tex={2}", | ||
251 | prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture); | ||
252 | } | ||
253 | } | ||
254 | } | ||
255 | |||
256 | // While we wait for the mesh defining asset to be loaded, stick in a simple box for the object. | ||
257 | BSShape fillShape = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); | ||
258 | physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,boxTempShape", prim.LocalID); | ||
259 | |||
260 | return fillShape.physShapeInfo; | ||
261 | } | ||
262 | |||
263 | public static String UsefulPrimInfo(BSScene pScene, BSPhysObject prim) | ||
264 | { | ||
265 | StringBuilder buff = new StringBuilder(prim.PhysObjectName); | ||
266 | buff.Append("/pos="); | ||
267 | buff.Append(prim.RawPosition.ToString()); | ||
268 | if (pScene != null) | ||
269 | { | ||
270 | buff.Append("/rgn="); | ||
271 | buff.Append(pScene.Name); | ||
272 | } | ||
273 | return buff.ToString(); | ||
274 | } | ||
275 | |||
276 | #endregion // Common shape routines | ||
113 | } | 277 | } |
114 | 278 | ||
279 | // ============================================================================================================ | ||
115 | public class BSShapeNull : BSShape | 280 | public class BSShapeNull : BSShape |
116 | { | 281 | { |
117 | public BSShapeNull() : base() | 282 | public BSShapeNull() : base() |
118 | { | 283 | { |
119 | } | 284 | } |
120 | public static BSShape GetReference() { return new BSShapeNull(); } | 285 | public static BSShape GetReference() { return new BSShapeNull(); } |
286 | public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) { return new BSShapeNull(); } | ||
121 | public override void Dereference(BSScene physicsScene) { /* The magic of garbage collection will make this go away */ } | 287 | public override void Dereference(BSScene physicsScene) { /* The magic of garbage collection will make this go away */ } |
122 | } | 288 | } |
123 | 289 | ||
290 | // ============================================================================================================ | ||
124 | public class BSShapeNative : BSShape | 291 | public class BSShapeNative : BSShape |
125 | { | 292 | { |
126 | private static string LogHeader = "[BULLETSIM SHAPE NATIVE]"; | 293 | private static string LogHeader = "[BULLETSIM SHAPE NATIVE]"; |
127 | public BSShapeNative() : base() | 294 | public BSShapeNative(BulletShape pShape) : base(pShape) |
128 | { | 295 | { |
129 | } | 296 | } |
130 | public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim, | 297 | |
131 | BSPhysicsShapeType shapeType, FixedShapeKey shapeKey) | 298 | public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim, |
299 | BSPhysicsShapeType shapeType, FixedShapeKey shapeKey) | ||
132 | { | 300 | { |
133 | // Native shapes are not shared and are always built anew. | 301 | // Native shapes are not shared and are always built anew. |
134 | //return new BSShapeNative(physicsScene, prim, shapeType, shapeKey); | 302 | return new BSShapeNative(CreatePhysicalNativeShape(physicsScene, prim, shapeType, shapeKey)); |
135 | return null; | ||
136 | } | 303 | } |
137 | 304 | ||
138 | private BSShapeNative(BSScene physicsScene, BSPhysObject prim, | 305 | public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) |
139 | BSPhysicsShapeType shapeType, FixedShapeKey shapeKey) | ||
140 | { | 306 | { |
307 | // Native shapes are not shared so we return a new shape. | ||
308 | BSShape ret = null; | ||
309 | lock (physShapeInfo) | ||
310 | { | ||
311 | ret = new BSShapeNative(CreatePhysicalNativeShape(pPhysicsScene, pPrim, | ||
312 | physShapeInfo.shapeType, (FixedShapeKey)physShapeInfo.shapeKey)); | ||
313 | } | ||
314 | return ret; | ||
315 | } | ||
316 | |||
317 | // Make this reference to the physical shape go away since native shapes are not shared. | ||
318 | public override void Dereference(BSScene physicsScene) | ||
319 | { | ||
320 | // Native shapes are not tracked and are released immediately | ||
321 | lock (physShapeInfo) | ||
322 | { | ||
323 | if (physShapeInfo.HasPhysicalShape) | ||
324 | { | ||
325 | physicsScene.DetailLog("{0},BSShapeNative.Dereference,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this); | ||
326 | physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo); | ||
327 | } | ||
328 | physShapeInfo.Clear(); | ||
329 | // Garbage collection will free up this instance. | ||
330 | } | ||
331 | } | ||
332 | |||
333 | private static BulletShape CreatePhysicalNativeShape(BSScene physicsScene, BSPhysObject prim, | ||
334 | BSPhysicsShapeType shapeType, FixedShapeKey shapeKey) | ||
335 | { | ||
336 | BulletShape newShape; | ||
337 | |||
141 | ShapeData nativeShapeData = new ShapeData(); | 338 | ShapeData nativeShapeData = new ShapeData(); |
142 | nativeShapeData.Type = shapeType; | 339 | nativeShapeData.Type = shapeType; |
143 | nativeShapeData.ID = prim.LocalID; | 340 | nativeShapeData.ID = prim.LocalID; |
@@ -146,84 +343,880 @@ public class BSShapeNative : BSShape | |||
146 | nativeShapeData.MeshKey = (ulong)shapeKey; | 343 | nativeShapeData.MeshKey = (ulong)shapeKey; |
147 | nativeShapeData.HullKey = (ulong)shapeKey; | 344 | nativeShapeData.HullKey = (ulong)shapeKey; |
148 | 345 | ||
149 | |||
150 | /* | ||
151 | if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE) | 346 | if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE) |
152 | { | 347 | { |
153 | ptr = PhysicsScene.PE.BuildCapsuleShape(physicsScene.World, 1f, 1f, prim.Scale); | 348 | newShape = physicsScene.PE.BuildCapsuleShape(physicsScene.World, 1f, 1f, prim.Scale); |
154 | physicsScene.DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale); | 349 | physicsScene.DetailLog("{0},BSShapeNative,capsule,scale={1}", prim.LocalID, prim.Scale); |
155 | } | 350 | } |
156 | else | 351 | else |
157 | { | 352 | { |
158 | ptr = PhysicsScene.PE.BuildNativeShape(physicsScene.World, nativeShapeData); | 353 | newShape = physicsScene.PE.BuildNativeShape(physicsScene.World, nativeShapeData); |
159 | } | 354 | } |
160 | if (ptr == IntPtr.Zero) | 355 | if (!newShape.HasPhysicalShape) |
161 | { | 356 | { |
162 | physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", | 357 | physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", |
163 | LogHeader, prim.LocalID, shapeType); | 358 | LogHeader, prim.LocalID, shapeType); |
164 | } | 359 | } |
165 | type = shapeType; | 360 | newShape.shapeType = shapeType; |
166 | key = (UInt64)shapeKey; | 361 | newShape.isNativeShape = true; |
167 | */ | 362 | newShape.shapeKey = (UInt64)shapeKey; |
168 | } | 363 | return newShape; |
169 | // Make this reference to the physical shape go away since native shapes are not shared. | ||
170 | public override void Dereference(BSScene physicsScene) | ||
171 | { | ||
172 | /* | ||
173 | // Native shapes are not tracked and are released immediately | ||
174 | physicsScene.DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this); | ||
175 | PhysicsScene.PE.DeleteCollisionShape(physicsScene.World, this); | ||
176 | ptr = IntPtr.Zero; | ||
177 | // Garbage collection will free up this instance. | ||
178 | */ | ||
179 | } | 364 | } |
365 | |||
180 | } | 366 | } |
181 | 367 | ||
368 | // ============================================================================================================ | ||
182 | public class BSShapeMesh : BSShape | 369 | public class BSShapeMesh : BSShape |
183 | { | 370 | { |
184 | private static string LogHeader = "[BULLETSIM SHAPE MESH]"; | 371 | private static string LogHeader = "[BULLETSIM SHAPE MESH]"; |
185 | private static Dictionary<System.UInt64, BSShapeMesh> Meshes = new Dictionary<System.UInt64, BSShapeMesh>(); | 372 | public static Dictionary<System.UInt64, BSShapeMesh> Meshes = new Dictionary<System.UInt64, BSShapeMesh>(); |
186 | 373 | ||
187 | public BSShapeMesh() : base() | 374 | public BSShapeMesh(BulletShape pShape) : base(pShape) |
188 | { | 375 | { |
189 | } | 376 | } |
190 | public static BSShape GetReference() { return new BSShapeNull(); } | 377 | public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) |
191 | public override void Dereference(BSScene physicsScene) { } | 378 | { |
379 | float lod; | ||
380 | System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); | ||
381 | |||
382 | BSShapeMesh retMesh = null; | ||
383 | lock (Meshes) | ||
384 | { | ||
385 | if (Meshes.TryGetValue(newMeshKey, out retMesh)) | ||
386 | { | ||
387 | // The mesh has already been created. Return a new reference to same. | ||
388 | retMesh.IncrementReference(); | ||
389 | } | ||
390 | else | ||
391 | { | ||
392 | retMesh = new BSShapeMesh(new BulletShape()); | ||
393 | // An instance of this mesh has not been created. Build and remember same. | ||
394 | BulletShape newShape = retMesh.CreatePhysicalMesh(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod); | ||
395 | |||
396 | // Check to see if mesh was created (might require an asset). | ||
397 | newShape = VerifyMeshCreated(physicsScene, newShape, prim); | ||
398 | if (!newShape.isNativeShape | ||
399 | || prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedMeshing | ||
400 | || prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedAssetFetch) | ||
401 | { | ||
402 | // If a mesh was what was created, remember the built shape for later sharing. | ||
403 | // Also note that if meshing failed we put it in the mesh list as there is nothing else to do about the mesh. | ||
404 | Meshes.Add(newMeshKey, retMesh); | ||
405 | } | ||
406 | |||
407 | retMesh.physShapeInfo = newShape; | ||
408 | } | ||
409 | } | ||
410 | physicsScene.DetailLog("{0},BSShapeMesh,getReference,mesh={1},size={2},lod={3}", prim.LocalID, retMesh, prim.Size, lod); | ||
411 | return retMesh; | ||
412 | } | ||
413 | public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) | ||
414 | { | ||
415 | BSShape ret = null; | ||
416 | // If the underlying shape is native, the actual shape has not been build (waiting for asset) | ||
417 | // and we must create a copy of the native shape since they are never shared. | ||
418 | if (physShapeInfo.HasPhysicalShape && physShapeInfo.isNativeShape) | ||
419 | { | ||
420 | // TODO: decide when the native shapes should be freed. Check in Dereference? | ||
421 | ret = BSShapeNative.GetReference(pPhysicsScene, pPrim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); | ||
422 | } | ||
423 | else | ||
424 | { | ||
425 | // Another reference to this shape is just counted. | ||
426 | IncrementReference(); | ||
427 | ret = this; | ||
428 | } | ||
429 | return ret; | ||
430 | } | ||
431 | public override void Dereference(BSScene physicsScene) | ||
432 | { | ||
433 | lock (Meshes) | ||
434 | { | ||
435 | this.DecrementReference(); | ||
436 | physicsScene.DetailLog("{0},BSShapeMesh.Dereference,shape={1}", BSScene.DetailLogZero, this); | ||
437 | // TODO: schedule aging and destruction of unused meshes. | ||
438 | } | ||
439 | } | ||
440 | // Loop through all the known meshes and return the description based on the physical address. | ||
441 | public static bool TryGetMeshByPtr(BulletShape pShape, out BSShapeMesh outMesh) | ||
442 | { | ||
443 | bool ret = false; | ||
444 | BSShapeMesh foundDesc = null; | ||
445 | lock (Meshes) | ||
446 | { | ||
447 | foreach (BSShapeMesh sm in Meshes.Values) | ||
448 | { | ||
449 | if (sm.physShapeInfo.ReferenceSame(pShape)) | ||
450 | { | ||
451 | foundDesc = sm; | ||
452 | ret = true; | ||
453 | break; | ||
454 | } | ||
455 | |||
456 | } | ||
457 | } | ||
458 | outMesh = foundDesc; | ||
459 | return ret; | ||
460 | } | ||
461 | |||
462 | public delegate BulletShape CreateShapeCall(BulletWorld world, int indicesCount, int[] indices, int verticesCount, float[] vertices ); | ||
463 | private BulletShape CreatePhysicalMesh(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey, | ||
464 | PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) | ||
465 | { | ||
466 | return BSShapeMesh.CreatePhysicalMeshShape(physicsScene, prim, newMeshKey, pbs, size, lod, | ||
467 | (w, iC, i, vC, v) => physicsScene.PE.CreateMeshShape(w, iC, i, vC, v) ); | ||
468 | } | ||
469 | |||
470 | // Code that uses the mesher to create the index/vertices info for a trimesh shape. | ||
471 | // This is used by the passed 'makeShape' call to create the Bullet mesh shape. | ||
472 | // The actual build call is passed so this logic can be used by several of the shapes that use a | ||
473 | // simple mesh as their base shape. | ||
474 | public static BulletShape CreatePhysicalMeshShape(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey, | ||
475 | PrimitiveBaseShape pbs, OMV.Vector3 size, float lod, CreateShapeCall makeShape) | ||
476 | { | ||
477 | BulletShape newShape = new BulletShape(); | ||
478 | |||
479 | IMesh meshData = null; | ||
480 | lock (physicsScene.mesher) | ||
481 | { | ||
482 | meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, | ||
483 | false, // say it is not physical so a bounding box is not built | ||
484 | false, // do not cache the mesh and do not use previously built versions | ||
485 | false, | ||
486 | false | ||
487 | ); | ||
488 | } | ||
489 | |||
490 | if (meshData != null) | ||
491 | { | ||
492 | if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) | ||
493 | { | ||
494 | // Release the fetched asset data once it has been used. | ||
495 | pbs.SculptData = new byte[0]; | ||
496 | prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown; | ||
497 | } | ||
498 | |||
499 | int[] indices = meshData.getIndexListAsInt(); | ||
500 | int realIndicesIndex = indices.Length; | ||
501 | float[] verticesAsFloats = meshData.getVertexListAsFloat(); | ||
502 | |||
503 | if (BSParam.ShouldRemoveZeroWidthTriangles) | ||
504 | { | ||
505 | // Remove degenerate triangles. These are triangles with two of the vertices | ||
506 | // are the same. This is complicated by the problem that vertices are not | ||
507 | // made unique in sculpties so we have to compare the values in the vertex. | ||
508 | realIndicesIndex = 0; | ||
509 | for (int tri = 0; tri < indices.Length; tri += 3) | ||
510 | { | ||
511 | // Compute displacements into vertex array for each vertex of the triangle | ||
512 | int v1 = indices[tri + 0] * 3; | ||
513 | int v2 = indices[tri + 1] * 3; | ||
514 | int v3 = indices[tri + 2] * 3; | ||
515 | // Check to see if any two of the vertices are the same | ||
516 | if (!( ( verticesAsFloats[v1 + 0] == verticesAsFloats[v2 + 0] | ||
517 | && verticesAsFloats[v1 + 1] == verticesAsFloats[v2 + 1] | ||
518 | && verticesAsFloats[v1 + 2] == verticesAsFloats[v2 + 2]) | ||
519 | || ( verticesAsFloats[v2 + 0] == verticesAsFloats[v3 + 0] | ||
520 | && verticesAsFloats[v2 + 1] == verticesAsFloats[v3 + 1] | ||
521 | && verticesAsFloats[v2 + 2] == verticesAsFloats[v3 + 2]) | ||
522 | || ( verticesAsFloats[v1 + 0] == verticesAsFloats[v3 + 0] | ||
523 | && verticesAsFloats[v1 + 1] == verticesAsFloats[v3 + 1] | ||
524 | && verticesAsFloats[v1 + 2] == verticesAsFloats[v3 + 2]) ) | ||
525 | ) | ||
526 | { | ||
527 | // None of the vertices of the triangles are the same. This is a good triangle; | ||
528 | indices[realIndicesIndex + 0] = indices[tri + 0]; | ||
529 | indices[realIndicesIndex + 1] = indices[tri + 1]; | ||
530 | indices[realIndicesIndex + 2] = indices[tri + 2]; | ||
531 | realIndicesIndex += 3; | ||
532 | } | ||
533 | } | ||
534 | } | ||
535 | physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,key={1},origTri={2},realTri={3},numVerts={4}", | ||
536 | BSScene.DetailLogZero, newMeshKey.ToString("X"), indices.Length / 3, realIndicesIndex / 3, verticesAsFloats.Length / 3); | ||
537 | |||
538 | if (realIndicesIndex != 0) | ||
539 | { | ||
540 | newShape = makeShape(physicsScene.World, realIndicesIndex, indices, verticesAsFloats.Length / 3, verticesAsFloats); | ||
541 | } | ||
542 | else | ||
543 | { | ||
544 | // Force the asset condition to 'failed' so we won't try to keep fetching and processing this mesh. | ||
545 | prim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedMeshing; | ||
546 | physicsScene.Logger.DebugFormat("{0} All mesh triangles degenerate. Prim={1}", LogHeader, UsefulPrimInfo(physicsScene, prim) ); | ||
547 | physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,allDegenerate,key={1}", prim.LocalID, newMeshKey); | ||
548 | } | ||
549 | } | ||
550 | newShape.shapeKey = newMeshKey; | ||
551 | |||
552 | return newShape; | ||
553 | } | ||
192 | } | 554 | } |
193 | 555 | ||
556 | // ============================================================================================================ | ||
194 | public class BSShapeHull : BSShape | 557 | public class BSShapeHull : BSShape |
195 | { | 558 | { |
196 | private static string LogHeader = "[BULLETSIM SHAPE HULL]"; | 559 | private static string LogHeader = "[BULLETSIM SHAPE HULL]"; |
197 | private static Dictionary<System.UInt64, BSShapeHull> Hulls = new Dictionary<System.UInt64, BSShapeHull>(); | 560 | public static Dictionary<System.UInt64, BSShapeHull> Hulls = new Dictionary<System.UInt64, BSShapeHull>(); |
198 | 561 | ||
199 | public BSShapeHull() : base() | 562 | public BSShapeHull(BulletShape pShape) : base(pShape) |
200 | { | 563 | { |
201 | } | 564 | } |
202 | public static BSShape GetReference() { return new BSShapeNull(); } | 565 | public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) |
203 | public override void Dereference(BSScene physicsScene) { } | 566 | { |
567 | float lod; | ||
568 | System.UInt64 newHullKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); | ||
569 | |||
570 | BSShapeHull retHull = null; | ||
571 | lock (Hulls) | ||
572 | { | ||
573 | if (Hulls.TryGetValue(newHullKey, out retHull)) | ||
574 | { | ||
575 | // The mesh has already been created. Return a new reference to same. | ||
576 | retHull.IncrementReference(); | ||
577 | } | ||
578 | else | ||
579 | { | ||
580 | retHull = new BSShapeHull(new BulletShape()); | ||
581 | // An instance of this mesh has not been created. Build and remember same. | ||
582 | BulletShape newShape = retHull.CreatePhysicalHull(physicsScene, prim, newHullKey, prim.BaseShape, prim.Size, lod); | ||
583 | |||
584 | // Check to see if hull was created (might require an asset). | ||
585 | newShape = VerifyMeshCreated(physicsScene, newShape, prim); | ||
586 | if (!newShape.isNativeShape | ||
587 | || prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedMeshing | ||
588 | || prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedAssetFetch) | ||
589 | { | ||
590 | // If a mesh was what was created, remember the built shape for later sharing. | ||
591 | Hulls.Add(newHullKey, retHull); | ||
592 | } | ||
593 | retHull.physShapeInfo = newShape; | ||
594 | } | ||
595 | } | ||
596 | physicsScene.DetailLog("{0},BSShapeHull,getReference,hull={1},size={2},lod={3}", prim.LocalID, retHull, prim.Size, lod); | ||
597 | return retHull; | ||
598 | } | ||
599 | public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) | ||
600 | { | ||
601 | BSShape ret = null; | ||
602 | // If the underlying shape is native, the actual shape has not been build (waiting for asset) | ||
603 | // and we must create a copy of the native shape since they are never shared. | ||
604 | if (physShapeInfo.HasPhysicalShape && physShapeInfo.isNativeShape) | ||
605 | { | ||
606 | // TODO: decide when the native shapes should be freed. Check in Dereference? | ||
607 | ret = BSShapeNative.GetReference(pPhysicsScene, pPrim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); | ||
608 | } | ||
609 | else | ||
610 | { | ||
611 | // Another reference to this shape is just counted. | ||
612 | IncrementReference(); | ||
613 | ret = this; | ||
614 | } | ||
615 | return ret; | ||
616 | } | ||
617 | public override void Dereference(BSScene physicsScene) | ||
618 | { | ||
619 | lock (Hulls) | ||
620 | { | ||
621 | this.DecrementReference(); | ||
622 | physicsScene.DetailLog("{0},BSShapeHull.Dereference,shape={1}", BSScene.DetailLogZero, this); | ||
623 | // TODO: schedule aging and destruction of unused meshes. | ||
624 | } | ||
625 | } | ||
626 | List<ConvexResult> m_hulls; | ||
627 | private BulletShape CreatePhysicalHull(BSScene physicsScene, BSPhysObject prim, System.UInt64 newHullKey, | ||
628 | PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) | ||
629 | { | ||
630 | BulletShape newShape = new BulletShape(); | ||
631 | |||
632 | IMesh meshData = null; | ||
633 | List<List<OMV.Vector3>> allHulls = null; | ||
634 | lock (physicsScene.mesher) | ||
635 | { | ||
636 | // Pass true for physicalness as this prevents the creation of bounding box which is not needed | ||
637 | meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, true /* isPhysical */, false /* shouldCache */, false, false); | ||
638 | |||
639 | // If we should use the asset's hull info, fetch it out of the locked mesher | ||
640 | if (meshData != null && BSParam.ShouldUseAssetHulls) | ||
641 | { | ||
642 | Meshmerizer realMesher = physicsScene.mesher as Meshmerizer; | ||
643 | if (realMesher != null) | ||
644 | { | ||
645 | allHulls = realMesher.GetConvexHulls(size); | ||
646 | } | ||
647 | if (allHulls == null) | ||
648 | { | ||
649 | physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,assetHulls,noAssetHull", prim.LocalID); | ||
650 | } | ||
651 | } | ||
652 | } | ||
653 | |||
654 | // If there is hull data in the mesh asset, build the hull from that | ||
655 | if (allHulls != null && BSParam.ShouldUseAssetHulls) | ||
656 | { | ||
657 | int hullCount = allHulls.Count; | ||
658 | int totalVertices = 1; // include one for the count of the hulls | ||
659 | // Using the structure described for HACD hulls, create the memory sturcture | ||
660 | // to pass the hull data to the creater. | ||
661 | foreach (List<OMV.Vector3> hullVerts in allHulls) | ||
662 | { | ||
663 | totalVertices += 4; // add four for the vertex count and centroid | ||
664 | totalVertices += hullVerts.Count * 3; // one vertex is three dimensions | ||
665 | } | ||
666 | float[] convHulls = new float[totalVertices]; | ||
667 | |||
668 | convHulls[0] = (float)hullCount; | ||
669 | int jj = 1; | ||
670 | foreach (List<OMV.Vector3> hullVerts in allHulls) | ||
671 | { | ||
672 | convHulls[jj + 0] = hullVerts.Count; | ||
673 | convHulls[jj + 1] = 0f; // centroid x,y,z | ||
674 | convHulls[jj + 2] = 0f; | ||
675 | convHulls[jj + 3] = 0f; | ||
676 | jj += 4; | ||
677 | foreach (OMV.Vector3 oneVert in hullVerts) | ||
678 | { | ||
679 | convHulls[jj + 0] = oneVert.X; | ||
680 | convHulls[jj + 1] = oneVert.Y; | ||
681 | convHulls[jj + 2] = oneVert.Z; | ||
682 | jj += 3; | ||
683 | } | ||
684 | } | ||
685 | |||
686 | // create the hull data structure in Bullet | ||
687 | newShape = physicsScene.PE.CreateHullShape(physicsScene.World, hullCount, convHulls); | ||
688 | |||
689 | physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,assetHulls,hulls={1},totVert={2},shape={3}", | ||
690 | prim.LocalID, hullCount, totalVertices, newShape); | ||
691 | } | ||
692 | |||
693 | // If no hull specified in the asset and we should use Bullet's HACD approximation... | ||
694 | if (!newShape.HasPhysicalShape && BSParam.ShouldUseBulletHACD) | ||
695 | { | ||
696 | // Build the hull shape from an existing mesh shape. | ||
697 | // The mesh should have already been created in Bullet. | ||
698 | physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,bulletHACD,entry", prim.LocalID); | ||
699 | BSShape meshShape = BSShapeMesh.GetReference(physicsScene, true, prim); | ||
700 | |||
701 | if (meshShape.physShapeInfo.HasPhysicalShape) | ||
702 | { | ||
703 | HACDParams parms; | ||
704 | parms.maxVerticesPerHull = BSParam.BHullMaxVerticesPerHull; | ||
705 | parms.minClusters = BSParam.BHullMinClusters; | ||
706 | parms.compacityWeight = BSParam.BHullCompacityWeight; | ||
707 | parms.volumeWeight = BSParam.BHullVolumeWeight; | ||
708 | parms.concavity = BSParam.BHullConcavity; | ||
709 | parms.addExtraDistPoints = BSParam.NumericBool(BSParam.BHullAddExtraDistPoints); | ||
710 | parms.addNeighboursDistPoints = BSParam.NumericBool(BSParam.BHullAddNeighboursDistPoints); | ||
711 | parms.addFacesPoints = BSParam.NumericBool(BSParam.BHullAddFacesPoints); | ||
712 | parms.shouldAdjustCollisionMargin = BSParam.NumericBool(BSParam.BHullShouldAdjustCollisionMargin); | ||
713 | |||
714 | physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,beforeCall", prim.LocalID, newShape.HasPhysicalShape); | ||
715 | newShape = physicsScene.PE.BuildHullShapeFromMesh(physicsScene.World, meshShape.physShapeInfo, parms); | ||
716 | physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,shape={1}", prim.LocalID, newShape); | ||
717 | |||
718 | // Now done with the mesh shape. | ||
719 | meshShape.Dereference(physicsScene); | ||
720 | } | ||
721 | physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,bulletHACD,exit,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); | ||
722 | } | ||
723 | |||
724 | // If no other hull specifications, use our HACD hull approximation. | ||
725 | if (!newShape.HasPhysicalShape && meshData != null) | ||
726 | { | ||
727 | if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) | ||
728 | { | ||
729 | // Release the fetched asset data once it has been used. | ||
730 | pbs.SculptData = new byte[0]; | ||
731 | prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown; | ||
732 | } | ||
733 | |||
734 | int[] indices = meshData.getIndexListAsInt(); | ||
735 | List<OMV.Vector3> vertices = meshData.getVertexList(); | ||
736 | |||
737 | //format conversion from IMesh format to DecompDesc format | ||
738 | List<int> convIndices = new List<int>(); | ||
739 | List<float3> convVertices = new List<float3>(); | ||
740 | for (int ii = 0; ii < indices.GetLength(0); ii++) | ||
741 | { | ||
742 | convIndices.Add(indices[ii]); | ||
743 | } | ||
744 | foreach (OMV.Vector3 vv in vertices) | ||
745 | { | ||
746 | convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); | ||
747 | } | ||
748 | |||
749 | uint maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplit; | ||
750 | if (BSParam.CSHullMaxDepthSplit != BSParam.CSHullMaxDepthSplitForSimpleShapes) | ||
751 | { | ||
752 | // Simple primitive shapes we know are convex so they are better implemented with | ||
753 | // fewer hulls. | ||
754 | // Check for simple shape (prim without cuts) and reduce split parameter if so. | ||
755 | if (BSShapeCollection.PrimHasNoCuts(pbs)) | ||
756 | { | ||
757 | maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplitForSimpleShapes; | ||
758 | } | ||
759 | } | ||
760 | |||
761 | // setup and do convex hull conversion | ||
762 | m_hulls = new List<ConvexResult>(); | ||
763 | DecompDesc dcomp = new DecompDesc(); | ||
764 | dcomp.mIndices = convIndices; | ||
765 | dcomp.mVertices = convVertices; | ||
766 | dcomp.mDepth = maxDepthSplit; | ||
767 | dcomp.mCpercent = BSParam.CSHullConcavityThresholdPercent; | ||
768 | dcomp.mPpercent = BSParam.CSHullVolumeConservationThresholdPercent; | ||
769 | dcomp.mMaxVertices = (uint)BSParam.CSHullMaxVertices; | ||
770 | dcomp.mSkinWidth = BSParam.CSHullMaxSkinWidth; | ||
771 | ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); | ||
772 | // create the hull into the _hulls variable | ||
773 | convexBuilder.process(dcomp); | ||
774 | |||
775 | physicsScene.DetailLog("{0},BSShapeCollection.CreatePhysicalHull,key={1},inVert={2},inInd={3},split={4},hulls={5}", | ||
776 | BSScene.DetailLogZero, newHullKey, indices.GetLength(0), vertices.Count, maxDepthSplit, m_hulls.Count); | ||
777 | |||
778 | // Convert the vertices and indices for passing to unmanaged. | ||
779 | // The hull information is passed as a large floating point array. | ||
780 | // The format is: | ||
781 | // convHulls[0] = number of hulls | ||
782 | // convHulls[1] = number of vertices in first hull | ||
783 | // convHulls[2] = hull centroid X coordinate | ||
784 | // convHulls[3] = hull centroid Y coordinate | ||
785 | // convHulls[4] = hull centroid Z coordinate | ||
786 | // convHulls[5] = first hull vertex X | ||
787 | // convHulls[6] = first hull vertex Y | ||
788 | // convHulls[7] = first hull vertex Z | ||
789 | // convHulls[8] = second hull vertex X | ||
790 | // ... | ||
791 | // convHulls[n] = number of vertices in second hull | ||
792 | // convHulls[n+1] = second hull centroid X coordinate | ||
793 | // ... | ||
794 | // | ||
795 | // TODO: is is very inefficient. Someday change the convex hull generator to return | ||
796 | // data structures that do not need to be converted in order to pass to Bullet. | ||
797 | // And maybe put the values directly into pinned memory rather than marshaling. | ||
798 | int hullCount = m_hulls.Count; | ||
799 | int totalVertices = 1; // include one for the count of the hulls | ||
800 | foreach (ConvexResult cr in m_hulls) | ||
801 | { | ||
802 | totalVertices += 4; // add four for the vertex count and centroid | ||
803 | totalVertices += cr.HullIndices.Count * 3; // we pass just triangles | ||
804 | } | ||
805 | float[] convHulls = new float[totalVertices]; | ||
806 | |||
807 | convHulls[0] = (float)hullCount; | ||
808 | int jj = 1; | ||
809 | foreach (ConvexResult cr in m_hulls) | ||
810 | { | ||
811 | // copy vertices for index access | ||
812 | float3[] verts = new float3[cr.HullVertices.Count]; | ||
813 | int kk = 0; | ||
814 | foreach (float3 ff in cr.HullVertices) | ||
815 | { | ||
816 | verts[kk++] = ff; | ||
817 | } | ||
818 | |||
819 | // add to the array one hull's worth of data | ||
820 | convHulls[jj++] = cr.HullIndices.Count; | ||
821 | convHulls[jj++] = 0f; // centroid x,y,z | ||
822 | convHulls[jj++] = 0f; | ||
823 | convHulls[jj++] = 0f; | ||
824 | foreach (int ind in cr.HullIndices) | ||
825 | { | ||
826 | convHulls[jj++] = verts[ind].x; | ||
827 | convHulls[jj++] = verts[ind].y; | ||
828 | convHulls[jj++] = verts[ind].z; | ||
829 | } | ||
830 | } | ||
831 | // create the hull data structure in Bullet | ||
832 | newShape = physicsScene.PE.CreateHullShape(physicsScene.World, hullCount, convHulls); | ||
833 | } | ||
834 | newShape.shapeKey = newHullKey; | ||
835 | return newShape; | ||
836 | } | ||
837 | // Callback from convex hull creater with a newly created hull. | ||
838 | // Just add it to our collection of hulls for this shape. | ||
839 | private void HullReturn(ConvexResult result) | ||
840 | { | ||
841 | m_hulls.Add(result); | ||
842 | return; | ||
843 | } | ||
844 | // Loop through all the known hulls and return the description based on the physical address. | ||
845 | public static bool TryGetHullByPtr(BulletShape pShape, out BSShapeHull outHull) | ||
846 | { | ||
847 | bool ret = false; | ||
848 | BSShapeHull foundDesc = null; | ||
849 | lock (Hulls) | ||
850 | { | ||
851 | foreach (BSShapeHull sh in Hulls.Values) | ||
852 | { | ||
853 | if (sh.physShapeInfo.ReferenceSame(pShape)) | ||
854 | { | ||
855 | foundDesc = sh; | ||
856 | ret = true; | ||
857 | break; | ||
858 | } | ||
859 | |||
860 | } | ||
861 | } | ||
862 | outHull = foundDesc; | ||
863 | return ret; | ||
864 | } | ||
204 | } | 865 | } |
205 | 866 | ||
867 | // ============================================================================================================ | ||
206 | public class BSShapeCompound : BSShape | 868 | public class BSShapeCompound : BSShape |
207 | { | 869 | { |
208 | private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]"; | 870 | private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]"; |
209 | public BSShapeCompound() : base() | 871 | public static Dictionary<string, BSShapeCompound> CompoundShapes = new Dictionary<string, BSShapeCompound>(); |
872 | |||
873 | public BSShapeCompound(BulletShape pShape) : base(pShape) | ||
210 | { | 874 | { |
211 | } | 875 | } |
212 | public static BSShape GetReference(BSPhysObject prim) | 876 | public static BSShape GetReference(BSScene physicsScene) |
213 | { | 877 | { |
214 | return new BSShapeNull(); | 878 | // Base compound shapes are not shared so this returns a raw shape. |
879 | // A built compound shape can be reused in linksets. | ||
880 | BSShapeCompound ret = new BSShapeCompound(CreatePhysicalCompoundShape(physicsScene)); | ||
881 | CompoundShapes.Add(ret.AddrString, ret); | ||
882 | return ret; | ||
883 | } | ||
884 | public override BSShape GetReference(BSScene physicsScene, BSPhysObject prim) | ||
885 | { | ||
886 | // Calling this reference means we want another handle to an existing compound shape | ||
887 | // (usually linksets) so return this copy. | ||
888 | IncrementReference(); | ||
889 | return this; | ||
890 | } | ||
891 | // Dereferencing a compound shape releases the hold on all the child shapes. | ||
892 | public override void Dereference(BSScene physicsScene) | ||
893 | { | ||
894 | lock (physShapeInfo) | ||
895 | { | ||
896 | this.DecrementReference(); | ||
897 | physicsScene.DetailLog("{0},BSShapeCompound.Dereference,shape={1}", BSScene.DetailLogZero, this); | ||
898 | if (referenceCount <= 0) | ||
899 | { | ||
900 | if (!physicsScene.PE.IsCompound(physShapeInfo)) | ||
901 | { | ||
902 | // Failed the sanity check!! | ||
903 | physicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", | ||
904 | LogHeader, physShapeInfo.shapeType, physShapeInfo.AddrString); | ||
905 | physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", | ||
906 | BSScene.DetailLogZero, physShapeInfo.shapeType, physShapeInfo.AddrString); | ||
907 | return; | ||
908 | } | ||
909 | |||
910 | int numChildren = physicsScene.PE.GetNumberOfCompoundChildren(physShapeInfo); | ||
911 | physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", | ||
912 | BSScene.DetailLogZero, physShapeInfo, numChildren); | ||
913 | |||
914 | // Loop through all the children dereferencing each. | ||
915 | for (int ii = numChildren - 1; ii >= 0; ii--) | ||
916 | { | ||
917 | BulletShape childShape = physicsScene.PE.RemoveChildShapeFromCompoundShapeIndex(physShapeInfo, ii); | ||
918 | DereferenceAnonCollisionShape(physicsScene, childShape); | ||
919 | } | ||
920 | |||
921 | lock (CompoundShapes) | ||
922 | CompoundShapes.Remove(physShapeInfo.AddrString); | ||
923 | physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo); | ||
924 | } | ||
925 | } | ||
926 | } | ||
927 | public static bool TryGetCompoundByPtr(BulletShape pShape, out BSShapeCompound outCompound) | ||
928 | { | ||
929 | lock (CompoundShapes) | ||
930 | { | ||
931 | string addr = pShape.AddrString; | ||
932 | return CompoundShapes.TryGetValue(addr, out outCompound); | ||
933 | } | ||
934 | } | ||
935 | private static BulletShape CreatePhysicalCompoundShape(BSScene physicsScene) | ||
936 | { | ||
937 | BulletShape cShape = physicsScene.PE.CreateCompoundShape(physicsScene.World, false); | ||
938 | return cShape; | ||
939 | } | ||
940 | // Sometimes we have a pointer to a collision shape but don't know what type it is. | ||
941 | // Figure out type and call the correct dereference routine. | ||
942 | // Called at taint-time. | ||
943 | private void DereferenceAnonCollisionShape(BSScene physicsScene, BulletShape pShape) | ||
944 | { | ||
945 | // TODO: figure a better way to go through all the shape types and find a possible instance. | ||
946 | physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,shape={1}", | ||
947 | BSScene.DetailLogZero, pShape); | ||
948 | BSShapeMesh meshDesc; | ||
949 | if (BSShapeMesh.TryGetMeshByPtr(pShape, out meshDesc)) | ||
950 | { | ||
951 | meshDesc.Dereference(physicsScene); | ||
952 | // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundMesh,shape={1}", BSScene.DetailLogZero, pShape); | ||
953 | } | ||
954 | else | ||
955 | { | ||
956 | BSShapeHull hullDesc; | ||
957 | if (BSShapeHull.TryGetHullByPtr(pShape, out hullDesc)) | ||
958 | { | ||
959 | hullDesc.Dereference(physicsScene); | ||
960 | // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundHull,shape={1}", BSScene.DetailLogZero, pShape); | ||
961 | } | ||
962 | else | ||
963 | { | ||
964 | BSShapeConvexHull chullDesc; | ||
965 | if (BSShapeConvexHull.TryGetConvexHullByPtr(pShape, out chullDesc)) | ||
966 | { | ||
967 | chullDesc.Dereference(physicsScene); | ||
968 | // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundConvexHull,shape={1}", BSScene.DetailLogZero, pShape); | ||
969 | } | ||
970 | else | ||
971 | { | ||
972 | BSShapeGImpact gImpactDesc; | ||
973 | if (BSShapeGImpact.TryGetGImpactByPtr(pShape, out gImpactDesc)) | ||
974 | { | ||
975 | gImpactDesc.Dereference(physicsScene); | ||
976 | // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundgImpact,shape={1}", BSScene.DetailLogZero, pShape); | ||
977 | } | ||
978 | else | ||
979 | { | ||
980 | // Didn't find it in the lists of specific types. It could be compound. | ||
981 | BSShapeCompound compoundDesc; | ||
982 | if (BSShapeCompound.TryGetCompoundByPtr(pShape, out compoundDesc)) | ||
983 | { | ||
984 | compoundDesc.Dereference(physicsScene); | ||
985 | // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,recursiveCompoundShape,shape={1}", BSScene.DetailLogZero, pShape); | ||
986 | } | ||
987 | else | ||
988 | { | ||
989 | // If none of the above, maybe it is a simple native shape. | ||
990 | if (physicsScene.PE.IsNativeShape(pShape)) | ||
991 | { | ||
992 | // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,assumingNative,shape={1}", BSScene.DetailLogZero, pShape); | ||
993 | BSShapeNative nativeShape = new BSShapeNative(pShape); | ||
994 | nativeShape.Dereference(physicsScene); | ||
995 | } | ||
996 | } | ||
997 | } | ||
998 | } | ||
999 | } | ||
1000 | } | ||
1001 | } | ||
1002 | } | ||
1003 | |||
1004 | // ============================================================================================================ | ||
1005 | public class BSShapeConvexHull : BSShape | ||
1006 | { | ||
1007 | private static string LogHeader = "[BULLETSIM SHAPE CONVEX HULL]"; | ||
1008 | public static Dictionary<System.UInt64, BSShapeConvexHull> ConvexHulls = new Dictionary<System.UInt64, BSShapeConvexHull>(); | ||
1009 | |||
1010 | public BSShapeConvexHull(BulletShape pShape) : base(pShape) | ||
1011 | { | ||
1012 | } | ||
1013 | public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) | ||
1014 | { | ||
1015 | float lod; | ||
1016 | System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); | ||
1017 | |||
1018 | physicsScene.DetailLog("{0},BSShapeConvexHull,getReference,newKey={1},size={2},lod={3}", | ||
1019 | prim.LocalID, newMeshKey.ToString("X"), prim.Size, lod); | ||
1020 | |||
1021 | BSShapeConvexHull retConvexHull = null; | ||
1022 | lock (ConvexHulls) | ||
1023 | { | ||
1024 | if (ConvexHulls.TryGetValue(newMeshKey, out retConvexHull)) | ||
1025 | { | ||
1026 | // The mesh has already been created. Return a new reference to same. | ||
1027 | retConvexHull.IncrementReference(); | ||
1028 | } | ||
1029 | else | ||
1030 | { | ||
1031 | retConvexHull = new BSShapeConvexHull(new BulletShape()); | ||
1032 | BulletShape convexShape = null; | ||
1033 | |||
1034 | // Get a handle to a mesh to build the hull from | ||
1035 | BSShape baseMesh = BSShapeMesh.GetReference(physicsScene, false /* forceRebuild */, prim); | ||
1036 | if (baseMesh.physShapeInfo.isNativeShape) | ||
1037 | { | ||
1038 | // We get here if the mesh was not creatable. Could be waiting for an asset from the disk. | ||
1039 | // In the short term, we return the native shape and a later ForceBodyShapeRebuild should | ||
1040 | // get back to this code with a buildable mesh. | ||
1041 | // TODO: not sure the temp native shape is freed when the mesh is rebuilt. When does this get freed? | ||
1042 | convexShape = baseMesh.physShapeInfo; | ||
1043 | } | ||
1044 | else | ||
1045 | { | ||
1046 | convexShape = physicsScene.PE.BuildConvexHullShapeFromMesh(physicsScene.World, baseMesh.physShapeInfo); | ||
1047 | convexShape.shapeKey = newMeshKey; | ||
1048 | ConvexHulls.Add(convexShape.shapeKey, retConvexHull); | ||
1049 | physicsScene.DetailLog("{0},BSShapeConvexHull.GetReference,addingNewlyCreatedShape,shape={1}", | ||
1050 | BSScene.DetailLogZero, convexShape); | ||
1051 | } | ||
1052 | |||
1053 | // Done with the base mesh | ||
1054 | baseMesh.Dereference(physicsScene); | ||
1055 | |||
1056 | retConvexHull.physShapeInfo = convexShape; | ||
1057 | } | ||
1058 | } | ||
1059 | return retConvexHull; | ||
1060 | } | ||
1061 | public override BSShape GetReference(BSScene physicsScene, BSPhysObject prim) | ||
1062 | { | ||
1063 | // Calling this reference means we want another handle to an existing shape | ||
1064 | // (usually linksets) so return this copy. | ||
1065 | IncrementReference(); | ||
1066 | return this; | ||
1067 | } | ||
1068 | // Dereferencing a compound shape releases the hold on all the child shapes. | ||
1069 | public override void Dereference(BSScene physicsScene) | ||
1070 | { | ||
1071 | lock (ConvexHulls) | ||
1072 | { | ||
1073 | this.DecrementReference(); | ||
1074 | physicsScene.DetailLog("{0},BSShapeConvexHull.Dereference,shape={1}", BSScene.DetailLogZero, this); | ||
1075 | // TODO: schedule aging and destruction of unused meshes. | ||
1076 | } | ||
1077 | } | ||
1078 | // Loop through all the known hulls and return the description based on the physical address. | ||
1079 | public static bool TryGetConvexHullByPtr(BulletShape pShape, out BSShapeConvexHull outHull) | ||
1080 | { | ||
1081 | bool ret = false; | ||
1082 | BSShapeConvexHull foundDesc = null; | ||
1083 | lock (ConvexHulls) | ||
1084 | { | ||
1085 | foreach (BSShapeConvexHull sh in ConvexHulls.Values) | ||
1086 | { | ||
1087 | if (sh.physShapeInfo.ReferenceSame(pShape)) | ||
1088 | { | ||
1089 | foundDesc = sh; | ||
1090 | ret = true; | ||
1091 | break; | ||
1092 | } | ||
1093 | |||
1094 | } | ||
1095 | } | ||
1096 | outHull = foundDesc; | ||
1097 | return ret; | ||
1098 | } | ||
1099 | } | ||
1100 | // ============================================================================================================ | ||
1101 | public class BSShapeGImpact : BSShape | ||
1102 | { | ||
1103 | private static string LogHeader = "[BULLETSIM SHAPE GIMPACT]"; | ||
1104 | public static Dictionary<System.UInt64, BSShapeGImpact> GImpacts = new Dictionary<System.UInt64, BSShapeGImpact>(); | ||
1105 | |||
1106 | public BSShapeGImpact(BulletShape pShape) : base(pShape) | ||
1107 | { | ||
1108 | } | ||
1109 | public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) | ||
1110 | { | ||
1111 | float lod; | ||
1112 | System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); | ||
1113 | |||
1114 | physicsScene.DetailLog("{0},BSShapeGImpact,getReference,newKey={1},size={2},lod={3}", | ||
1115 | prim.LocalID, newMeshKey.ToString("X"), prim.Size, lod); | ||
1116 | |||
1117 | BSShapeGImpact retGImpact = null; | ||
1118 | lock (GImpacts) | ||
1119 | { | ||
1120 | if (GImpacts.TryGetValue(newMeshKey, out retGImpact)) | ||
1121 | { | ||
1122 | // The mesh has already been created. Return a new reference to same. | ||
1123 | retGImpact.IncrementReference(); | ||
1124 | } | ||
1125 | else | ||
1126 | { | ||
1127 | retGImpact = new BSShapeGImpact(new BulletShape()); | ||
1128 | BulletShape newShape = retGImpact.CreatePhysicalGImpact(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod); | ||
1129 | |||
1130 | // Check to see if mesh was created (might require an asset). | ||
1131 | newShape = VerifyMeshCreated(physicsScene, newShape, prim); | ||
1132 | newShape.shapeKey = newMeshKey; | ||
1133 | if (!newShape.isNativeShape | ||
1134 | || prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedMeshing | ||
1135 | || prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedAssetFetch) | ||
1136 | { | ||
1137 | // If a mesh was what was created, remember the built shape for later sharing. | ||
1138 | // Also note that if meshing failed we put it in the mesh list as there is nothing | ||
1139 | // else to do about the mesh. | ||
1140 | GImpacts.Add(newMeshKey, retGImpact); | ||
1141 | } | ||
1142 | |||
1143 | retGImpact.physShapeInfo = newShape; | ||
1144 | } | ||
1145 | } | ||
1146 | return retGImpact; | ||
1147 | } | ||
1148 | |||
1149 | private BulletShape CreatePhysicalGImpact(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey, | ||
1150 | PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) | ||
1151 | { | ||
1152 | return BSShapeMesh.CreatePhysicalMeshShape(physicsScene, prim, newMeshKey, pbs, size, lod, | ||
1153 | (w, iC, i, vC, v) => physicsScene.PE.CreateGImpactShape(w, iC, i, vC, v) ); | ||
1154 | } | ||
1155 | |||
1156 | public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) | ||
1157 | { | ||
1158 | BSShape ret = null; | ||
1159 | // If the underlying shape is native, the actual shape has not been build (waiting for asset) | ||
1160 | // and we must create a copy of the native shape since they are never shared. | ||
1161 | if (physShapeInfo.HasPhysicalShape && physShapeInfo.isNativeShape) | ||
1162 | { | ||
1163 | // TODO: decide when the native shapes should be freed. Check in Dereference? | ||
1164 | ret = BSShapeNative.GetReference(pPhysicsScene, pPrim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); | ||
1165 | } | ||
1166 | else | ||
1167 | { | ||
1168 | // Another reference to this shape is just counted. | ||
1169 | IncrementReference(); | ||
1170 | ret = this; | ||
1171 | } | ||
1172 | return ret; | ||
1173 | } | ||
1174 | // Dereferencing a compound shape releases the hold on all the child shapes. | ||
1175 | public override void Dereference(BSScene physicsScene) | ||
1176 | { | ||
1177 | lock (GImpacts) | ||
1178 | { | ||
1179 | this.DecrementReference(); | ||
1180 | physicsScene.DetailLog("{0},BSShapeGImpact.Dereference,shape={1}", BSScene.DetailLogZero, this); | ||
1181 | // TODO: schedule aging and destruction of unused meshes. | ||
1182 | } | ||
1183 | } | ||
1184 | // Loop through all the known hulls and return the description based on the physical address. | ||
1185 | public static bool TryGetGImpactByPtr(BulletShape pShape, out BSShapeGImpact outHull) | ||
1186 | { | ||
1187 | bool ret = false; | ||
1188 | BSShapeGImpact foundDesc = null; | ||
1189 | lock (GImpacts) | ||
1190 | { | ||
1191 | foreach (BSShapeGImpact sh in GImpacts.Values) | ||
1192 | { | ||
1193 | if (sh.physShapeInfo.ReferenceSame(pShape)) | ||
1194 | { | ||
1195 | foundDesc = sh; | ||
1196 | ret = true; | ||
1197 | break; | ||
1198 | } | ||
1199 | |||
1200 | } | ||
1201 | } | ||
1202 | outHull = foundDesc; | ||
1203 | return ret; | ||
215 | } | 1204 | } |
216 | public override void Dereference(BSScene physicsScene) { } | ||
217 | } | 1205 | } |
218 | 1206 | ||
1207 | // ============================================================================================================ | ||
219 | public class BSShapeAvatar : BSShape | 1208 | public class BSShapeAvatar : BSShape |
220 | { | 1209 | { |
221 | private static string LogHeader = "[BULLETSIM SHAPE AVATAR]"; | 1210 | private static string LogHeader = "[BULLETSIM SHAPE AVATAR]"; |
222 | public BSShapeAvatar() : base() | 1211 | public BSShapeAvatar() : base() |
223 | { | 1212 | { |
224 | } | 1213 | } |
225 | public static BSShape GetReference(BSPhysObject prim) | 1214 | public static BSShape GetReference(BSPhysObject prim) |
226 | { | 1215 | { |
1216 | return new BSShapeNull(); | ||
1217 | } | ||
1218 | public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) | ||
1219 | { | ||
227 | return new BSShapeNull(); | 1220 | return new BSShapeNull(); |
228 | } | 1221 | } |
229 | public override void Dereference(BSScene physicsScene) { } | 1222 | public override void Dereference(BSScene physicsScene) { } |