diff options
Diffstat (limited to 'OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs')
-rwxr-xr-x | OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs | 1465 |
1 files changed, 1465 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..79f1a89 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs | |||
@@ -0,0 +1,1465 @@ | |||
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 | false, | ||
560 | false | ||
561 | ); | ||
562 | } | ||
563 | |||
564 | if (meshData != null) | ||
565 | { | ||
566 | if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) | ||
567 | { | ||
568 | // Release the fetched asset data once it has been used. | ||
569 | pbs.SculptData = new byte[0]; | ||
570 | prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown; | ||
571 | } | ||
572 | |||
573 | int[] indices = meshData.getIndexListAsInt(); | ||
574 | int realIndicesIndex = indices.Length; | ||
575 | float[] verticesAsFloats = meshData.getVertexListAsFloat(); | ||
576 | |||
577 | if (BSParam.ShouldRemoveZeroWidthTriangles) | ||
578 | { | ||
579 | // Remove degenerate triangles. These are triangles with two of the vertices | ||
580 | // are the same. This is complicated by the problem that vertices are not | ||
581 | // made unique in sculpties so we have to compare the values in the vertex. | ||
582 | realIndicesIndex = 0; | ||
583 | for (int tri = 0; tri < indices.Length; tri += 3) | ||
584 | { | ||
585 | // Compute displacements into vertex array for each vertex of the triangle | ||
586 | int v1 = indices[tri + 0] * 3; | ||
587 | int v2 = indices[tri + 1] * 3; | ||
588 | int v3 = indices[tri + 2] * 3; | ||
589 | // Check to see if any two of the vertices are the same | ||
590 | if (!( ( verticesAsFloats[v1 + 0] == verticesAsFloats[v2 + 0] | ||
591 | && verticesAsFloats[v1 + 1] == verticesAsFloats[v2 + 1] | ||
592 | && verticesAsFloats[v1 + 2] == verticesAsFloats[v2 + 2]) | ||
593 | || ( verticesAsFloats[v2 + 0] == verticesAsFloats[v3 + 0] | ||
594 | && verticesAsFloats[v2 + 1] == verticesAsFloats[v3 + 1] | ||
595 | && verticesAsFloats[v2 + 2] == verticesAsFloats[v3 + 2]) | ||
596 | || ( verticesAsFloats[v1 + 0] == verticesAsFloats[v3 + 0] | ||
597 | && verticesAsFloats[v1 + 1] == verticesAsFloats[v3 + 1] | ||
598 | && verticesAsFloats[v1 + 2] == verticesAsFloats[v3 + 2]) ) | ||
599 | ) | ||
600 | { | ||
601 | // None of the vertices of the triangles are the same. This is a good triangle; | ||
602 | indices[realIndicesIndex + 0] = indices[tri + 0]; | ||
603 | indices[realIndicesIndex + 1] = indices[tri + 1]; | ||
604 | indices[realIndicesIndex + 2] = indices[tri + 2]; | ||
605 | realIndicesIndex += 3; | ||
606 | } | ||
607 | } | ||
608 | } | ||
609 | physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,key={1},origTri={2},realTri={3},numVerts={4}", | ||
610 | BSScene.DetailLogZero, newMeshKey.ToString("X"), indices.Length / 3, realIndicesIndex / 3, verticesAsFloats.Length / 3); | ||
611 | |||
612 | if (realIndicesIndex != 0) | ||
613 | { | ||
614 | newShape = makeShape(physicsScene.World, realIndicesIndex, indices, verticesAsFloats.Length / 3, verticesAsFloats); | ||
615 | } | ||
616 | else | ||
617 | { | ||
618 | // Force the asset condition to 'failed' so we won't try to keep fetching and processing this mesh. | ||
619 | prim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedMeshing; | ||
620 | physicsScene.Logger.DebugFormat("{0} All mesh triangles degenerate. Prim={1}", LogHeader, UsefulPrimInfo(physicsScene, prim) ); | ||
621 | physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,allDegenerate,key={1}", prim.LocalID, newMeshKey); | ||
622 | } | ||
623 | } | ||
624 | newShape.shapeKey = newMeshKey; | ||
625 | |||
626 | return newShape; | ||
627 | } | ||
628 | } | ||
629 | |||
630 | // ============================================================================================================ | ||
631 | // BSShapeHull is a physical shape representation htat is made up of many convex hulls. | ||
632 | // The convex hulls are either supplied with the asset or are approximated by one of the | ||
633 | // convex hull creation routines (in OpenSim or in Bullet). | ||
634 | public class BSShapeHull : BSShape | ||
635 | { | ||
636 | #pragma warning disable 414 | ||
637 | private static string LogHeader = "[BULLETSIM SHAPE HULL]"; | ||
638 | #pragma warning restore 414 | ||
639 | |||
640 | public static Dictionary<System.UInt64, BSShapeHull> Hulls = new Dictionary<System.UInt64, BSShapeHull>(); | ||
641 | |||
642 | |||
643 | public BSShapeHull(BulletShape pShape) : base(pShape) | ||
644 | { | ||
645 | } | ||
646 | public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) | ||
647 | { | ||
648 | float lod; | ||
649 | System.UInt64 newHullKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); | ||
650 | |||
651 | BSShapeHull retHull = null; | ||
652 | lock (Hulls) | ||
653 | { | ||
654 | if (Hulls.TryGetValue(newHullKey, out retHull)) | ||
655 | { | ||
656 | // The mesh has already been created. Return a new reference to same. | ||
657 | retHull.IncrementReference(); | ||
658 | } | ||
659 | else | ||
660 | { | ||
661 | retHull = new BSShapeHull(new BulletShape()); | ||
662 | // An instance of this mesh has not been created. Build and remember same. | ||
663 | BulletShape newShape = retHull.CreatePhysicalHull(physicsScene, prim, newHullKey, prim.BaseShape, prim.Size, lod); | ||
664 | |||
665 | // Check to see if hull was created (might require an asset). | ||
666 | newShape = VerifyMeshCreated(physicsScene, newShape, prim); | ||
667 | if (!newShape.isNativeShape || prim.AssetFailed()) | ||
668 | { | ||
669 | // If a mesh was what was created, remember the built shape for later sharing. | ||
670 | Hulls.Add(newHullKey, retHull); | ||
671 | } | ||
672 | retHull.physShapeInfo = newShape; | ||
673 | } | ||
674 | } | ||
675 | physicsScene.DetailLog("{0},BSShapeHull,getReference,hull={1},size={2},lod={3}", prim.LocalID, retHull, prim.Size, lod); | ||
676 | return retHull; | ||
677 | } | ||
678 | public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) | ||
679 | { | ||
680 | BSShape ret = null; | ||
681 | // If the underlying shape is native, the actual shape has not been build (waiting for asset) | ||
682 | // and we must create a copy of the native shape since they are never shared. | ||
683 | if (physShapeInfo.HasPhysicalShape && physShapeInfo.isNativeShape) | ||
684 | { | ||
685 | // TODO: decide when the native shapes should be freed. Check in Dereference? | ||
686 | ret = BSShapeNative.GetReference(pPhysicsScene, pPrim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); | ||
687 | } | ||
688 | else | ||
689 | { | ||
690 | // Another reference to this shape is just counted. | ||
691 | IncrementReference(); | ||
692 | ret = this; | ||
693 | } | ||
694 | return ret; | ||
695 | } | ||
696 | public override void Dereference(BSScene physicsScene) | ||
697 | { | ||
698 | lock (Hulls) | ||
699 | { | ||
700 | this.DecrementReference(); | ||
701 | physicsScene.DetailLog("{0},BSShapeHull.Dereference,shape={1}", BSScene.DetailLogZero, this); | ||
702 | // TODO: schedule aging and destruction of unused meshes. | ||
703 | } | ||
704 | } | ||
705 | |||
706 | List<ConvexResult> m_hulls; | ||
707 | private BulletShape CreatePhysicalHull(BSScene physicsScene, BSPhysObject prim, System.UInt64 newHullKey, | ||
708 | PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) | ||
709 | { | ||
710 | BulletShape newShape = new BulletShape(); | ||
711 | |||
712 | IMesh meshData = null; | ||
713 | List<List<OMV.Vector3>> allHulls = null; | ||
714 | lock (physicsScene.mesher) | ||
715 | { | ||
716 | // Pass true for physicalness as this prevents the creation of bounding box which is not needed | ||
717 | meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, true /* isPhysical */, false /* shouldCache */, false, false); | ||
718 | |||
719 | // If we should use the asset's hull info, fetch it out of the locked mesher | ||
720 | if (meshData != null && BSParam.ShouldUseAssetHulls) | ||
721 | { | ||
722 | Meshmerizer realMesher = physicsScene.mesher as Meshmerizer; | ||
723 | if (realMesher != null) | ||
724 | { | ||
725 | allHulls = realMesher.GetConvexHulls(size); | ||
726 | } | ||
727 | if (allHulls == null) | ||
728 | { | ||
729 | physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,assetHulls,noAssetHull", prim.LocalID); | ||
730 | } | ||
731 | } | ||
732 | } | ||
733 | |||
734 | // If there is hull data in the mesh asset, build the hull from that | ||
735 | if (allHulls != null && BSParam.ShouldUseAssetHulls) | ||
736 | { | ||
737 | int hullCount = allHulls.Count; | ||
738 | shapeInfo.HullCount = hullCount; | ||
739 | int totalVertices = 1; // include one for the count of the hulls | ||
740 | // Using the structure described for HACD hulls, create the memory sturcture | ||
741 | // to pass the hull data to the creater. | ||
742 | foreach (List<OMV.Vector3> hullVerts in allHulls) | ||
743 | { | ||
744 | totalVertices += 4; // add four for the vertex count and centroid | ||
745 | totalVertices += hullVerts.Count * 3; // one vertex is three dimensions | ||
746 | } | ||
747 | float[] convHulls = new float[totalVertices]; | ||
748 | |||
749 | convHulls[0] = (float)hullCount; | ||
750 | int jj = 1; | ||
751 | int hullIndex = 0; | ||
752 | foreach (List<OMV.Vector3> hullVerts in allHulls) | ||
753 | { | ||
754 | convHulls[jj + 0] = hullVerts.Count; | ||
755 | convHulls[jj + 1] = 0f; // centroid x,y,z | ||
756 | convHulls[jj + 2] = 0f; | ||
757 | convHulls[jj + 3] = 0f; | ||
758 | jj += 4; | ||
759 | foreach (OMV.Vector3 oneVert in hullVerts) | ||
760 | { | ||
761 | convHulls[jj + 0] = oneVert.X; | ||
762 | convHulls[jj + 1] = oneVert.Y; | ||
763 | convHulls[jj + 2] = oneVert.Z; | ||
764 | jj += 3; | ||
765 | } | ||
766 | shapeInfo.SetVerticesPerHull(hullIndex, hullVerts.Count); | ||
767 | hullIndex++; | ||
768 | } | ||
769 | |||
770 | // create the hull data structure in Bullet | ||
771 | newShape = physicsScene.PE.CreateHullShape(physicsScene.World, hullCount, convHulls); | ||
772 | |||
773 | physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,assetHulls,hulls={1},totVert={2},shape={3}", | ||
774 | prim.LocalID, hullCount, totalVertices, newShape); | ||
775 | } | ||
776 | |||
777 | // If no hull specified in the asset and we should use Bullet's HACD approximation... | ||
778 | if (!newShape.HasPhysicalShape && BSParam.ShouldUseBulletHACD) | ||
779 | { | ||
780 | // Build the hull shape from an existing mesh shape. | ||
781 | // The mesh should have already been created in Bullet. | ||
782 | physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,bulletHACD,entry", prim.LocalID); | ||
783 | BSShape meshShape = BSShapeMesh.GetReference(physicsScene, true, prim); | ||
784 | |||
785 | if (meshShape.physShapeInfo.HasPhysicalShape) | ||
786 | { | ||
787 | HACDParams parms = new HACDParams(); | ||
788 | parms.maxVerticesPerHull = BSParam.BHullMaxVerticesPerHull; | ||
789 | parms.minClusters = BSParam.BHullMinClusters; | ||
790 | parms.compacityWeight = BSParam.BHullCompacityWeight; | ||
791 | parms.volumeWeight = BSParam.BHullVolumeWeight; | ||
792 | parms.concavity = BSParam.BHullConcavity; | ||
793 | parms.addExtraDistPoints = BSParam.NumericBool(BSParam.BHullAddExtraDistPoints); | ||
794 | parms.addNeighboursDistPoints = BSParam.NumericBool(BSParam.BHullAddNeighboursDistPoints); | ||
795 | parms.addFacesPoints = BSParam.NumericBool(BSParam.BHullAddFacesPoints); | ||
796 | parms.shouldAdjustCollisionMargin = BSParam.NumericBool(BSParam.BHullShouldAdjustCollisionMargin); | ||
797 | parms.whichHACD = 0; // Use the HACD routine that comes with Bullet | ||
798 | |||
799 | physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,beforeCall", prim.LocalID, newShape.HasPhysicalShape); | ||
800 | newShape = physicsScene.PE.BuildHullShapeFromMesh(physicsScene.World, meshShape.physShapeInfo, parms); | ||
801 | physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,shape={1}", prim.LocalID, newShape); | ||
802 | |||
803 | // Now done with the mesh shape. | ||
804 | shapeInfo.HullCount = 1; | ||
805 | BSShapeMesh maybeMesh = meshShape as BSShapeMesh; | ||
806 | if (maybeMesh != null) | ||
807 | shapeInfo.SetVerticesPerHull(0, maybeMesh.shapeInfo.Vertices); | ||
808 | meshShape.Dereference(physicsScene); | ||
809 | } | ||
810 | physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,bulletHACD,exit,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); | ||
811 | } | ||
812 | |||
813 | // If no other hull specifications, use our HACD hull approximation. | ||
814 | if (!newShape.HasPhysicalShape && meshData != null) | ||
815 | { | ||
816 | if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) | ||
817 | { | ||
818 | // Release the fetched asset data once it has been used. | ||
819 | pbs.SculptData = new byte[0]; | ||
820 | prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown; | ||
821 | } | ||
822 | |||
823 | int[] indices = meshData.getIndexListAsInt(); | ||
824 | List<OMV.Vector3> vertices = meshData.getVertexList(); | ||
825 | |||
826 | //format conversion from IMesh format to DecompDesc format | ||
827 | List<int> convIndices = new List<int>(); | ||
828 | List<float3> convVertices = new List<float3>(); | ||
829 | for (int ii = 0; ii < indices.GetLength(0); ii++) | ||
830 | { | ||
831 | convIndices.Add(indices[ii]); | ||
832 | } | ||
833 | foreach (OMV.Vector3 vv in vertices) | ||
834 | { | ||
835 | convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); | ||
836 | } | ||
837 | |||
838 | uint maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplit; | ||
839 | if (BSParam.CSHullMaxDepthSplit != BSParam.CSHullMaxDepthSplitForSimpleShapes) | ||
840 | { | ||
841 | // Simple primitive shapes we know are convex so they are better implemented with | ||
842 | // fewer hulls. | ||
843 | // Check for simple shape (prim without cuts) and reduce split parameter if so. | ||
844 | if (BSShapeCollection.PrimHasNoCuts(pbs)) | ||
845 | { | ||
846 | maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplitForSimpleShapes; | ||
847 | } | ||
848 | } | ||
849 | |||
850 | // setup and do convex hull conversion | ||
851 | m_hulls = new List<ConvexResult>(); | ||
852 | DecompDesc dcomp = new DecompDesc(); | ||
853 | dcomp.mIndices = convIndices; | ||
854 | dcomp.mVertices = convVertices; | ||
855 | dcomp.mDepth = maxDepthSplit; | ||
856 | dcomp.mCpercent = BSParam.CSHullConcavityThresholdPercent; | ||
857 | dcomp.mPpercent = BSParam.CSHullVolumeConservationThresholdPercent; | ||
858 | dcomp.mMaxVertices = (uint)BSParam.CSHullMaxVertices; | ||
859 | dcomp.mSkinWidth = BSParam.CSHullMaxSkinWidth; | ||
860 | ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); | ||
861 | // create the hull into the _hulls variable | ||
862 | convexBuilder.process(dcomp); | ||
863 | |||
864 | physicsScene.DetailLog("{0},BSShapeCollection.CreatePhysicalHull,key={1},inVert={2},inInd={3},split={4},hulls={5}", | ||
865 | BSScene.DetailLogZero, newHullKey, indices.GetLength(0), vertices.Count, maxDepthSplit, m_hulls.Count); | ||
866 | |||
867 | // Convert the vertices and indices for passing to unmanaged. | ||
868 | // The hull information is passed as a large floating point array. | ||
869 | // The format is: | ||
870 | // convHulls[0] = number of hulls | ||
871 | // convHulls[1] = number of vertices in first hull | ||
872 | // convHulls[2] = hull centroid X coordinate | ||
873 | // convHulls[3] = hull centroid Y coordinate | ||
874 | // convHulls[4] = hull centroid Z coordinate | ||
875 | // convHulls[5] = first hull vertex X | ||
876 | // convHulls[6] = first hull vertex Y | ||
877 | // convHulls[7] = first hull vertex Z | ||
878 | // convHulls[8] = second hull vertex X | ||
879 | // ... | ||
880 | // convHulls[n] = number of vertices in second hull | ||
881 | // convHulls[n+1] = second hull centroid X coordinate | ||
882 | // ... | ||
883 | // | ||
884 | // TODO: is is very inefficient. Someday change the convex hull generator to return | ||
885 | // data structures that do not need to be converted in order to pass to Bullet. | ||
886 | // And maybe put the values directly into pinned memory rather than marshaling. | ||
887 | int hullCount = m_hulls.Count; | ||
888 | int totalVertices = 1; // include one for the count of the hulls | ||
889 | foreach (ConvexResult cr in m_hulls) | ||
890 | { | ||
891 | totalVertices += 4; // add four for the vertex count and centroid | ||
892 | totalVertices += cr.HullIndices.Count * 3; // we pass just triangles | ||
893 | } | ||
894 | float[] convHulls = new float[totalVertices]; | ||
895 | |||
896 | convHulls[0] = (float)hullCount; | ||
897 | int jj = 1; | ||
898 | foreach (ConvexResult cr in m_hulls) | ||
899 | { | ||
900 | // copy vertices for index access | ||
901 | float3[] verts = new float3[cr.HullVertices.Count]; | ||
902 | int kk = 0; | ||
903 | foreach (float3 ff in cr.HullVertices) | ||
904 | { | ||
905 | verts[kk++] = ff; | ||
906 | } | ||
907 | |||
908 | // add to the array one hull's worth of data | ||
909 | convHulls[jj++] = cr.HullIndices.Count; | ||
910 | convHulls[jj++] = 0f; // centroid x,y,z | ||
911 | convHulls[jj++] = 0f; | ||
912 | convHulls[jj++] = 0f; | ||
913 | foreach (int ind in cr.HullIndices) | ||
914 | { | ||
915 | convHulls[jj++] = verts[ind].x; | ||
916 | convHulls[jj++] = verts[ind].y; | ||
917 | convHulls[jj++] = verts[ind].z; | ||
918 | } | ||
919 | } | ||
920 | // create the hull data structure in Bullet | ||
921 | newShape = physicsScene.PE.CreateHullShape(physicsScene.World, hullCount, convHulls); | ||
922 | } | ||
923 | newShape.shapeKey = newHullKey; | ||
924 | return newShape; | ||
925 | } | ||
926 | // Callback from convex hull creater with a newly created hull. | ||
927 | // Just add it to our collection of hulls for this shape. | ||
928 | private void HullReturn(ConvexResult result) | ||
929 | { | ||
930 | m_hulls.Add(result); | ||
931 | return; | ||
932 | } | ||
933 | // Loop through all the known hulls and return the description based on the physical address. | ||
934 | public static bool TryGetHullByPtr(BulletShape pShape, out BSShapeHull outHull) | ||
935 | { | ||
936 | bool ret = false; | ||
937 | BSShapeHull foundDesc = null; | ||
938 | lock (Hulls) | ||
939 | { | ||
940 | foreach (BSShapeHull sh in Hulls.Values) | ||
941 | { | ||
942 | if (sh.physShapeInfo.ReferenceSame(pShape)) | ||
943 | { | ||
944 | foundDesc = sh; | ||
945 | ret = true; | ||
946 | break; | ||
947 | } | ||
948 | |||
949 | } | ||
950 | } | ||
951 | outHull = foundDesc; | ||
952 | return ret; | ||
953 | } | ||
954 | } | ||
955 | |||
956 | // ============================================================================================================ | ||
957 | // BSShapeCompound is a wrapper for the Bullet compound shape which is built from multiple, separate | ||
958 | // meshes. Used by BulletSim for complex shapes like linksets. | ||
959 | public class BSShapeCompound : BSShape | ||
960 | { | ||
961 | private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]"; | ||
962 | public static Dictionary<string, BSShapeCompound> CompoundShapes = new Dictionary<string, BSShapeCompound>(); | ||
963 | |||
964 | public BSShapeCompound(BulletShape pShape) : base(pShape) | ||
965 | { | ||
966 | } | ||
967 | public static BSShape GetReference(BSScene physicsScene) | ||
968 | { | ||
969 | // Base compound shapes are not shared so this returns a raw shape. | ||
970 | // A built compound shape can be reused in linksets. | ||
971 | BSShapeCompound ret = new BSShapeCompound(CreatePhysicalCompoundShape(physicsScene)); | ||
972 | CompoundShapes.Add(ret.AddrString, ret); | ||
973 | return ret; | ||
974 | } | ||
975 | public override BSShape GetReference(BSScene physicsScene, BSPhysObject prim) | ||
976 | { | ||
977 | // Calling this reference means we want another handle to an existing compound shape | ||
978 | // (usually linksets) so return this copy. | ||
979 | IncrementReference(); | ||
980 | return this; | ||
981 | } | ||
982 | // Dereferencing a compound shape releases the hold on all the child shapes. | ||
983 | public override void Dereference(BSScene physicsScene) | ||
984 | { | ||
985 | lock (physShapeInfo) | ||
986 | { | ||
987 | this.DecrementReference(); | ||
988 | physicsScene.DetailLog("{0},BSShapeCompound.Dereference,shape={1}", BSScene.DetailLogZero, this); | ||
989 | if (referenceCount <= 0) | ||
990 | { | ||
991 | if (!physicsScene.PE.IsCompound(physShapeInfo)) | ||
992 | { | ||
993 | // Failed the sanity check!! | ||
994 | physicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", | ||
995 | LogHeader, physShapeInfo.shapeType, physShapeInfo.AddrString); | ||
996 | physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", | ||
997 | BSScene.DetailLogZero, physShapeInfo.shapeType, physShapeInfo.AddrString); | ||
998 | return; | ||
999 | } | ||
1000 | |||
1001 | int numChildren = physicsScene.PE.GetNumberOfCompoundChildren(physShapeInfo); | ||
1002 | physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", | ||
1003 | BSScene.DetailLogZero, physShapeInfo, numChildren); | ||
1004 | |||
1005 | // Loop through all the children dereferencing each. | ||
1006 | for (int ii = numChildren - 1; ii >= 0; ii--) | ||
1007 | { | ||
1008 | BulletShape childShape = physicsScene.PE.RemoveChildShapeFromCompoundShapeIndex(physShapeInfo, ii); | ||
1009 | DereferenceAnonCollisionShape(physicsScene, childShape); | ||
1010 | } | ||
1011 | |||
1012 | lock (CompoundShapes) | ||
1013 | CompoundShapes.Remove(physShapeInfo.AddrString); | ||
1014 | physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo); | ||
1015 | } | ||
1016 | } | ||
1017 | } | ||
1018 | public static bool TryGetCompoundByPtr(BulletShape pShape, out BSShapeCompound outCompound) | ||
1019 | { | ||
1020 | lock (CompoundShapes) | ||
1021 | { | ||
1022 | string addr = pShape.AddrString; | ||
1023 | return CompoundShapes.TryGetValue(addr, out outCompound); | ||
1024 | } | ||
1025 | } | ||
1026 | private static BulletShape CreatePhysicalCompoundShape(BSScene physicsScene) | ||
1027 | { | ||
1028 | BulletShape cShape = physicsScene.PE.CreateCompoundShape(physicsScene.World, false); | ||
1029 | return cShape; | ||
1030 | } | ||
1031 | // Sometimes we have a pointer to a collision shape but don't know what type it is. | ||
1032 | // Figure out type and call the correct dereference routine. | ||
1033 | // Called at taint-time. | ||
1034 | private void DereferenceAnonCollisionShape(BSScene physicsScene, BulletShape pShape) | ||
1035 | { | ||
1036 | // TODO: figure a better way to go through all the shape types and find a possible instance. | ||
1037 | physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,shape={1}", | ||
1038 | BSScene.DetailLogZero, pShape); | ||
1039 | BSShapeMesh meshDesc; | ||
1040 | if (BSShapeMesh.TryGetMeshByPtr(pShape, out meshDesc)) | ||
1041 | { | ||
1042 | meshDesc.Dereference(physicsScene); | ||
1043 | // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundMesh,shape={1}", BSScene.DetailLogZero, pShape); | ||
1044 | } | ||
1045 | else | ||
1046 | { | ||
1047 | BSShapeHull hullDesc; | ||
1048 | if (BSShapeHull.TryGetHullByPtr(pShape, out hullDesc)) | ||
1049 | { | ||
1050 | hullDesc.Dereference(physicsScene); | ||
1051 | // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundHull,shape={1}", BSScene.DetailLogZero, pShape); | ||
1052 | } | ||
1053 | else | ||
1054 | { | ||
1055 | BSShapeConvexHull chullDesc; | ||
1056 | if (BSShapeConvexHull.TryGetConvexHullByPtr(pShape, out chullDesc)) | ||
1057 | { | ||
1058 | chullDesc.Dereference(physicsScene); | ||
1059 | // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundConvexHull,shape={1}", BSScene.DetailLogZero, pShape); | ||
1060 | } | ||
1061 | else | ||
1062 | { | ||
1063 | BSShapeGImpact gImpactDesc; | ||
1064 | if (BSShapeGImpact.TryGetGImpactByPtr(pShape, out gImpactDesc)) | ||
1065 | { | ||
1066 | gImpactDesc.Dereference(physicsScene); | ||
1067 | // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundgImpact,shape={1}", BSScene.DetailLogZero, pShape); | ||
1068 | } | ||
1069 | else | ||
1070 | { | ||
1071 | // Didn't find it in the lists of specific types. It could be compound. | ||
1072 | BSShapeCompound compoundDesc; | ||
1073 | if (BSShapeCompound.TryGetCompoundByPtr(pShape, out compoundDesc)) | ||
1074 | { | ||
1075 | compoundDesc.Dereference(physicsScene); | ||
1076 | // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,recursiveCompoundShape,shape={1}", BSScene.DetailLogZero, pShape); | ||
1077 | } | ||
1078 | else | ||
1079 | { | ||
1080 | // If none of the above, maybe it is a simple native shape. | ||
1081 | if (physicsScene.PE.IsNativeShape(pShape)) | ||
1082 | { | ||
1083 | // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,assumingNative,shape={1}", BSScene.DetailLogZero, pShape); | ||
1084 | BSShapeNative nativeShape = new BSShapeNative(pShape); | ||
1085 | nativeShape.Dereference(physicsScene); | ||
1086 | } | ||
1087 | else | ||
1088 | { | ||
1089 | physicsScene.Logger.WarnFormat("{0} DereferenceAnonCollisionShape. Did not find shape. {1}", | ||
1090 | LogHeader, pShape); | ||
1091 | } | ||
1092 | } | ||
1093 | } | ||
1094 | } | ||
1095 | } | ||
1096 | } | ||
1097 | } | ||
1098 | } | ||
1099 | |||
1100 | // ============================================================================================================ | ||
1101 | // BSShapeConvexHull is a wrapper for a Bullet single convex hull. A BSShapeHull contains multiple convex | ||
1102 | // hull shapes. This is used for simple prims that are convex and thus can be made into a simple | ||
1103 | // collision shape (a single hull). More complex physical shapes will be BSShapeHull's. | ||
1104 | public class BSShapeConvexHull : BSShape | ||
1105 | { | ||
1106 | #pragma warning disable 414 | ||
1107 | private static string LogHeader = "[BULLETSIM SHAPE CONVEX HULL]"; | ||
1108 | #pragma warning restore 414 | ||
1109 | |||
1110 | public static Dictionary<System.UInt64, BSShapeConvexHull> ConvexHulls = new Dictionary<System.UInt64, BSShapeConvexHull>(); | ||
1111 | |||
1112 | public BSShapeConvexHull(BulletShape pShape) : base(pShape) | ||
1113 | { | ||
1114 | } | ||
1115 | public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) | ||
1116 | { | ||
1117 | float lod; | ||
1118 | System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); | ||
1119 | |||
1120 | physicsScene.DetailLog("{0},BSShapeConvexHull,getReference,newKey={1},size={2},lod={3}", | ||
1121 | prim.LocalID, newMeshKey.ToString("X"), prim.Size, lod); | ||
1122 | |||
1123 | BSShapeConvexHull retConvexHull = null; | ||
1124 | lock (ConvexHulls) | ||
1125 | { | ||
1126 | if (ConvexHulls.TryGetValue(newMeshKey, out retConvexHull)) | ||
1127 | { | ||
1128 | // The mesh has already been created. Return a new reference to same. | ||
1129 | retConvexHull.IncrementReference(); | ||
1130 | } | ||
1131 | else | ||
1132 | { | ||
1133 | retConvexHull = new BSShapeConvexHull(new BulletShape()); | ||
1134 | BulletShape convexShape = null; | ||
1135 | |||
1136 | // Get a handle to a mesh to build the hull from | ||
1137 | BSShape baseMesh = BSShapeMesh.GetReference(physicsScene, false /* forceRebuild */, prim); | ||
1138 | if (baseMesh.physShapeInfo.isNativeShape) | ||
1139 | { | ||
1140 | // We get here if the mesh was not creatable. Could be waiting for an asset from the disk. | ||
1141 | // In the short term, we return the native shape and a later ForceBodyShapeRebuild should | ||
1142 | // get back to this code with a buildable mesh. | ||
1143 | // TODO: not sure the temp native shape is freed when the mesh is rebuilt. When does this get freed? | ||
1144 | convexShape = baseMesh.physShapeInfo; | ||
1145 | } | ||
1146 | else | ||
1147 | { | ||
1148 | convexShape = physicsScene.PE.BuildConvexHullShapeFromMesh(physicsScene.World, baseMesh.physShapeInfo); | ||
1149 | convexShape.shapeKey = newMeshKey; | ||
1150 | ConvexHulls.Add(convexShape.shapeKey, retConvexHull); | ||
1151 | physicsScene.DetailLog("{0},BSShapeConvexHull.GetReference,addingNewlyCreatedShape,shape={1}", | ||
1152 | BSScene.DetailLogZero, convexShape); | ||
1153 | } | ||
1154 | |||
1155 | // Done with the base mesh | ||
1156 | baseMesh.Dereference(physicsScene); | ||
1157 | |||
1158 | retConvexHull.physShapeInfo = convexShape; | ||
1159 | } | ||
1160 | } | ||
1161 | return retConvexHull; | ||
1162 | } | ||
1163 | public override BSShape GetReference(BSScene physicsScene, BSPhysObject prim) | ||
1164 | { | ||
1165 | // Calling this reference means we want another handle to an existing shape | ||
1166 | // (usually linksets) so return this copy. | ||
1167 | IncrementReference(); | ||
1168 | return this; | ||
1169 | } | ||
1170 | // Dereferencing a compound shape releases the hold on all the child shapes. | ||
1171 | public override void Dereference(BSScene physicsScene) | ||
1172 | { | ||
1173 | lock (ConvexHulls) | ||
1174 | { | ||
1175 | this.DecrementReference(); | ||
1176 | physicsScene.DetailLog("{0},BSShapeConvexHull.Dereference,shape={1}", BSScene.DetailLogZero, this); | ||
1177 | // TODO: schedule aging and destruction of unused meshes. | ||
1178 | } | ||
1179 | } | ||
1180 | // Loop through all the known hulls and return the description based on the physical address. | ||
1181 | public static bool TryGetConvexHullByPtr(BulletShape pShape, out BSShapeConvexHull outHull) | ||
1182 | { | ||
1183 | bool ret = false; | ||
1184 | BSShapeConvexHull foundDesc = null; | ||
1185 | lock (ConvexHulls) | ||
1186 | { | ||
1187 | foreach (BSShapeConvexHull sh in ConvexHulls.Values) | ||
1188 | { | ||
1189 | if (sh.physShapeInfo.ReferenceSame(pShape)) | ||
1190 | { | ||
1191 | foundDesc = sh; | ||
1192 | ret = true; | ||
1193 | break; | ||
1194 | } | ||
1195 | |||
1196 | } | ||
1197 | } | ||
1198 | outHull = foundDesc; | ||
1199 | return ret; | ||
1200 | } | ||
1201 | } | ||
1202 | // ============================================================================================================ | ||
1203 | // BSShapeGImpact is a wrapper for the Bullet GImpact shape which is a collision mesh shape that | ||
1204 | // can handle concave as well as convex shapes. Much slower computationally but creates smoother | ||
1205 | // shapes than multiple convex hull approximations. | ||
1206 | public class BSShapeGImpact : BSShape | ||
1207 | { | ||
1208 | #pragma warning disable 414 | ||
1209 | private static string LogHeader = "[BULLETSIM SHAPE GIMPACT]"; | ||
1210 | #pragma warning restore 414 | ||
1211 | |||
1212 | public static Dictionary<System.UInt64, BSShapeGImpact> GImpacts = new Dictionary<System.UInt64, BSShapeGImpact>(); | ||
1213 | |||
1214 | public BSShapeGImpact(BulletShape pShape) : base(pShape) | ||
1215 | { | ||
1216 | } | ||
1217 | public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) | ||
1218 | { | ||
1219 | float lod; | ||
1220 | System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); | ||
1221 | |||
1222 | physicsScene.DetailLog("{0},BSShapeGImpact,getReference,newKey={1},size={2},lod={3}", | ||
1223 | prim.LocalID, newMeshKey.ToString("X"), prim.Size, lod); | ||
1224 | |||
1225 | BSShapeGImpact retGImpact = null; | ||
1226 | lock (GImpacts) | ||
1227 | { | ||
1228 | if (GImpacts.TryGetValue(newMeshKey, out retGImpact)) | ||
1229 | { | ||
1230 | // The mesh has already been created. Return a new reference to same. | ||
1231 | retGImpact.IncrementReference(); | ||
1232 | } | ||
1233 | else | ||
1234 | { | ||
1235 | retGImpact = new BSShapeGImpact(new BulletShape()); | ||
1236 | BulletShape newShape = retGImpact.CreatePhysicalGImpact(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod); | ||
1237 | |||
1238 | // Check to see if mesh was created (might require an asset). | ||
1239 | newShape = VerifyMeshCreated(physicsScene, newShape, prim); | ||
1240 | newShape.shapeKey = newMeshKey; | ||
1241 | if (!newShape.isNativeShape || prim.AssetFailed()) | ||
1242 | { | ||
1243 | // If a mesh was what was created, remember the built shape for later sharing. | ||
1244 | // Also note that if meshing failed we put it in the mesh list as there is nothing | ||
1245 | // else to do about the mesh. | ||
1246 | GImpacts.Add(newMeshKey, retGImpact); | ||
1247 | } | ||
1248 | |||
1249 | retGImpact.physShapeInfo = newShape; | ||
1250 | } | ||
1251 | } | ||
1252 | return retGImpact; | ||
1253 | } | ||
1254 | |||
1255 | private BulletShape CreatePhysicalGImpact(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey, | ||
1256 | PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) | ||
1257 | { | ||
1258 | return BSShapeMesh.CreatePhysicalMeshShape(physicsScene, prim, newMeshKey, pbs, size, lod, | ||
1259 | (w, iC, i, vC, v) => | ||
1260 | { | ||
1261 | shapeInfo.Vertices = vC; | ||
1262 | return physicsScene.PE.CreateGImpactShape(w, iC, i, vC, v); | ||
1263 | }); | ||
1264 | } | ||
1265 | |||
1266 | public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) | ||
1267 | { | ||
1268 | BSShape ret = null; | ||
1269 | // If the underlying shape is native, the actual shape has not been build (waiting for asset) | ||
1270 | // and we must create a copy of the native shape since they are never shared. | ||
1271 | if (physShapeInfo.HasPhysicalShape && physShapeInfo.isNativeShape) | ||
1272 | { | ||
1273 | // TODO: decide when the native shapes should be freed. Check in Dereference? | ||
1274 | ret = BSShapeNative.GetReference(pPhysicsScene, pPrim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); | ||
1275 | } | ||
1276 | else | ||
1277 | { | ||
1278 | // Another reference to this shape is just counted. | ||
1279 | IncrementReference(); | ||
1280 | ret = this; | ||
1281 | } | ||
1282 | return ret; | ||
1283 | } | ||
1284 | // Dereferencing a compound shape releases the hold on all the child shapes. | ||
1285 | public override void Dereference(BSScene physicsScene) | ||
1286 | { | ||
1287 | lock (GImpacts) | ||
1288 | { | ||
1289 | this.DecrementReference(); | ||
1290 | physicsScene.DetailLog("{0},BSShapeGImpact.Dereference,shape={1}", BSScene.DetailLogZero, this); | ||
1291 | // TODO: schedule aging and destruction of unused meshes. | ||
1292 | } | ||
1293 | } | ||
1294 | // Loop through all the known hulls and return the description based on the physical address. | ||
1295 | public static bool TryGetGImpactByPtr(BulletShape pShape, out BSShapeGImpact outHull) | ||
1296 | { | ||
1297 | bool ret = false; | ||
1298 | BSShapeGImpact foundDesc = null; | ||
1299 | lock (GImpacts) | ||
1300 | { | ||
1301 | foreach (BSShapeGImpact sh in GImpacts.Values) | ||
1302 | { | ||
1303 | if (sh.physShapeInfo.ReferenceSame(pShape)) | ||
1304 | { | ||
1305 | foundDesc = sh; | ||
1306 | ret = true; | ||
1307 | break; | ||
1308 | } | ||
1309 | |||
1310 | } | ||
1311 | } | ||
1312 | outHull = foundDesc; | ||
1313 | return ret; | ||
1314 | } | ||
1315 | } | ||
1316 | |||
1317 | // ============================================================================================================ | ||
1318 | // BSShapeAvatar is a specialized mesh shape for avatars. | ||
1319 | public class BSShapeAvatar : BSShape | ||
1320 | { | ||
1321 | #pragma warning disable 414 | ||
1322 | private static string LogHeader = "[BULLETSIM SHAPE AVATAR]"; | ||
1323 | #pragma warning restore 414 | ||
1324 | |||
1325 | public BSShapeAvatar() | ||
1326 | : base() | ||
1327 | { | ||
1328 | } | ||
1329 | public static BSShape GetReference(BSPhysObject prim) | ||
1330 | { | ||
1331 | return new BSShapeNull(); | ||
1332 | } | ||
1333 | public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) | ||
1334 | { | ||
1335 | return new BSShapeNull(); | ||
1336 | } | ||
1337 | public override void Dereference(BSScene physicsScene) { } | ||
1338 | |||
1339 | // From the front: | ||
1340 | // A---A | ||
1341 | // / \ | ||
1342 | // B-------B | ||
1343 | // / \ +Z | ||
1344 | // C-----------C | | ||
1345 | // \ / -Y --+-- +Y | ||
1346 | // \ / | | ||
1347 | // \ / -Z | ||
1348 | // D-----D | ||
1349 | // \ / | ||
1350 | // E-E | ||
1351 | |||
1352 | // From the top A and E are just lines. | ||
1353 | // B, C and D are hexagons: | ||
1354 | // | ||
1355 | // C1--C2 +X | ||
1356 | // / \ | | ||
1357 | // C0 C3 -Y --+-- +Y | ||
1358 | // \ / | | ||
1359 | // C5--C4 -X | ||
1360 | |||
1361 | // Zero goes directly through the middle so the offsets are from that middle axis | ||
1362 | // and up and down from a middle horizon (A and E are the same distance from the zero). | ||
1363 | // The height, width and depth is one. All scaling is done by the simulator. | ||
1364 | |||
1365 | // Z component -- how far the level is from the middle zero | ||
1366 | private const float Aup = 0.5f; | ||
1367 | private const float Bup = 0.4f; | ||
1368 | private const float Cup = 0.3f; | ||
1369 | private const float Dup = -0.4f; | ||
1370 | private const float Eup = -0.5f; | ||
1371 | |||
1372 | // Y component -- distance from center to x0 and x3 | ||
1373 | private const float Awid = 0.25f; | ||
1374 | private const float Bwid = 0.3f; | ||
1375 | private const float Cwid = 0.5f; | ||
1376 | private const float Dwid = 0.3f; | ||
1377 | private const float Ewid = 0.2f; | ||
1378 | |||
1379 | // Y component -- distance from center to x1, x2, x4 and x5 | ||
1380 | private const float Afwid = 0.0f; | ||
1381 | private const float Bfwid = 0.2f; | ||
1382 | private const float Cfwid = 0.4f; | ||
1383 | private const float Dfwid = 0.2f; | ||
1384 | private const float Efwid = 0.0f; | ||
1385 | |||
1386 | // X component -- distance from zero to the front or back of a level | ||
1387 | private const float Adep = 0f; | ||
1388 | private const float Bdep = 0.3f; | ||
1389 | private const float Cdep = 0.5f; | ||
1390 | private const float Ddep = 0.2f; | ||
1391 | private const float Edep = 0f; | ||
1392 | |||
1393 | private OMV.Vector3[] avatarVertices = { | ||
1394 | new OMV.Vector3( 0.0f, -Awid, Aup), // A0 | ||
1395 | new OMV.Vector3( 0.0f, +Awid, Aup), // A3 | ||
1396 | |||
1397 | new OMV.Vector3( 0.0f, -Bwid, Bup), // B0 | ||
1398 | new OMV.Vector3(+Bdep, -Bfwid, Bup), // B1 | ||
1399 | new OMV.Vector3(+Bdep, +Bfwid, Bup), // B2 | ||
1400 | new OMV.Vector3( 0.0f, +Bwid, Bup), // B3 | ||
1401 | new OMV.Vector3(-Bdep, +Bfwid, Bup), // B4 | ||
1402 | new OMV.Vector3(-Bdep, -Bfwid, Bup), // B5 | ||
1403 | |||
1404 | new OMV.Vector3( 0.0f, -Cwid, Cup), // C0 | ||
1405 | new OMV.Vector3(+Cdep, -Cfwid, Cup), // C1 | ||
1406 | new OMV.Vector3(+Cdep, +Cfwid, Cup), // C2 | ||
1407 | new OMV.Vector3( 0.0f, +Cwid, Cup), // C3 | ||
1408 | new OMV.Vector3(-Cdep, +Cfwid, Cup), // C4 | ||
1409 | new OMV.Vector3(-Cdep, -Cfwid, Cup), // C5 | ||
1410 | |||
1411 | new OMV.Vector3( 0.0f, -Dwid, Dup), // D0 | ||
1412 | new OMV.Vector3(+Ddep, -Dfwid, Dup), // D1 | ||
1413 | new OMV.Vector3(+Ddep, +Dfwid, Dup), // D2 | ||
1414 | new OMV.Vector3( 0.0f, +Dwid, Dup), // D3 | ||
1415 | new OMV.Vector3(-Ddep, +Dfwid, Dup), // D4 | ||
1416 | new OMV.Vector3(-Ddep, -Dfwid, Dup), // D5 | ||
1417 | |||
1418 | new OMV.Vector3( 0.0f, -Ewid, Eup), // E0 | ||
1419 | new OMV.Vector3( 0.0f, +Ewid, Eup), // E3 | ||
1420 | }; | ||
1421 | |||
1422 | // Offsets of the vertices in the vertices array | ||
1423 | private enum Ind : int | ||
1424 | { | ||
1425 | A0, A3, | ||
1426 | B0, B1, B2, B3, B4, B5, | ||
1427 | C0, C1, C2, C3, C4, C5, | ||
1428 | D0, D1, D2, D3, D4, D5, | ||
1429 | E0, E3 | ||
1430 | } | ||
1431 | |||
1432 | // Comments specify trianges and quads in clockwise direction | ||
1433 | private Ind[] avatarIndices = { | ||
1434 | Ind.A0, Ind.B0, Ind.B1, // A0,B0,B1 | ||
1435 | Ind.A0, Ind.B1, Ind.B2, Ind.B2, Ind.A3, Ind.A0, // A0,B1,B2,A3 | ||
1436 | Ind.A3, Ind.B2, Ind.B3, // A3,B2,B3 | ||
1437 | Ind.A3, Ind.B3, Ind.B4, // A3,B3,B4 | ||
1438 | Ind.A3, Ind.B4, Ind.B5, Ind.B5, Ind.A0, Ind.A3, // A3,B4,B5,A0 | ||
1439 | Ind.A0, Ind.B5, Ind.B0, // A0,B5,B0 | ||
1440 | |||
1441 | Ind.B0, Ind.C0, Ind.C1, Ind.C1, Ind.B1, Ind.B0, // B0,C0,C1,B1 | ||
1442 | Ind.B1, Ind.C1, Ind.C2, Ind.C2, Ind.B2, Ind.B1, // B1,C1,C2,B2 | ||
1443 | Ind.B2, Ind.C2, Ind.C3, Ind.C3, Ind.B3, Ind.B2, // B2,C2,C3,B3 | ||
1444 | Ind.B3, Ind.C3, Ind.C4, Ind.C4, Ind.B4, Ind.B3, // B3,C3,C4,B4 | ||
1445 | Ind.B4, Ind.C4, Ind.C5, Ind.C5, Ind.B5, Ind.B4, // B4,C4,C5,B5 | ||
1446 | Ind.B5, Ind.C5, Ind.C0, Ind.C0, Ind.B0, Ind.B5, // B5,C5,C0,B0 | ||
1447 | |||
1448 | Ind.C0, Ind.D0, Ind.D1, Ind.D1, Ind.C1, Ind.C0, // C0,D0,D1,C1 | ||
1449 | Ind.C1, Ind.D1, Ind.D2, Ind.D2, Ind.C2, Ind.C1, // C1,D1,D2,C2 | ||
1450 | Ind.C2, Ind.D2, Ind.D3, Ind.D3, Ind.C3, Ind.C2, // C2,D2,D3,C3 | ||
1451 | Ind.C3, Ind.D3, Ind.D4, Ind.D4, Ind.C4, Ind.C3, // C3,D3,D4,C4 | ||
1452 | Ind.C4, Ind.D4, Ind.D5, Ind.D5, Ind.C5, Ind.C4, // C4,D4,D5,C5 | ||
1453 | Ind.C5, Ind.D5, Ind.D0, Ind.D0, Ind.C0, Ind.C5, // C5,D5,D0,C0 | ||
1454 | |||
1455 | Ind.E0, Ind.D0, Ind.D1, // E0,D0,D1 | ||
1456 | Ind.E0, Ind.D1, Ind.D2, Ind.D2, Ind.E3, Ind.E0, // E0,D1,D2,E3 | ||
1457 | Ind.E3, Ind.D2, Ind.D3, // E3,D2,D3 | ||
1458 | Ind.E3, Ind.D3, Ind.D4, // E3,D3,D4 | ||
1459 | Ind.E3, Ind.D4, Ind.D5, Ind.D5, Ind.E0, Ind.E3, // E3,D4,D5,E0 | ||
1460 | Ind.E0, Ind.D5, Ind.D0, // E0,D5,D0 | ||
1461 | |||
1462 | }; | ||
1463 | |||
1464 | } | ||
1465 | } | ||