BulletSim: add asset fetching so BulletSim works with new physics asset handling.

Refactor some names to make them available for the asset tracking and fetching.
connector_plugin
Robert Adams 2012-10-19 15:43:31 -07:00
parent f422b9b388
commit d94c4646cc
4 changed files with 245 additions and 170 deletions

View File

@ -200,10 +200,9 @@ public class BSCharacter : BSPhysObject
} }
} }
public override OMV.Vector3 Scale { get; set; } public override OMV.Vector3 Scale { get; set; }
private PrimitiveBaseShape _pbs;
public override PrimitiveBaseShape Shape public override PrimitiveBaseShape Shape
{ {
set { _pbs = value;} set { BaseShape = value; }
} }
public override bool Grabbed { public override bool Grabbed {

View File

@ -47,6 +47,7 @@ public abstract class BSPhysObject : PhysicsActor
TypeName = typeName; TypeName = typeName;
Linkset = new BSLinkset(PhysicsScene, this); Linkset = new BSLinkset(PhysicsScene, this);
LastAssetBuildFailed = false;
CollisionCollection = new CollisionEventUpdate(); CollisionCollection = new CollisionEventUpdate();
SubscribedEventsMs = 0; SubscribedEventsMs = 0;
@ -69,6 +70,13 @@ public abstract class BSPhysObject : PhysicsActor
// Reference to the physical shape (btCollisionShape) of this object // Reference to the physical shape (btCollisionShape) of this object
public BulletShape BSShape; public BulletShape BSShape;
// 'true' if the mesh's underlying asset failed to build.
// This will keep us from looping after the first time the build failed.
public bool LastAssetBuildFailed { get; set; }
// The objects base shape information. Null if not a prim type shape.
public PrimitiveBaseShape BaseShape { get; protected set; }
// When the physical properties are updated, an EntityProperty holds the update values. // When the physical properties are updated, an EntityProperty holds the update values.
// Keep the current and last EntityProperties to enable computation of differences // Keep the current and last EntityProperties to enable computation of differences
// between the current update and the previous values. // between the current update and the previous values.
@ -101,6 +109,8 @@ public abstract class BSPhysObject : PhysicsActor
public abstract float ForceBuoyancy { get; set; } public abstract float ForceBuoyancy { get; set; }
public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
#region Collisions #region Collisions
// Requested number of milliseconds between collision events. Zero means disabled. // Requested number of milliseconds between collision events. Zero means disabled.

View File

@ -46,8 +46,6 @@ public sealed class BSPrim : BSPhysObject
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[BULLETS PRIM]"; private static readonly string LogHeader = "[BULLETS PRIM]";
private PrimitiveBaseShape _pbs;
// _size is what the user passed. Scale is what we pass to the physics engine with the mesh. // _size is what the user passed. Scale is what we pass to the physics engine with the mesh.
// Often Scale is unity because the meshmerizer will apply _size when creating the mesh. // Often Scale is unity because the meshmerizer will apply _size when creating the mesh.
private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user
@ -103,7 +101,7 @@ public sealed class BSPrim : BSPhysObject
_buoyancy = 1f; _buoyancy = 1f;
_velocity = OMV.Vector3.Zero; _velocity = OMV.Vector3.Zero;
_rotationalVelocity = OMV.Vector3.Zero; _rotationalVelocity = OMV.Vector3.Zero;
_pbs = pbs; BaseShape = pbs;
_isPhysical = pisPhysical; _isPhysical = pisPhysical;
_isVolumeDetect = false; _isVolumeDetect = false;
_friction = PhysicsScene.Params.defaultFriction; // TODO: compute based on object material _friction = PhysicsScene.Params.defaultFriction; // TODO: compute based on object material
@ -160,14 +158,7 @@ public sealed class BSPrim : BSPhysObject
get { return _size; } get { return _size; }
set { set {
_size = value; _size = value;
PhysicsScene.TaintedObject("BSPrim.setSize", delegate() ForceBodyShapeRebuild(false);
{
_mass = CalculateMass(); // changing size changes the mass
// Since _size changed, the mesh needs to be rebuilt. If rebuilt, all the correct
// scale and margins are set.
CreateGeomAndObject(true);
// DetailLog("{0},BSPrim.setSize,size={1},scale={2},mass={3},physical={4}", LocalID, _size, Scale, _mass, IsPhysical);
});
} }
} }
// Scale is what we set in the physics engine. It is different than 'size' in that // Scale is what we set in the physics engine. It is different than 'size' in that
@ -176,14 +167,23 @@ public sealed class BSPrim : BSPhysObject
public override PrimitiveBaseShape Shape { public override PrimitiveBaseShape Shape {
set { set {
_pbs = value; BaseShape = value;
PhysicsScene.TaintedObject("BSPrim.setShape", delegate() ForceBodyShapeRebuild(false);
{
_mass = CalculateMass(); // changing the shape changes the mass
CreateGeomAndObject(true);
});
} }
} }
public override bool ForceBodyShapeRebuild(bool inTaintTime)
{
BSScene.TaintCallback rebuildOperation = delegate()
{
_mass = CalculateMass(); // changing the shape changes the mass
CreateGeomAndObject(true);
};
if (inTaintTime)
rebuildOperation();
else
PhysicsScene.TaintedObject("BSPrim.ForceBodyShapeRebuild", rebuildOperation);
return true;
}
public override bool Grabbed { public override bool Grabbed {
set { _grabbed = value; set { _grabbed = value;
} }
@ -924,19 +924,19 @@ public sealed class BSPrim : BSPhysObject
float tmp; float tmp;
float returnMass = 0; float returnMass = 0;
float hollowAmount = (float)_pbs.ProfileHollow * 2.0e-5f; float hollowAmount = (float)BaseShape.ProfileHollow * 2.0e-5f;
float hollowVolume = hollowAmount * hollowAmount; float hollowVolume = hollowAmount * hollowAmount;
switch (_pbs.ProfileShape) switch (BaseShape.ProfileShape)
{ {
case ProfileShape.Square: case ProfileShape.Square:
// default box // default box
if (_pbs.PathCurve == (byte)Extrusion.Straight) if (BaseShape.PathCurve == (byte)Extrusion.Straight)
{ {
if (hollowAmount > 0.0) if (hollowAmount > 0.0)
{ {
switch (_pbs.HollowShape) switch (BaseShape.HollowShape)
{ {
case HollowShape.Square: case HollowShape.Square:
case HollowShape.Same: case HollowShape.Same:
@ -960,19 +960,19 @@ public sealed class BSPrim : BSPhysObject
} }
} }
else if (_pbs.PathCurve == (byte)Extrusion.Curve1) else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
{ {
//a tube //a tube
volume *= 0.78539816339e-2f * (float)(200 - _pbs.PathScaleX); volume *= 0.78539816339e-2f * (float)(200 - BaseShape.PathScaleX);
tmp= 1.0f -2.0e-2f * (float)(200 - _pbs.PathScaleY); tmp= 1.0f -2.0e-2f * (float)(200 - BaseShape.PathScaleY);
volume -= volume*tmp*tmp; volume -= volume*tmp*tmp;
if (hollowAmount > 0.0) if (hollowAmount > 0.0)
{ {
hollowVolume *= hollowAmount; hollowVolume *= hollowAmount;
switch (_pbs.HollowShape) switch (BaseShape.HollowShape)
{ {
case HollowShape.Square: case HollowShape.Square:
case HollowShape.Same: case HollowShape.Same:
@ -997,13 +997,13 @@ public sealed class BSPrim : BSPhysObject
case ProfileShape.Circle: case ProfileShape.Circle:
if (_pbs.PathCurve == (byte)Extrusion.Straight) if (BaseShape.PathCurve == (byte)Extrusion.Straight)
{ {
volume *= 0.78539816339f; // elipse base volume *= 0.78539816339f; // elipse base
if (hollowAmount > 0.0) if (hollowAmount > 0.0)
{ {
switch (_pbs.HollowShape) switch (BaseShape.HollowShape)
{ {
case HollowShape.Same: case HollowShape.Same:
case HollowShape.Circle: case HollowShape.Circle:
@ -1025,10 +1025,10 @@ public sealed class BSPrim : BSPhysObject
} }
} }
else if (_pbs.PathCurve == (byte)Extrusion.Curve1) else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
{ {
volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - _pbs.PathScaleX); volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - BaseShape.PathScaleX);
tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
volume *= (1.0f - tmp * tmp); volume *= (1.0f - tmp * tmp);
if (hollowAmount > 0.0) if (hollowAmount > 0.0)
@ -1037,7 +1037,7 @@ public sealed class BSPrim : BSPhysObject
// calculate the hollow volume by it's shape compared to the prim shape // calculate the hollow volume by it's shape compared to the prim shape
hollowVolume *= hollowAmount; hollowVolume *= hollowAmount;
switch (_pbs.HollowShape) switch (BaseShape.HollowShape)
{ {
case HollowShape.Same: case HollowShape.Same:
case HollowShape.Circle: case HollowShape.Circle:
@ -1061,7 +1061,7 @@ public sealed class BSPrim : BSPhysObject
break; break;
case ProfileShape.HalfCircle: case ProfileShape.HalfCircle:
if (_pbs.PathCurve == (byte)Extrusion.Curve1) if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
{ {
volume *= 0.52359877559829887307710723054658f; volume *= 0.52359877559829887307710723054658f;
} }
@ -1069,7 +1069,7 @@ public sealed class BSPrim : BSPhysObject
case ProfileShape.EquilateralTriangle: case ProfileShape.EquilateralTriangle:
if (_pbs.PathCurve == (byte)Extrusion.Straight) if (BaseShape.PathCurve == (byte)Extrusion.Straight)
{ {
volume *= 0.32475953f; volume *= 0.32475953f;
@ -1077,7 +1077,7 @@ public sealed class BSPrim : BSPhysObject
{ {
// calculate the hollow volume by it's shape compared to the prim shape // calculate the hollow volume by it's shape compared to the prim shape
switch (_pbs.HollowShape) switch (BaseShape.HollowShape)
{ {
case HollowShape.Same: case HollowShape.Same:
case HollowShape.Triangle: case HollowShape.Triangle:
@ -1102,11 +1102,11 @@ public sealed class BSPrim : BSPhysObject
volume *= (1.0f - hollowVolume); volume *= (1.0f - hollowVolume);
} }
} }
else if (_pbs.PathCurve == (byte)Extrusion.Curve1) else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
{ {
volume *= 0.32475953f; volume *= 0.32475953f;
volume *= 0.01f * (float)(200 - _pbs.PathScaleX); volume *= 0.01f * (float)(200 - BaseShape.PathScaleX);
tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
volume *= (1.0f - tmp * tmp); volume *= (1.0f - tmp * tmp);
if (hollowAmount > 0.0) if (hollowAmount > 0.0)
@ -1114,7 +1114,7 @@ public sealed class BSPrim : BSPhysObject
hollowVolume *= hollowAmount; hollowVolume *= hollowAmount;
switch (_pbs.HollowShape) switch (BaseShape.HollowShape)
{ {
case HollowShape.Same: case HollowShape.Same:
case HollowShape.Triangle: case HollowShape.Triangle:
@ -1154,26 +1154,26 @@ public sealed class BSPrim : BSPhysObject
float profileBegin; float profileBegin;
float profileEnd; float profileEnd;
if (_pbs.PathCurve == (byte)Extrusion.Straight || _pbs.PathCurve == (byte)Extrusion.Flexible) if (BaseShape.PathCurve == (byte)Extrusion.Straight || BaseShape.PathCurve == (byte)Extrusion.Flexible)
{ {
taperX1 = _pbs.PathScaleX * 0.01f; taperX1 = BaseShape.PathScaleX * 0.01f;
if (taperX1 > 1.0f) if (taperX1 > 1.0f)
taperX1 = 2.0f - taperX1; taperX1 = 2.0f - taperX1;
taperX = 1.0f - taperX1; taperX = 1.0f - taperX1;
taperY1 = _pbs.PathScaleY * 0.01f; taperY1 = BaseShape.PathScaleY * 0.01f;
if (taperY1 > 1.0f) if (taperY1 > 1.0f)
taperY1 = 2.0f - taperY1; taperY1 = 2.0f - taperY1;
taperY = 1.0f - taperY1; taperY = 1.0f - taperY1;
} }
else else
{ {
taperX = _pbs.PathTaperX * 0.01f; taperX = BaseShape.PathTaperX * 0.01f;
if (taperX < 0.0f) if (taperX < 0.0f)
taperX = -taperX; taperX = -taperX;
taperX1 = 1.0f - taperX; taperX1 = 1.0f - taperX;
taperY = _pbs.PathTaperY * 0.01f; taperY = BaseShape.PathTaperY * 0.01f;
if (taperY < 0.0f) if (taperY < 0.0f)
taperY = -taperY; taperY = -taperY;
taperY1 = 1.0f - taperY; taperY1 = 1.0f - taperY;
@ -1183,13 +1183,13 @@ public sealed class BSPrim : BSPhysObject
volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY); volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY);
pathBegin = (float)_pbs.PathBegin * 2.0e-5f; pathBegin = (float)BaseShape.PathBegin * 2.0e-5f;
pathEnd = 1.0f - (float)_pbs.PathEnd * 2.0e-5f; pathEnd = 1.0f - (float)BaseShape.PathEnd * 2.0e-5f;
volume *= (pathEnd - pathBegin); volume *= (pathEnd - pathBegin);
// this is crude aproximation // this is crude aproximation
profileBegin = (float)_pbs.ProfileBegin * 2.0e-5f; profileBegin = (float)BaseShape.ProfileBegin * 2.0e-5f;
profileEnd = 1.0f - (float)_pbs.ProfileEnd * 2.0e-5f; profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f;
volume *= (profileEnd - profileBegin); volume *= (profileEnd - profileBegin);
returnMass = _density * volume; returnMass = _density * volume;
@ -1251,7 +1251,7 @@ public sealed class BSPrim : BSPhysObject
// Create the correct physical representation for this type of object. // Create the correct physical representation for this type of object.
// Updates BSBody and BSShape with the new information. // Updates BSBody and BSShape with the new information.
// Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary.
PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, _pbs, PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, BaseShape,
null, delegate(BulletBody dBody) null, delegate(BulletBody dBody)
{ {
// Called if the current prim body is about to be destroyed. // Called if the current prim body is about to be destroyed.

View File

@ -117,7 +117,7 @@ public class BSShapeCollection : IDisposable
// Track another user of a body // Track another user of a body
// We presume the caller has allocated the body. // We presume the caller has allocated the body.
// Bodies only have one user so the reference count is either 1 or 0. // 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) public void ReferenceBody(BulletBody body, bool inTaintTime)
{ {
lock (m_collectionActivityLock) lock (m_collectionActivityLock)
@ -241,26 +241,32 @@ public class BSShapeCollection : IDisposable
BSScene.TaintCallback dereferenceOperation = delegate() BSScene.TaintCallback dereferenceOperation = delegate()
{ {
switch (shape.type) if (shape.ptr != IntPtr.Zero)
{ {
case ShapeData.PhysicsShapeType.SHAPE_HULL: if (shape.isNativeShape)
DereferenceHull(shape, shapeCallback); {
break;
case ShapeData.PhysicsShapeType.SHAPE_MESH:
DereferenceMesh(shape, shapeCallback);
break;
case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
break;
default:
// Native shapes are not tracked and are released immediately // Native shapes are not tracked and are released immediately
if (shape.ptr != IntPtr.Zero & shape.isNativeShape) DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime);
if (shapeCallback != null) shapeCallback(shape);
BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
}
else
{
switch (shape.type)
{ {
DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}", case ShapeData.PhysicsShapeType.SHAPE_HULL:
BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime); DereferenceHull(shape, shapeCallback);
if (shapeCallback != null) shapeCallback(shape); break;
BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); case ShapeData.PhysicsShapeType.SHAPE_MESH:
DereferenceMesh(shape, shapeCallback);
break;
case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
break;
default:
break;
} }
break; }
} }
}; };
if (inTaintTime) if (inTaintTime)
@ -405,7 +411,6 @@ public class BSShapeCollection : IDisposable
ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey, ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey,
ShapeDestructionCallback shapeCallback) ShapeDestructionCallback shapeCallback)
{ {
BulletShape newShape;
shapeData.Type = shapeType; shapeData.Type = shapeType;
// Bullet native objects are scaled by the Bullet engine so pass the size in // Bullet native objects are scaled by the Bullet engine so pass the size in
@ -415,20 +420,7 @@ public class BSShapeCollection : IDisposable
// release any previous shape // release any previous shape
DereferenceShape(prim.BSShape, true, shapeCallback); DereferenceShape(prim.BSShape, true, shapeCallback);
if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR) BulletShape newShape = BuildPhysicalNativeShape(shapeType, shapeData, shapeKey);
{
newShape = new BulletShape(
BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1.0f, 1.0f, shapeData.Scale),
shapeType);
newShape.shapeKey = (System.UInt64)shapeKey;
newShape.isNativeShape = true;
}
else
{
newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType);
newShape.shapeKey = (System.UInt64)shapeKey;
newShape.isNativeShape = true;
}
// Don't need to do a 'ReferenceShape()' here because native shapes are not shared. // Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}", DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
@ -438,6 +430,27 @@ public class BSShapeCollection : IDisposable
return true; return true;
} }
private BulletShape BuildPhysicalNativeShape(ShapeData.PhysicsShapeType shapeType,
ShapeData shapeData, ShapeData.FixedShapeKey shapeKey)
{
BulletShape newShape;
if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR)
{
newShape = new BulletShape(
BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1.0f, 1.0f, shapeData.Scale),
shapeType);
}
else
{
newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType);
}
newShape.shapeKey = (System.UInt64)shapeKey;
newShape.isNativeShape = true;
return newShape;
}
// Builds a mesh shape in the physical world and updates prim.BSShape. // 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. // Dereferences previous shape in BSShape and adds a reference for this new shape.
// Returns 'true' of a mesh was actually built. Otherwise . // Returns 'true' of a mesh was actually built. Otherwise .
@ -461,6 +474,8 @@ public class BSShapeCollection : IDisposable
DereferenceShape(prim.BSShape, true, shapeCallback); DereferenceShape(prim.BSShape, true, shapeCallback);
newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod); newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod);
// Take evasive action if the mesh was not constructed.
newShape = VerifyMeshCreated(newShape, prim, shapeData, pbs);
ReferenceShape(newShape); ReferenceShape(newShape);
@ -474,7 +489,7 @@ public class BSShapeCollection : IDisposable
private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
{ {
IMesh meshData = null; IMesh meshData = null;
IntPtr meshPtr; IntPtr meshPtr = IntPtr.Zero;
MeshDesc meshDesc; MeshDesc meshDesc;
if (Meshes.TryGetValue(newMeshKey, out meshDesc)) if (Meshes.TryGetValue(newMeshKey, out meshDesc))
{ {
@ -486,23 +501,26 @@ public class BSShapeCollection : IDisposable
// Pass false for physicalness as this creates some sort of bounding box which we don't need // Pass false for physicalness as this creates some sort of bounding box which we don't need
meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
int[] indices = meshData.getIndexListAsInt(); if (meshData != null)
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; int[] indices = meshData.getIndexListAsInt();
verticesAsFloats[vi++] = vv.Y; List<OMV.Vector3> vertices = meshData.getVertexList();
verticesAsFloats[vi++] = vv.Z;
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}: CreateGeomMesh: 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);
} }
// m_log.DebugFormat("{0}: CreateGeomMesh: 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, ShapeData.PhysicsShapeType.SHAPE_MESH); BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH);
newShape.shapeKey = newMeshKey; newShape.shapeKey = newMeshKey;
@ -531,6 +549,7 @@ public class BSShapeCollection : IDisposable
DereferenceShape(prim.BSShape, true, shapeCallback); DereferenceShape(prim.BSShape, true, shapeCallback);
newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod); newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod);
newShape = VerifyMeshCreated(newShape, prim, shapeData, pbs);
ReferenceShape(newShape); ReferenceShape(newShape);
@ -544,7 +563,7 @@ public class BSShapeCollection : IDisposable
private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
{ {
IntPtr hullPtr; IntPtr hullPtr = IntPtr.Zero;
HullDesc hullDesc; HullDesc hullDesc;
if (Hulls.TryGetValue(newHullKey, out hullDesc)) if (Hulls.TryGetValue(newHullKey, out hullDesc))
{ {
@ -556,86 +575,89 @@ public class BSShapeCollection : IDisposable
// Build a new hull in the physical world // Build a new hull in the physical world
// Pass false for physicalness as this creates some sort of bounding box which we don't need // Pass false for physicalness as this creates some sort of bounding box which we don't need
IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, 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 int[] indices = meshData.getIndexListAsInt();
m_hulls = new List<ConvexResult>(); List<OMV.Vector3> vertices = meshData.getVertexList();
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. //format conversion from IMesh format to DecompDesc format
// The hull information is passed as a large floating point array. List<int> convIndices = new List<int>();
// The format is: List<float3> convVertices = new List<float3>();
// convHulls[0] = number of hulls for (int ii = 0; ii < indices.GetLength(0); ii++)
// 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; convIndices.Add(indices[ii]);
}
foreach (OMV.Vector3 vv in vertices)
{
convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
} }
// add to the array one hull's worth of data // setup and do convex hull conversion
convHulls[jj++] = cr.HullIndices.Count; m_hulls = new List<ConvexResult>();
convHulls[jj++] = 0f; // centroid x,y,z DecompDesc dcomp = new DecompDesc();
convHulls[jj++] = 0f; dcomp.mIndices = convIndices;
convHulls[jj++] = 0f; dcomp.mVertices = convVertices;
foreach (int ind in cr.HullIndices) 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)
{ {
convHulls[jj++] = verts[ind].x; totalVertices += 4; // add four for the vertex count and centroid
convHulls[jj++] = verts[ind].y; totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
convHulls[jj++] = verts[ind].z;
} }
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);
} }
// create the hull data structure in Bullet
hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
} }
BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL); BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL);
@ -676,6 +698,50 @@ public class BSShapeCollection : IDisposable
return ComputeShapeKey(shapeData, pbs, out lod); return ComputeShapeKey(shapeData, pbs, out lod);
} }
private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs)
{
// If the shape was successfully created, nothing more to do
if (newShape.ptr != IntPtr.Zero)
return newShape;
// The most common reason for failure is that an underlying asset is not available
// If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
if (pbs.SculptEntry && !prim.LastAssetBuildFailed && pbs.SculptTexture != OMV.UUID.Zero)
{
prim.LastAssetBuildFailed = true;
BSPhysObject xprim = prim;
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 = new byte[asset.Data.Length];
asset.Data.CopyTo(yprim.BaseShape.SculptData, 0);
// This will cause the prim to see that the filler shape is not the right
// one and try again to build the object.
yprim.ForceBodyShapeRebuild(false);
});
}
});
}
// While we figure out the real problem, stick a simple native shape on the object.
BulletShape fillinShape =
BuildPhysicalNativeShape(ShapeData.PhysicsShapeType.SHAPE_SPHERE, shapeData, ShapeData.FixedShapeKey.KEY_SPHERE);
return fillinShape;
}
// Create a body object in Bullet. // Create a body object in Bullet.
// Updates prim.BSBody with the information about the new body if one is created. // Updates prim.BSBody with the information about the new body if one is created.
// Returns 'true' if an object was actually created. // Returns 'true' if an object was actually created.