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