Pass collisions and updates in pinned memory (saves marshaling).

Fix folding feet by using collision normals.
Add constraint specification.
bulletsim
Robert.Adams 2011-07-22 10:22:21 -07:00 committed by Dan Lake
parent 4cbc912375
commit 5ffec1cd64
8 changed files with 157 additions and 98 deletions

View File

@ -185,7 +185,7 @@ public class BSCharacter : PhysicsActor
get { return _force; } get { return _force; }
set { set {
_force = value; _force = value;
m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force); // m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force);
_scene.TaintedObject(delegate() _scene.TaintedObject(delegate()
{ {
BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force);
@ -411,8 +411,9 @@ public class BSCharacter : PhysicsActor
_collidingGroundStep = _scene.SimulationStep; _collidingGroundStep = _scene.SimulationStep;
} }
// throttle collisions to the rate specified in the subscription
if (_subscribedEventsMs == 0) return; // don't want collisions if (_subscribedEventsMs == 0) return; // don't want collisions
int nowTime = Util.EnvironmentTickCount(); int nowTime = _scene.SimulationNowTime;
if (nowTime < (_lastCollisionTime + _subscribedEventsMs)) return; if (nowTime < (_lastCollisionTime + _subscribedEventsMs)) return;
_lastCollisionTime = nowTime; _lastCollisionTime = nowTime;

View File

@ -261,6 +261,7 @@ public sealed class BSPrim : PhysicsActor
{ {
if (_childrenPrims.Contains(child)) if (_childrenPrims.Contains(child))
{ {
BulletSimAPI.RemoveConstraint(_scene.WorldID, child.LocalID, this.LocalID);
_childrenPrims.Remove(child); _childrenPrims.Remove(child);
child.ParentPrim = null; // the child has lost its parent child.ParentPrim = null; // the child has lost its parent
RecreateGeomAndObject(); // rebuild my shape with the child removed RecreateGeomAndObject(); // rebuild my shape with the child removed
@ -1018,12 +1019,23 @@ public sealed class BSPrim : PhysicsActor
if (IsRootOfLinkset) if (IsRootOfLinkset)
{ {
// Create a linkset around this object // Create a linkset around this object
/* CreateLinksetWithCompoundHull();
* NOTE: the original way of creating a linkset was to create a compound hull in the // CreateLinksetWithConstraints();
* root which consisted of the hulls of all the children. This didn't work well because }
* OpenSimulator needs updates and collisions for all the children and the physics else
* engine didn't create events for the children when the root hull was moved. {
* This code creates the compound hull. // simple object
// m_log.DebugFormat("{0}: CreateObject. ID={1}", LogHeader, LocalID);
ShapeData shape;
FillShapeInfo(out shape);
BulletSimAPI.CreateObject(_scene.WorldID, shape);
}
}
// Create a linkset by creating a compound hull at the root prim that consists of all
// the children.
void CreateLinksetWithCompoundHull()
{
// If I am the root prim of a linkset, replace my physical shape with all the // If I am the root prim of a linkset, replace my physical shape with all the
// pieces of the children. // pieces of the children.
// All of the children should have called CreateGeom so they have a hull // All of the children should have called CreateGeom so they have a hull
@ -1041,14 +1053,18 @@ public sealed class BSPrim : PhysicsActor
ii++; ii++;
} }
BulletSimAPI.CreateLinkset(_scene.WorldID, totalPrimsInLinkset, shapes); BulletSimAPI.CreateLinkset(_scene.WorldID, totalPrimsInLinkset, shapes);
*/ }
// Create the linkset by putting constraints between the objects of the set so they cannot move // Create the linkset by putting constraints between the objects of the set so they cannot move
// relative to each other. // relative to each other.
void CreateLinksetWithConstraints()
{
// m_log.DebugFormat("{0}: CreateLinkset. Root prim={1}, prims={2}", LogHeader, LocalID, _childrenPrims.Count+1); // m_log.DebugFormat("{0}: CreateLinkset. Root prim={1}, prims={2}", LogHeader, LocalID, _childrenPrims.Count+1);
// remove any constraints that might be in place // remove any constraints that might be in place
foreach (BSPrim prim in _childrenPrims) foreach (BSPrim prim in _childrenPrims)
{ {
// m_log.DebugFormat("{0}: CreateObject: RemoveConstraint between root prim {1} and child prim {2}", LogHeader, LocalID, prim.LocalID);
BulletSimAPI.RemoveConstraint(_scene.WorldID, LocalID, prim.LocalID); BulletSimAPI.RemoveConstraint(_scene.WorldID, LocalID, prim.LocalID);
} }
// create constraints between the root prim and each of the children // create constraints between the root prim and each of the children
@ -1056,17 +1072,30 @@ public sealed class BSPrim : PhysicsActor
{ {
// this is a constraint that allows no freedom of movement between the two objects // this is a constraint that allows no freedom of movement between the two objects
// http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
BulletSimAPI.AddConstraint(_scene.WorldID, LocalID, prim.LocalID, OMV.Vector3.Zero, OMV.Vector3.Zero, // m_log.DebugFormat("{0}: CreateObject: AddConstraint between root prim {1} and child prim {2}", LogHeader, LocalID, prim.LocalID);
OMV.Vector3.Zero, OMV.Vector3.Zero, OMV.Vector3.Zero, OMV.Vector3.Zero);
} // relative position normalized to the root prim
} OMV.Vector3 childRelativePosition = (prim._position - this._position) * OMV.Quaternion.Inverse(this._orientation);
else // OMV.Quaternion relativeRotation = OMV.Quaternion.Identity;
{
// simple object // rotation is pointing up the vector between the object centers
// m_log.DebugFormat("{0}: CreateObject. ID={1}", LogHeader, LocalID); OMV.Quaternion relativeRotation = OMV.Quaternion.CreateFromAxisAngle(childRelativePosition, 0f);
ShapeData shape;
FillShapeInfo(out shape); /* // the logic for relative rotation from http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=6391
BulletSimAPI.CreateObject(_scene.WorldID, shape); OMV.Vector3 rrn = childRelativePosition;
rrn.Normalize();
rrn /= rrn.X;
OMV.Matrix4 rotmat = new OMV.Matrix4(rrn.X, rrn.Y, rrn.Z, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
OMV.Quaternion relativeRotation = OMV.Quaternion.CreateFromRotationMatrix(rotmat);
*/
BulletSimAPI.AddConstraint(_scene.WorldID, LocalID, prim.LocalID,
childRelativePosition / 2,
relativeRotation,
-childRelativePosition / 2,
relativeRotation,
OMV.Vector3.Zero, OMV.Vector3.Zero,
OMV.Vector3.Zero, OMV.Vector3.Zero);
} }
} }
@ -1118,49 +1147,52 @@ public sealed class BSPrim : PhysicsActor
// The physics engine says that properties have updated. Update same and inform // The physics engine says that properties have updated. Update same and inform
// the world that things have changed. // the world that things have changed.
// TODO: do we really need to check for changed? Maybe just copy values and call RequestPhysicsterseUpdate() // TODO: do we really need to check for changed? Maybe just copy values and call RequestPhysicsterseUpdate()
private int UpPropPosition = 1 << 0; enum UpdatedProperties {
private int UpPropRotation = 1 << 1; Position = 1 << 0,
private int UpPropVelocity = 1 << 2; Rotation = 1 << 1,
private int UpPropAcceleration = 1 << 3; Velocity = 1 << 2,
private int UpPropAngularVel = 1 << 4; Acceleration = 1 << 3,
AngularVel = 1 << 4
}
public void UpdateProperties(EntityProperties entprop) public void UpdateProperties(EntityProperties entprop)
{ {
int changed = 0; UpdatedProperties changed = 0;
// assign to the local variables so the normal set action does not happen // assign to the local variables so the normal set action does not happen
if (_position != entprop.Position) if (_position != entprop.Position)
{ {
_position = entprop.Position; _position = entprop.Position;
// m_log.DebugFormat("{0}: UpdateProperties: position = {1}", LogHeader, _position); // m_log.DebugFormat("{0}: UpdateProperties: position = {1}", LogHeader, _position);
changed |= UpPropPosition; changed |= UpdatedProperties.Position;
} }
if (_orientation != entprop.Rotation) if (_orientation != entprop.Rotation)
{ {
_orientation = entprop.Rotation; _orientation = entprop.Rotation;
// m_log.DebugFormat("{0}: UpdateProperties: rotation = {1}", LogHeader, _orientation); // m_log.DebugFormat("{0}: UpdateProperties: rotation = {1}", LogHeader, _orientation);
changed |= UpPropRotation; changed |= UpdatedProperties.Rotation;
} }
if (_velocity != entprop.Velocity) if (_velocity != entprop.Velocity)
{ {
_velocity = entprop.Velocity; _velocity = entprop.Velocity;
// m_log.DebugFormat("{0}: UpdateProperties: velocity = {1}", LogHeader, _velocity); // m_log.DebugFormat("{0}: UpdateProperties: velocity = {1}", LogHeader, _velocity);
changed |= UpPropVelocity; changed |= UpdatedProperties.Velocity;
} }
if (_acceleration != entprop.Acceleration) if (_acceleration != entprop.Acceleration)
{ {
_acceleration = entprop.Acceleration; _acceleration = entprop.Acceleration;
// m_log.DebugFormat("{0}: UpdateProperties: acceleration = {1}", LogHeader, _acceleration); // m_log.DebugFormat("{0}: UpdateProperties: acceleration = {1}", LogHeader, _acceleration);
changed |= UpPropAcceleration; changed |= UpdatedProperties.Acceleration;
} }
if (_rotationalVelocity != entprop.AngularVelocity) if (_rotationalVelocity != entprop.AngularVelocity)
{ {
_rotationalVelocity = entprop.AngularVelocity; _rotationalVelocity = entprop.AngularVelocity;
// m_log.DebugFormat("{0}: UpdateProperties: rotationalVelocity = {1}", LogHeader, _rotationalVelocity); // m_log.DebugFormat("{0}: UpdateProperties: rotationalVelocity = {1}", LogHeader, _rotationalVelocity);
changed |= UpPropAngularVel; changed |= UpdatedProperties.AngularVel;
} }
if (changed != 0) if (changed != 0)
{ {
// m_log.DebugFormat("{0}: UpdateProperties: id={1}, c={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation); // m_log.DebugFormat("{0}: UpdateProperties: id={1}, c={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation);
if (this._parentPrim == null)
base.RequestPhysicsterseUpdate(); base.RequestPhysicsterseUpdate();
} }
} }
@ -1178,7 +1210,7 @@ public sealed class BSPrim : PhysicsActor
if (_subscribedEventsMs == 0) return; // nothing in the object is waiting for collision events if (_subscribedEventsMs == 0) return; // nothing in the object is waiting for collision events
// throttle the collisions to the number of milliseconds specified in the subscription // throttle the collisions to the number of milliseconds specified in the subscription
int nowTime = Util.EnvironmentTickCount(); int nowTime = _scene.SimulationNowTime;
if (nowTime < (_lastCollisionTime + _subscribedEventsMs)) return; if (nowTime < (_lastCollisionTime + _subscribedEventsMs)) return;
_lastCollisionTime = nowTime; _lastCollisionTime = nowTime;

View File

@ -38,9 +38,7 @@ using OpenSim.Region.Framework;
// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) // TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
// Fix folding up feet // Fix folding up feet
// Fix terrain. Only flat terrain works. Terrain with shape is oriented wrong? Origined wrong?
// Parameterize BulletSim. Pass a structure of parameters to the C++ code. Capsule size, friction, ... // Parameterize BulletSim. Pass a structure of parameters to the C++ code. Capsule size, friction, ...
// Shift drag duplication of objects does not work
// Adjust character capsule size when height is adjusted (ScenePresence.SetHeight) // Adjust character capsule size when height is adjusted (ScenePresence.SetHeight)
// Test sculpties // Test sculpties
// Compute physics FPS reasonably // Compute physics FPS reasonably
@ -82,6 +80,19 @@ public class BSScene : PhysicsScene
private long m_simulationStep = 0; private long m_simulationStep = 0;
public long SimulationStep { get { return m_simulationStep; } } public long SimulationStep { get { return m_simulationStep; } }
// A value of the time now so all the collision and update routines do not have to get their own
// Set to 'now' just before all the prims and actors are called for collisions and updates
private int m_simulationNowTime;
public int SimulationNowTime { get { return m_simulationNowTime; } }
private int m_maxCollisionsPerFrame = 2048;
private CollisionDesc[] m_collisionArray;
private GCHandle m_collisionArrayPinnedHandle;
private int m_maxUpdatesPerFrame = 2048;
private EntityProperties[] m_updateArray;
private GCHandle m_updateArrayPinnedHandle;
private bool _meshSculptedPrim = true; // cause scuplted prims to get meshed private bool _meshSculptedPrim = true; // cause scuplted prims to get meshed
private bool _forceSimplePrimMeshing = false; // if a cube or sphere, let Bullet do internal shapes private bool _forceSimplePrimMeshing = false; // if a cube or sphere, let Bullet do internal shapes
public float maximumMassObject = 10000.01f; public float maximumMassObject = 10000.01f;
@ -129,8 +140,19 @@ public class BSScene : PhysicsScene
_taintedObjects = new List<TaintCallback>(); _taintedObjects = new List<TaintCallback>();
mesher = meshmerizer; mesher = meshmerizer;
// The bounding box for the simulated world
Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, 4096f);
// Allocate pinned memory to pass back object property updates and collisions from simulation step
m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame];
m_collisionArrayPinnedHandle = GCHandle.Alloc(m_collisionArray, GCHandleType.Pinned);
m_updateArray = new EntityProperties[m_maxUpdatesPerFrame];
m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned);
// m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
m_worldID = BulletSimAPI.Initialize(new Vector3(Constants.RegionSize, Constants.RegionSize, 4096f)); m_worldID = BulletSimAPI.Initialize(worldExtent,
m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(),
m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject());
} }
// Called directly from unmanaged code so don't do much // Called directly from unmanaged code so don't do much
@ -188,19 +210,7 @@ public class BSScene : PhysicsScene
} }
public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position, public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
Vector3 size, Quaternion rotation) // deprecated Vector3 size, Quaternion rotation, bool isPhysical, uint localID)
{
return null;
}
public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
Vector3 size, Quaternion rotation, bool isPhysical)
{
m_log.ErrorFormat("{0}: CALL TO AddPrimShape in BSScene. NOT IMPLEMENTED", LogHeader);
return null;
}
public override PhysicsActor AddPrimShape(uint localID, string primName, PrimitiveBaseShape pbs, Vector3 position,
Vector3 size, Quaternion rotation, bool isPhysical)
{ {
// m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName); // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName);
IMesh mesh = null; IMesh mesh = null;
@ -225,10 +235,8 @@ public class BSScene : PhysicsScene
{ {
int updatedEntityCount; int updatedEntityCount;
IntPtr updatedEntitiesPtr; IntPtr updatedEntitiesPtr;
IntPtr[] updatedEntities;
int collidersCount; int collidersCount;
IntPtr collidersPtr; IntPtr collidersPtr;
int[] colliders; // should be uint but Marshal.Copy does not have that overload
// update the prim states while we know the physics engine is not busy // update the prim states while we know the physics engine is not busy
ProcessTaints(); ProcessTaints();
@ -242,32 +250,33 @@ public class BSScene : PhysicsScene
int numSubSteps = BulletSimAPI.PhysicsStep(m_worldID, timeStep, m_maxSubSteps, m_fixedTimeStep, int numSubSteps = BulletSimAPI.PhysicsStep(m_worldID, timeStep, m_maxSubSteps, m_fixedTimeStep,
out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
// if there were collisions, they show up here // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in
// Get a value for 'now' so all the collision and update routines don't have to get their own
m_simulationNowTime = Util.EnvironmentTickCount();
// If there were collisions, process them by sending the event to the prim.
// Collisions must be processed before updates.
if (collidersCount > 0) if (collidersCount > 0)
{ {
colliders = new int[collidersCount]; for (int ii = 0; ii < collidersCount; ii++)
Marshal.Copy(collidersPtr, colliders, 0, collidersCount);
for (int ii = 0; ii < collidersCount; ii+=2)
{ {
uint cA = (uint)colliders[ii]; uint cA = m_collisionArray[ii].aID;
uint cB = (uint)colliders[ii+1]; uint cB = m_collisionArray[ii].bID;
SendCollision(cA, cB); Vector3 point = m_collisionArray[ii].point;
SendCollision(cB, cA); Vector3 normal = m_collisionArray[ii].normal;
SendCollision(cA, cB, point, normal, 0.01f);
SendCollision(cB, cA, point, -normal, 0.01f);
} }
} }
// if any of the objects had updated properties, they are returned in the updatedEntity structure // If any of the objects had updated properties, tell the object it has been changed by the physics engine
// TODO: figure out how to pass all of the EntityProperties structures in one marshal call.
if (updatedEntityCount > 0) if (updatedEntityCount > 0)
{ {
updatedEntities = new IntPtr[updatedEntityCount];
// fetch all the pointers to all the EntityProperties structures for these updates
Marshal.Copy(updatedEntitiesPtr, updatedEntities, 0, updatedEntityCount);
for (int ii = 0; ii < updatedEntityCount; ii++) for (int ii = 0; ii < updatedEntityCount; ii++)
{ {
IntPtr updatePointer = updatedEntities[ii]; EntityProperties entprop = m_updateArray[ii];
EntityProperties entprop = (EntityProperties)Marshal.PtrToStructure(updatePointer, typeof(EntityProperties)); // m_log.DebugFormat("{0}: entprop[{1}]: id={2}, pos={3}", LogHeader, ii, entprop.ID, entprop.Position);
// m_log.DebugFormat("{0}: entprop: id={1}, pos={2}", LogHeader, entprop.ID, entprop.Position);
BSCharacter actor; BSCharacter actor;
if (m_avatars.TryGetValue(entprop.ID, out actor)) if (m_avatars.TryGetValue(entprop.ID, out actor))
{ {
@ -282,12 +291,12 @@ public class BSScene : PhysicsScene
} }
} }
// fps calculation wrong. This calculation returns about 1 in normal operation. // fps calculation wrong. This calculation always returns about 1 in normal operation.
return timeStep / (numSubSteps * m_fixedTimeStep) * 1000f; return timeStep / (numSubSteps * m_fixedTimeStep) * 1000f;
} }
// Something has collided // Something has collided
private void SendCollision(uint localID, uint collidingWith) private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penitration)
{ {
if (localID == TERRAIN_ID || localID == GROUNDPLANE_ID) if (localID == TERRAIN_ID || localID == GROUNDPLANE_ID)
{ {
@ -303,12 +312,12 @@ public class BSScene : PhysicsScene
BSPrim prim; BSPrim prim;
if (m_prims.TryGetValue(localID, out prim)) { if (m_prims.TryGetValue(localID, out prim)) {
prim.Collide(collidingWith, type, Vector3.Zero, Vector3.UnitZ, 0.01f); prim.Collide(collidingWith, type, collidePoint, collideNormal, 0.01f);
return; return;
} }
BSCharacter actor; BSCharacter actor;
if (m_avatars.TryGetValue(localID, out actor)) { if (m_avatars.TryGetValue(localID, out actor)) {
actor.Collide(collidingWith, type, Vector3.Zero, Vector3.UnitZ, 0.01f); actor.Collide(collidingWith, type, collidePoint, collideNormal, 0.01f);
return; return;
} }
return; return;
@ -317,7 +326,6 @@ public class BSScene : PhysicsScene
public override void GetResults() { } public override void GetResults() { }
public override void SetTerrain(float[] heightMap) { public override void SetTerrain(float[] heightMap) {
m_log.DebugFormat("{0}: SetTerrain", LogHeader);
m_heightMap = heightMap; m_heightMap = heightMap;
this.TaintedObject(delegate() this.TaintedObject(delegate()
{ {

View File

@ -78,7 +78,13 @@ public struct RaycastHit
public float Fraction; public float Fraction;
public Vector3 Normal; public Vector3 Normal;
} }
public struct CollisionDesc
{
public uint aID;
public uint bID;
public Vector3 point;
public Vector3 normal;
}
public struct EntityProperties public struct EntityProperties
{ {
public uint ID; public uint ID;
@ -92,7 +98,8 @@ public struct EntityProperties
static class BulletSimAPI { static class BulletSimAPI {
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern uint Initialize(Vector3 maxPosition); public static extern uint Initialize(Vector3 maxPosition, int maxCollisions, IntPtr collisionArray,
int maxUpdates, IntPtr updateArray);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetHeightmap(uint worldID, [MarshalAs(UnmanagedType.LPArray)] float[] heightMap); public static extern void SetHeightmap(uint worldID, [MarshalAs(UnmanagedType.LPArray)] float[] heightMap);
@ -124,7 +131,12 @@ public static extern void CreateLinkset(uint worldID, int objectCount, ShapeData
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void AddConstraint(uint worldID, uint id1, uint id2, public static extern void AddConstraint(uint worldID, uint id1, uint id2,
Vector3 frame1, Vector3 frame2, Vector3 lowLinear, Vector3 hiLinear, Vector3 lowAngular, Vector3 hiAngular); Vector3 frame1, Quaternion frame1rot,
Vector3 frame2, Quaternion frame2rot,
Vector3 lowLinear, Vector3 hiLinear, Vector3 lowAngular, Vector3 hiAngular);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool RemoveConstraintByID(uint worldID, uint id1);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool RemoveConstraint(uint worldID, uint id1, uint id2); public static extern bool RemoveConstraint(uint worldID, uint id1, uint id2);

BIN
bin/BulletSim-x86_64.dll Executable file

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,6 @@
<configuration>
<dllmap os="windows" cpu="x86" dll="BulletSim" target="BulletSim" />
<dllmap os="windows" cpu="x86-64,ia64" dll="BulletSim" target="BulletSim-x86_64" />
<dllmap os="!windows,osx" cpu="x86" dll="BulletSim" target="libBulletSim" />
<dllmap os="!windows,osx" cpu="x86-64,ia64" dll="BulletSim" target="libBulletSim-x86_64" />
</configuration>

Binary file not shown.