1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015 |
- /*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyrightD
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the OpenSimulator Project nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- using System;
- using System.Collections.Generic;
- using System.Text;
- using OMV = OpenMetaverse;
- using OpenSim.Framework;
- using OpenSim.Region.Physics.Manager;
- using OpenSim.Region.Physics.ConvexDecompositionDotNet;
- namespace OpenSim.Region.Physics.BulletSNPlugin
- {
- public sealed class BSShapeCollection : IDisposable
- {
- private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]";
- private BSScene PhysicsScene { get; set; }
- private Object m_collectionActivityLock = new Object();
- // Description of a Mesh
- private struct MeshDesc
- {
- public Object ptr;
- public int referenceCount;
- public DateTime lastReferenced;
- public UInt64 shapeKey;
- }
- // Description of a hull.
- // Meshes and hulls have the same shape hash key but we only need hulls for efficient collision calculations.
- private struct HullDesc
- {
- public Object ptr;
- public int referenceCount;
- public DateTime lastReferenced;
- public UInt64 shapeKey;
- }
- // The sharable set of meshes and hulls. Indexed by their shape hash.
- private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>();
- private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>();
- private bool DDetail = false;
- public BSShapeCollection(BSScene physScene)
- {
- PhysicsScene = physScene;
- // Set the next to 'true' for very detailed shape update detailed logging (detailed details?)
- // While detailed debugging is still active, this is better than commenting out all the
- // DetailLog statements. When debugging slows down, this and the protected logging
- // statements can be commented/removed.
- DDetail = true;
- }
- public void Dispose()
- {
- // TODO!!!!!!!!!
- }
- // Callbacks called just before either the body or shape is destroyed.
- // Mostly used for changing bodies out from under Linksets.
- // Useful for other cases where parameters need saving.
- // Passing 'null' says no callback.
- public delegate void ShapeDestructionCallback(BulletShape shape);
- public delegate void BodyDestructionCallback(BulletBody body);
- // Called to update/change the body and shape for an object.
- // First checks the shape and updates that if necessary then makes
- // sure the body is of the right type.
- // Return 'true' if either the body or the shape changed.
- // 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before
- // the current shape or body is destroyed. This allows the caller to remove any
- // higher level dependencies on the shape or body. Mostly used for LinkSets to
- // remove the physical constraints before the body is destroyed.
- // Called at taint-time!!
- public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim,
- ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
- {
- PhysicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape");
- bool ret = false;
- // This lock could probably be pushed down lower but building shouldn't take long
- lock (m_collectionActivityLock)
- {
- // Do we have the correct geometry for this type of object?
- // Updates prim.BSShape with information/pointers to shape.
- // Returns 'true' of BSShape is changed to a new shape.
- bool newGeom = CreateGeom(forceRebuild, prim, shapeCallback);
- // If we had to select a new shape geometry for the object,
- // rebuild the body around it.
- // Updates prim.BSBody with information/pointers to requested body
- // Returns 'true' if BSBody was changed.
- bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World,
- prim.PhysShape, bodyCallback);
- ret = newGeom || newBody;
- }
- DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}",
- prim.LocalID, forceRebuild, ret, prim.PhysBody, prim.PhysShape);
- return ret;
- }
- public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim)
- {
- return GetBodyAndShape(forceRebuild, sim, prim, null, null);
- }
- // Track another user of a body.
- // We presume the caller has allocated the body.
- // Bodies only have one user so the body is just put into the world if not already there.
- public void ReferenceBody(BulletBody body, bool inTaintTime)
- {
- lock (m_collectionActivityLock)
- {
- if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body);
- PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.ReferenceBody", delegate()
- {
- if (!BulletSimAPI.IsInWorld2(PhysicsScene.World.ptr, body.ptr))
- {
- BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr);
- if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
- }
- });
- }
- }
- // Release the usage of a body.
- // Called when releasing use of a BSBody. BSShape is handled separately.
- public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback )
- {
- if (!body.HasPhysicalBody)
- return;
- lock (m_collectionActivityLock)
- {
- PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceBody", delegate()
- {
- if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1},inTaintTime={2}",
- body.ID, body, inTaintTime);
- // If the caller needs to know the old body is going away, pass the event up.
- if (bodyCallback != null) bodyCallback(body);
- if (BulletSimAPI.IsInWorld2(PhysicsScene.World.ptr, body.ptr))
- {
- BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
- if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body);
- }
- // Zero any reference to the shape so it is not freed when the body is deleted.
- BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, null);
- BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
- });
- }
- }
- // Track the datastructures and use count for a shape.
- // When creating a hull, this is called first to reference the mesh
- // and then again to reference the hull.
- // Meshes and hulls for the same shape have the same hash key.
- // NOTE that native shapes are not added to the mesh list or removed.
- // Returns 'true' if this is the initial reference to the shape. Otherwise reused.
- public bool ReferenceShape(BulletShape shape)
- {
- bool ret = false;
- switch (shape.type)
- {
- case BSPhysicsShapeType.SHAPE_MESH:
- MeshDesc meshDesc;
- if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
- {
- // There is an existing instance of this mesh.
- meshDesc.referenceCount++;
- if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}",
- BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
- }
- else
- {
- // This is a new reference to a mesh
- meshDesc.ptr = shape.ptr;
- meshDesc.shapeKey = shape.shapeKey;
- // We keep a reference to the underlying IMesh data so a hull can be built
- meshDesc.referenceCount = 1;
- if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}",
- BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
- ret = true;
- }
- meshDesc.lastReferenced = System.DateTime.Now;
- Meshes[shape.shapeKey] = meshDesc;
- break;
- case BSPhysicsShapeType.SHAPE_HULL:
- HullDesc hullDesc;
- if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
- {
- // There is an existing instance of this hull.
- hullDesc.referenceCount++;
- if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}",
- BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
- }
- else
- {
- // This is a new reference to a hull
- hullDesc.ptr = shape.ptr;
- hullDesc.shapeKey = shape.shapeKey;
- hullDesc.referenceCount = 1;
- if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}",
- BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
- ret = true;
- }
- hullDesc.lastReferenced = System.DateTime.Now;
- Hulls[shape.shapeKey] = hullDesc;
- break;
- case BSPhysicsShapeType.SHAPE_UNKNOWN:
- break;
- default:
- // Native shapes are not tracked and they don't go into any list
- break;
- }
- return ret;
- }
- // Release the usage of a shape.
- public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback)
- {
- if (!shape.HasPhysicalShape)
- return;
- PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceShape", delegate()
- {
- if (shape.HasPhysicalShape)
- {
- if (shape.isNativeShape)
- {
- // Native shapes are not tracked and are released immediately
- if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
- BSScene.DetailLogZero, shape.ptr.ToString(), inTaintTime);
- if (shapeCallback != null) shapeCallback(shape);
- BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
- }
- else
- {
- switch (shape.type)
- {
- case BSPhysicsShapeType.SHAPE_HULL:
- DereferenceHull(shape, shapeCallback);
- break;
- case BSPhysicsShapeType.SHAPE_MESH:
- DereferenceMesh(shape, shapeCallback);
- break;
- case BSPhysicsShapeType.SHAPE_COMPOUND:
- DereferenceCompound(shape, shapeCallback);
- break;
- case BSPhysicsShapeType.SHAPE_UNKNOWN:
- break;
- default:
- break;
- }
- }
- }
- });
- }
- // Count down the reference count for a mesh shape
- // Called at taint-time.
- private void DereferenceMesh(BulletShape shape, ShapeDestructionCallback shapeCallback)
- {
- MeshDesc meshDesc;
- if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
- {
- meshDesc.referenceCount--;
- // TODO: release the Bullet storage
- if (shapeCallback != null) shapeCallback(shape);
- meshDesc.lastReferenced = System.DateTime.Now;
- Meshes[shape.shapeKey] = meshDesc;
- if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}",
- BSScene.DetailLogZero, shape, meshDesc.referenceCount);
- }
- }
- // Count down the reference count for a hull shape
- // Called at taint-time.
- private void DereferenceHull(BulletShape shape, ShapeDestructionCallback shapeCallback)
- {
- HullDesc hullDesc;
- if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
- {
- hullDesc.referenceCount--;
- // TODO: release the Bullet storage (aging old entries?)
- // Tell upper layers that, if they have dependencies on this shape, this link is going away
- if (shapeCallback != null) shapeCallback(shape);
- hullDesc.lastReferenced = System.DateTime.Now;
- Hulls[shape.shapeKey] = hullDesc;
- if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}",
- BSScene.DetailLogZero, shape, hullDesc.referenceCount);
- }
- }
- // Remove a reference to a compound shape.
- // Taking a compound shape apart is a little tricky because if you just delete the
- // physical shape, it will free all the underlying children. We can't do that because
- // they could be shared. So, this removes each of the children from the compound and
- // dereferences them separately before destroying the compound collision object itself.
- // Called at taint-time.
- private void DereferenceCompound(BulletShape shape, ShapeDestructionCallback shapeCallback)
- {
- if (!BulletSimAPI.IsCompound2(shape.ptr))
- {
- // Failed the sanity check!!
- PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}",
- LogHeader, shape.type, shape.ptr.ToString());
- if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}",
- BSScene.DetailLogZero, shape.type, shape.ptr.ToString());
- return;
- }
- int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr);
- if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren);
- for (int ii = numChildren - 1; ii >= 0; ii--)
- {
- Object childShape = BulletSimAPI.RemoveChildShapeFromCompoundShapeIndex2(shape.ptr, ii);
- DereferenceAnonCollisionShape(childShape);
- }
- BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
- }
- // Sometimes we have a pointer to a collision shape but don't know what type it is.
- // Figure out type and call the correct dereference routine.
- // Called at taint-time.
- private void DereferenceAnonCollisionShape(Object cShape)
- {
- MeshDesc meshDesc;
- HullDesc hullDesc;
- BulletShape shapeInfo = new BulletShape(cShape);
- if (TryGetMeshByPtr(cShape, out meshDesc))
- {
- shapeInfo.type = BSPhysicsShapeType.SHAPE_MESH;
- shapeInfo.shapeKey = meshDesc.shapeKey;
- }
- else
- {
- if (TryGetHullByPtr(cShape, out hullDesc))
- {
- shapeInfo.type = BSPhysicsShapeType.SHAPE_HULL;
- shapeInfo.shapeKey = hullDesc.shapeKey;
- }
- else
- {
- if (BulletSimAPI.IsCompound2(cShape))
- {
- shapeInfo.type = BSPhysicsShapeType.SHAPE_COMPOUND;
- }
- else
- {
- if (BulletSimAPI.IsNativeShape2(cShape))
- {
- shapeInfo.isNativeShape = true;
- shapeInfo.type = BSPhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter)
- }
- }
- }
- }
- if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo);
- if (shapeInfo.type != BSPhysicsShapeType.SHAPE_UNKNOWN)
- {
- DereferenceShape(shapeInfo, true, null);
- }
- else
- {
- PhysicsScene.Logger.ErrorFormat("{0} Could not decypher shape type. Region={1}, addr={2}",
- LogHeader, PhysicsScene.RegionName, cShape.ToString());
- }
- }
- // Create the geometry information in Bullet for later use.
- // The objects needs a hull if it's physical otherwise a mesh is enough.
- // if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls,
- // shared geometries will be used. If the parameters of the existing shape are the same
- // as this request, the shape is not rebuilt.
- // Info in prim.BSShape is updated to the new shape.
- // Returns 'true' if the geometry was rebuilt.
- // Called at taint-time!
- private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
- {
- bool ret = false;
- bool haveShape = false;
- if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE)
- {
- // an avatar capsule is close to a native shape (it is not shared)
- GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_CAPSULE,
- FixedShapeKey.KEY_CAPSULE, shapeCallback);
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape);
- ret = true;
- haveShape = true;
- }
- // Compound shapes are handled special as they are rebuilt from scratch.
- // This isn't too great a hardship since most of the child shapes will have already been created.
- if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND)
- {
- ret = GetReferenceToCompoundShape(prim, shapeCallback);
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape);
- haveShape = true;
- }
- if (!haveShape)
- {
- ret = CreateGeomNonSpecial(forceRebuild, prim, shapeCallback);
- }
- return ret;
- }
- // Create a mesh/hull shape or a native shape if 'nativeShapePossible' is 'true'.
- public bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
- {
- bool ret = false;
- bool haveShape = false;
- bool nativeShapePossible = true;
- PrimitiveBaseShape pbs = prim.BaseShape;
- // If the prim attributes are simple, this could be a simple Bullet native shape
- if (!haveShape
- && pbs != null
- && nativeShapePossible
- && ((pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim)
- || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
- && pbs.ProfileHollow == 0
- && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
- && pbs.PathBegin == 0 && pbs.PathEnd == 0
- && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
- && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
- && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) )
- {
- // Get the scale of any existing shape so we can see if the new shape is same native type and same size.
- OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero;
- if (prim.PhysShape.HasPhysicalShape)
- scaleOfExistingShape = BulletSimAPI.GetLocalScaling2(prim.PhysShape.ptr);
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}",
- prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.type);
- // It doesn't look like Bullet scales spheres so make sure the scales are all equal
- if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
- && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)
- {
- haveShape = true;
- if (forceRebuild
- || prim.Scale != scaleOfExistingShape
- || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_SPHERE
- )
- {
- ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE,
- FixedShapeKey.KEY_SPHERE, shapeCallback);
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
- prim.LocalID, forceRebuild, prim.PhysShape);
- }
- }
- if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
- {
- haveShape = true;
- if (forceRebuild
- || prim.Scale != scaleOfExistingShape
- || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_BOX
- )
- {
- ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX,
- FixedShapeKey.KEY_BOX, shapeCallback);
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
- prim.LocalID, forceRebuild, prim.PhysShape);
- }
- }
- }
- // If a simple shape is not happening, create a mesh and possibly a hull.
- if (!haveShape && pbs != null)
- {
- ret = CreateGeomMeshOrHull(prim, shapeCallback);
- }
- return ret;
- }
- public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
- {
- bool ret = false;
- // Note that if it's a native shape, the check for physical/non-physical is not
- // made. Native shapes work in either case.
- if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects)
- {
- // Update prim.BSShape to reference a hull of this shape.
- ret = GetReferenceToHull(prim,shapeCallback);
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}",
- prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
- }
- else
- {
- ret = GetReferenceToMesh(prim, shapeCallback);
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}",
- prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
- }
- return ret;
- }
- // Creates a native shape and assignes it to prim.BSShape.
- // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape().
- private bool GetReferenceToNativeShape(BSPhysObject prim,
- BSPhysicsShapeType shapeType, FixedShapeKey shapeKey,
- ShapeDestructionCallback shapeCallback)
- {
- // release any previous shape
- DereferenceShape(prim.PhysShape, true, shapeCallback);
- BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey);
- // Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
- if (DDetail) DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
- prim.LocalID, newShape, prim.Scale);
- // native shapes are scaled by Bullet
- prim.PhysShape = newShape;
- return true;
- }
- private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, BSPhysicsShapeType shapeType,
- FixedShapeKey shapeKey)
- {
- BulletShape newShape;
- // Need to make sure the passed shape information is for the native type.
- ShapeData nativeShapeData = new ShapeData();
- nativeShapeData.Type = shapeType;
- nativeShapeData.ID = prim.LocalID;
- nativeShapeData.Scale = prim.Scale;
- nativeShapeData.Size = prim.Scale; // unneeded, I think.
- nativeShapeData.MeshKey = (ulong)shapeKey;
- nativeShapeData.HullKey = (ulong)shapeKey;
- if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
- {
- // The proper scale has been calculated in the prim.
- newShape = new BulletShape(
- BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale)
- , shapeType);
- if (DDetail) DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
- }
- else
- {
- // Native shapes are scaled in Bullet so set the scaling to the size
- newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType);
- }
- if (!newShape.HasPhysicalShape)
- {
- PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
- LogHeader, prim.LocalID, shapeType);
- }
- newShape.shapeKey = (System.UInt64)shapeKey;
- newShape.isNativeShape = true;
- return newShape;
- }
- // Builds a mesh shape in the physical world and updates prim.BSShape.
- // Dereferences previous shape in BSShape and adds a reference for this new shape.
- // Returns 'true' of a mesh was actually built. Otherwise .
- // Called at taint-time!
- private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
- {
- BulletShape newShape = new BulletShape();
- float lod;
- System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
- // if this new shape is the same as last time, don't recreate the mesh
- if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_MESH)
- return false;
- if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}",
- prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"));
- // Since we're recreating new, get rid of the reference to the previous shape
- DereferenceShape(prim.PhysShape, true, shapeCallback);
- newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, prim.BaseShape, prim.Size, lod);
- // Take evasive action if the mesh was not constructed.
- newShape = VerifyMeshCreated(newShape, prim);
- ReferenceShape(newShape);
- prim.PhysShape = newShape;
- return true; // 'true' means a new shape has been added to this prim
- }
- private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
- {
- IMesh meshData = null;
- Object meshPtr = null;
- MeshDesc meshDesc;
- if (Meshes.TryGetValue(newMeshKey, out meshDesc))
- {
- // If the mesh has already been built just use it.
- meshPtr = meshDesc.ptr;
- }
- else
- {
- meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false);
- if (meshData != null)
- {
- int[] indices = meshData.getIndexListAsInt();
- List<OMV.Vector3> vertices = meshData.getVertexList();
- float[] verticesAsFloats = new float[vertices.Count * 3];
- int vi = 0;
- foreach (OMV.Vector3 vv in vertices)
- {
- verticesAsFloats[vi++] = vv.X;
- verticesAsFloats[vi++] = vv.Y;
- verticesAsFloats[vi++] = vv.Z;
- }
- // m_log.DebugFormat("{0}: BSShapeCollection.CreatePhysicalMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}",
- // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count);
- meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
- indices.GetLength(0), indices, vertices.Count, verticesAsFloats);
- }
- }
- BulletShape newShape = new BulletShape(meshPtr, BSPhysicsShapeType.SHAPE_MESH);
- newShape.shapeKey = newMeshKey;
- return newShape;
- }
- // See that hull shape exists in the physical world and update prim.BSShape.
- // We could be creating the hull because scale changed or whatever.
- private bool GetReferenceToHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
- {
- BulletShape newShape;
- float lod;
- System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
- // if the hull hasn't changed, don't rebuild it
- if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_HULL)
- return false;
- if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}",
- prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X"));
- // Remove usage of the previous shape.
- DereferenceShape(prim.PhysShape, true, shapeCallback);
- newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, prim.BaseShape, prim.Size, lod);
- newShape = VerifyMeshCreated(newShape, prim);
- ReferenceShape(newShape);
- prim.PhysShape = newShape;
- return true; // 'true' means a new shape has been added to this prim
- }
- List<ConvexResult> m_hulls;
- private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
- {
- Object hullPtr = null;
- HullDesc hullDesc;
- if (Hulls.TryGetValue(newHullKey, out hullDesc))
- {
- // If the hull shape already is created, just use it.
- hullPtr = hullDesc.ptr;
- }
- else
- {
- // Build a new hull in the physical world
- // Pass true for physicalness as this creates some sort of bounding box which we don't need
- IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false);
- if (meshData != null)
- {
- int[] indices = meshData.getIndexListAsInt();
- List<OMV.Vector3> vertices = meshData.getVertexList();
- //format conversion from IMesh format to DecompDesc format
- List<int> convIndices = new List<int>();
- List<float3> convVertices = new List<float3>();
- for (int ii = 0; ii < indices.GetLength(0); ii++)
- {
- convIndices.Add(indices[ii]);
- }
- foreach (OMV.Vector3 vv in vertices)
- {
- convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
- }
- // setup and do convex hull conversion
- m_hulls = new List<ConvexResult>();
- DecompDesc dcomp = new DecompDesc();
- dcomp.mIndices = convIndices;
- dcomp.mVertices = convVertices;
- ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
- // create the hull into the _hulls variable
- convexBuilder.process(dcomp);
- // Convert the vertices and indices for passing to unmanaged.
- // The hull information is passed as a large floating point array.
- // The format is:
- // convHulls[0] = number of hulls
- // convHulls[1] = number of vertices in first hull
- // convHulls[2] = hull centroid X coordinate
- // convHulls[3] = hull centroid Y coordinate
- // convHulls[4] = hull centroid Z coordinate
- // convHulls[5] = first hull vertex X
- // convHulls[6] = first hull vertex Y
- // convHulls[7] = first hull vertex Z
- // convHulls[8] = second hull vertex X
- // ...
- // convHulls[n] = number of vertices in second hull
- // convHulls[n+1] = second hull centroid X coordinate
- // ...
- //
- // TODO: is is very inefficient. Someday change the convex hull generator to return
- // data structures that do not need to be converted in order to pass to Bullet.
- // And maybe put the values directly into pinned memory rather than marshaling.
- int hullCount = m_hulls.Count;
- int totalVertices = 1; // include one for the count of the hulls
- foreach (ConvexResult cr in m_hulls)
- {
- totalVertices += 4; // add four for the vertex count and centroid
- totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
- }
- float[] convHulls = new float[totalVertices];
- convHulls[0] = (float)hullCount;
- int jj = 1;
- foreach (ConvexResult cr in m_hulls)
- {
- // copy vertices for index access
- float3[] verts = new float3[cr.HullVertices.Count];
- int kk = 0;
- foreach (float3 ff in cr.HullVertices)
- {
- verts[kk++] = ff;
- }
- // add to the array one hull's worth of data
- convHulls[jj++] = cr.HullIndices.Count;
- convHulls[jj++] = 0f; // centroid x,y,z
- convHulls[jj++] = 0f;
- convHulls[jj++] = 0f;
- foreach (int ind in cr.HullIndices)
- {
- convHulls[jj++] = verts[ind].x;
- convHulls[jj++] = verts[ind].y;
- convHulls[jj++] = verts[ind].z;
- }
- }
- // create the hull data structure in Bullet
- hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
- }
- }
- BulletShape newShape = new BulletShape(hullPtr, BSPhysicsShapeType.SHAPE_HULL);
- newShape.shapeKey = newHullKey;
- return newShape;
- }
- // Callback from convex hull creater with a newly created hull.
- // Just add it to our collection of hulls for this shape.
- private void HullReturn(ConvexResult result)
- {
- m_hulls.Add(result);
- return;
- }
- // Compound shapes are always built from scratch.
- // This shouldn't be to bad since most of the parts will be meshes that had been built previously.
- private bool GetReferenceToCompoundShape(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
- {
- // Remove reference to the old shape
- // Don't need to do this as the shape is freed when the new root shape is created below.
- // DereferenceShape(prim.PhysShape, true, shapeCallback);
- BulletShape cShape = new BulletShape(
- BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr, false), BSPhysicsShapeType.SHAPE_COMPOUND);
- // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape.
- CreateGeomMeshOrHull(prim, shapeCallback);
- BulletSimAPI.AddChildShapeToCompoundShape2(cShape.ptr, prim.PhysShape.ptr, OMV.Vector3.Zero, OMV.Quaternion.Identity);
- if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}",
- prim.LocalID, cShape, prim.PhysShape);
- prim.PhysShape = cShape;
- return true;
- }
- // Create a hash of all the shape parameters to be used as a key
- // for this particular shape.
- private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod)
- {
- // level of detail based on size and type of the object
- float lod = BSParam.MeshLOD;
- if (pbs.SculptEntry)
- lod = BSParam.SculptLOD;
- // Mega prims usually get more detail because one can interact with shape approximations at this size.
- float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z));
- if (maxAxis > BSParam.MeshMegaPrimThreshold)
- lod = BSParam.MeshMegaPrimLOD;
- retLod = lod;
- return pbs.GetMeshKey(size, lod);
- }
- // For those who don't want the LOD
- private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs)
- {
- float lod;
- return ComputeShapeKey(size, pbs, out lod);
- }
- // The creation of a mesh or hull can fail if an underlying asset is not available.
- // There are two cases: 1) the asset is not in the cache and it needs to be fetched;
- // and 2) the asset cannot be converted (like failed decompression of JPEG2000s).
- // The first case causes the asset to be fetched. The second case requires
- // us to not loop forever.
- // Called after creating a physical mesh or hull. If the physical shape was created,
- // just return.
- private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim)
- {
- // If the shape was successfully created, nothing more to do
- if (newShape.HasPhysicalShape)
- return newShape;
- // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
- if (prim.BaseShape.SculptEntry && !prim.LastAssetBuildFailed && prim.BaseShape.SculptTexture != OMV.UUID.Zero)
- {
- prim.LastAssetBuildFailed = true;
- BSPhysObject xprim = prim;
- DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lID={1},lastFailed={2}",
- LogHeader, prim.LocalID, prim.LastAssetBuildFailed);
- Util.FireAndForget(delegate
- {
- RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod;
- if (assetProvider != null)
- {
- BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
- assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
- {
- if (!yprim.BaseShape.SculptEntry)
- return;
- if (yprim.BaseShape.SculptTexture.ToString() != asset.ID)
- return;
- yprim.BaseShape.SculptData = asset.Data;
- // This will cause the prim to see that the filler shape is not the right
- // one and try again to build the object.
- // No race condition with the normal shape setting since the rebuild is at taint time.
- yprim.ForceBodyShapeRebuild(false);
- });
- }
- });
- }
- else
- {
- if (prim.LastAssetBuildFailed)
- {
- PhysicsScene.Logger.ErrorFormat("{0} Mesh failed to fetch asset. lID={1}, texture={2}",
- LogHeader, prim.LocalID, prim.BaseShape.SculptTexture);
- }
- }
- // While we figure out the real problem, stick a simple native shape on the object.
- BulletShape fillinShape =
- BuildPhysicalNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
- return fillinShape;
- }
- // Create a body object in Bullet.
- // Updates prim.BSBody with the information about the new body if one is created.
- // Returns 'true' if an object was actually created.
- // Called at taint-time.
- private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, BulletShape shape,
- BodyDestructionCallback bodyCallback)
- {
- bool ret = false;
- // the mesh, hull or native shape must have already been created in Bullet
- bool mustRebuild = !prim.PhysBody.HasPhysicalBody;
- // If there is an existing body, verify it's of an acceptable type.
- // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
- if (!mustRebuild)
- {
- CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.PhysBody.ptr);
- if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY
- || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT)
- {
- // If the collisionObject is not the correct type for solidness, rebuild what's there
- mustRebuild = true;
- }
- }
- if (mustRebuild || forceRebuild)
- {
- // Free any old body
- DereferenceBody(prim.PhysBody, true, bodyCallback);
- BulletBody aBody;
- Object bodyPtr = null;
- if (prim.IsSolid)
- {
- bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
- prim.LocalID, prim.RawPosition, prim.RawOrientation);
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString());
- }
- else
- {
- bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
- prim.LocalID, prim.RawPosition, prim.RawOrientation);
- if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString());
- }
- aBody = new BulletBody(prim.LocalID, bodyPtr);
- ReferenceBody(aBody, true);
- prim.PhysBody = aBody;
- ret = true;
- }
- return ret;
- }
- private bool TryGetMeshByPtr(Object addr, out MeshDesc outDesc)
- {
- bool ret = false;
- MeshDesc foundDesc = new MeshDesc();
- foreach (MeshDesc md in Meshes.Values)
- {
- if (md.ptr == addr)
- {
- foundDesc = md;
- ret = true;
- break;
- }
- }
- outDesc = foundDesc;
- return ret;
- }
- private bool TryGetHullByPtr(Object addr, out HullDesc outDesc)
- {
- bool ret = false;
- HullDesc foundDesc = new HullDesc();
- foreach (HullDesc hd in Hulls.Values)
- {
- if (hd.ptr == addr)
- {
- foundDesc = hd;
- ret = true;
- break;
- }
- }
- outDesc = foundDesc;
- return ret;
- }
- private void DetailLog(string msg, params Object[] args)
- {
- if (PhysicsScene.PhysicsLogging.Enabled)
- PhysicsScene.DetailLog(msg, args);
- }
- }
- }
|