BulletSim: Changes to terrain storage and management so mega-regions work.
Moved all terrain code out of BSScene and into new BSTerrainManager. Added logic to manage multiple terrains for mega-regions. Added new functions to BulletSimAPI to match the library. Moved all of the terrain creation and setup logic from C++ code to C# code. The unused code has not yet been removed from either place. Soon. Moved checks for avatar above ground and in bounds into BSCharacter.integration
parent
7b6987ce83
commit
7c140570db
|
@ -226,16 +226,37 @@ public class BSCharacter : BSPhysObject
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
// If below the ground, move the avatar up
|
// If below the ground, move the avatar up
|
||||||
float terrainHeight = Scene.GetTerrainHeightAtXYZ(_position);
|
float terrainHeight = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position);
|
||||||
if (_position.Z < terrainHeight)
|
if (Position.Z < terrainHeight)
|
||||||
{
|
{
|
||||||
DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},orient={2}", LocalID, _position, _orientation);
|
DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
|
||||||
_position.Z = terrainHeight + 2.0f;
|
Vector3 newPos = _position;
|
||||||
|
newPos.Z = terrainHeight + 2.0f;
|
||||||
|
_position = newPos;
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check for out of bounds
|
// TODO: check for out of bounds
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A version of the sanity check that also makes sure a new position value is
|
||||||
|
// pushed back to the physics engine. This routine would be used by anyone
|
||||||
|
// who is not already pushing the value.
|
||||||
|
private bool PositionSanityCheck2()
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (PositionSanityCheck())
|
||||||
|
{
|
||||||
|
// The new position value must be pushed into the physics engine but we can't
|
||||||
|
// just assign to "Position" because of potential call loops.
|
||||||
|
_scene.TaintedObject("BSCharacter.PositionSanityCheck", delegate()
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
|
||||||
|
BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation);
|
||||||
|
});
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,9 +521,13 @@ public class BSCharacter : BSPhysObject
|
||||||
// Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
|
// Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
|
||||||
// base.RequestPhysicsterseUpdate();
|
// base.RequestPhysicsterseUpdate();
|
||||||
|
|
||||||
DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
|
// Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
|
||||||
|
PositionSanityCheck2();
|
||||||
|
|
||||||
|
float heightHere = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position); // just for debug
|
||||||
|
DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5},terrain={6}",
|
||||||
LocalID, entprop.Position, entprop.Rotation, entprop.Velocity,
|
LocalID, entprop.Position, entprop.Rotation, entprop.Velocity,
|
||||||
entprop.Acceleration, entprop.RotationalVelocity);
|
entprop.Acceleration, entprop.RotationalVelocity, heightHere);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by the scene when a collision with this object is reported
|
// Called by the scene when a collision with this object is reported
|
||||||
|
|
|
@ -48,11 +48,10 @@ public abstract class BSConstraint : IDisposable
|
||||||
{
|
{
|
||||||
if (m_enabled)
|
if (m_enabled)
|
||||||
{
|
{
|
||||||
// BulletSimAPI.RemoveConstraint(m_world.ID, m_body1.ID, m_body2.ID);
|
m_enabled = false;
|
||||||
bool success = BulletSimAPI.DestroyConstraint2(m_world.Ptr, m_constraint.Ptr);
|
bool success = BulletSimAPI.DestroyConstraint2(m_world.Ptr, m_constraint.Ptr);
|
||||||
m_world.scene.DetailLog("{0},BSConstraint.Dispose,taint,body1={1},body2={2},success={3}", BSScene.DetailLogZero, m_body1.ID, m_body2.ID, success);
|
m_world.scene.DetailLog("{0},BSConstraint.Dispose,taint,body1={1},body2={2},success={3}", BSScene.DetailLogZero, m_body1.ID, m_body2.ID, success);
|
||||||
m_constraint.Ptr = System.IntPtr.Zero;
|
m_constraint.Ptr = System.IntPtr.Zero;
|
||||||
m_enabled = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -465,6 +465,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
}
|
}
|
||||||
}//end SetDefaultsForType
|
}//end SetDefaultsForType
|
||||||
|
|
||||||
|
// One step of the vehicle properties for the next 'pTimestep' seconds.
|
||||||
internal void Step(float pTimestep)
|
internal void Step(float pTimestep)
|
||||||
{
|
{
|
||||||
if (m_type == Vehicle.TYPE_NONE) return;
|
if (m_type == Vehicle.TYPE_NONE) return;
|
||||||
|
@ -592,9 +593,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
}
|
}
|
||||||
|
|
||||||
// If below the terrain, move us above the ground a little.
|
// If below the terrain, move us above the ground a little.
|
||||||
if (pos.Z < m_prim.Scene.GetTerrainHeightAtXYZ(pos))
|
if (pos.Z < m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos))
|
||||||
{
|
{
|
||||||
pos.Z = m_prim.Scene.GetTerrainHeightAtXYZ(pos) + 2;
|
pos.Z = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos) + 2;
|
||||||
m_prim.Position = pos;
|
m_prim.Position = pos;
|
||||||
VDetailLog("{0},MoveLinear,terrainHeight,pos={1}", m_prim.LocalID, pos);
|
VDetailLog("{0},MoveLinear,terrainHeight,pos={1}", m_prim.LocalID, pos);
|
||||||
}
|
}
|
||||||
|
@ -609,7 +610,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
}
|
}
|
||||||
if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
|
if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
|
||||||
{
|
{
|
||||||
m_VhoverTargetHeight = m_prim.Scene.GetTerrainHeightAtXY(pos.X, pos.Y) + m_VhoverHeight;
|
m_VhoverTargetHeight = m_prim.Scene.TerrainManager.GetTerrainHeightAtXY(pos.X, pos.Y) + m_VhoverHeight;
|
||||||
}
|
}
|
||||||
if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
|
if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
|
||||||
{
|
{
|
||||||
|
@ -673,7 +674,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
{
|
{
|
||||||
grav.Z = (float)(grav.Z * 1.125);
|
grav.Z = (float)(grav.Z * 1.125);
|
||||||
}
|
}
|
||||||
float terraintemp = m_prim.Scene.GetTerrainHeightAtXYZ(pos);
|
float terraintemp = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos);
|
||||||
float postemp = (pos.Z - terraintemp);
|
float postemp = (pos.Z - terraintemp);
|
||||||
if (postemp > 2.5f)
|
if (postemp > 2.5f)
|
||||||
{
|
{
|
||||||
|
|
|
@ -51,6 +51,8 @@ public abstract class BSPhysObject : PhysicsActor
|
||||||
public abstract BulletBody Body { get; set; }
|
public abstract BulletBody Body { get; set; }
|
||||||
public abstract void ZeroMotion();
|
public abstract void ZeroMotion();
|
||||||
|
|
||||||
|
public virtual void StepVehicle(float timeStep) { }
|
||||||
|
|
||||||
public abstract void UpdateProperties(EntityProperties entprop);
|
public abstract void UpdateProperties(EntityProperties entprop);
|
||||||
|
|
||||||
public abstract void Destroy();
|
public abstract void Destroy();
|
||||||
|
|
|
@ -377,7 +377,7 @@ public sealed class BSPrim : BSPhysObject
|
||||||
|
|
||||||
// Called each simulation step to advance vehicle characteristics.
|
// Called each simulation step to advance vehicle characteristics.
|
||||||
// Called from Scene when doing simulation step so we're in taint processing time.
|
// Called from Scene when doing simulation step so we're in taint processing time.
|
||||||
public void StepVehicle(float timeStep)
|
public override void StepVehicle(float timeStep)
|
||||||
{
|
{
|
||||||
if (IsPhysical)
|
if (IsPhysical)
|
||||||
_vehicle.Step(timeStep);
|
_vehicle.Step(timeStep);
|
||||||
|
|
|
@ -39,8 +39,6 @@ using log4net;
|
||||||
using OpenMetaverse;
|
using OpenMetaverse;
|
||||||
|
|
||||||
// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
|
// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
|
||||||
// Debug linkset
|
|
||||||
// Test with multiple regions in one simulator
|
|
||||||
// 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
|
||||||
|
@ -54,10 +52,8 @@ using OpenMetaverse;
|
||||||
// Use collision masks for collision with terrain and phantom objects
|
// Use collision masks for collision with terrain and phantom objects
|
||||||
// Check out llVolumeDetect. Must do something for that.
|
// Check out llVolumeDetect. Must do something for that.
|
||||||
// Should prim.link() and prim.delink() membership checking happen at taint time?
|
// Should prim.link() and prim.delink() membership checking happen at taint time?
|
||||||
// changing the position and orientation of a linked prim must rebuild the constraint with the root.
|
|
||||||
// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once
|
// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once
|
||||||
// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
|
// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
|
||||||
// Implement the genCollisions feature in BulletSim::SetObjectProperties (don't pass up unneeded collisions)
|
|
||||||
// Implement LockAngularMotion
|
// Implement LockAngularMotion
|
||||||
// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
|
// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
|
||||||
// Does NeedsMeshing() really need to exclude all the different shapes?
|
// Does NeedsMeshing() really need to exclude all the different shapes?
|
||||||
|
@ -85,18 +81,15 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
// moved to a better place.
|
// moved to a better place.
|
||||||
private HashSet<BSCharacter> m_avatarsWithCollisions = new HashSet<BSCharacter>();
|
private HashSet<BSCharacter> m_avatarsWithCollisions = new HashSet<BSCharacter>();
|
||||||
|
|
||||||
private List<BSPrim> m_vehicles = new List<BSPrim>();
|
// List of all the objects that have vehicle properties and should be called
|
||||||
|
// to update each physics step.
|
||||||
private float[] m_heightMap;
|
private List<BSPhysObject> m_vehicles = new List<BSPhysObject>();
|
||||||
private float m_waterLevel;
|
|
||||||
private uint m_worldID;
|
|
||||||
public uint WorldID { get { return m_worldID; } }
|
|
||||||
|
|
||||||
// let my minuions use my logger
|
// let my minuions use my logger
|
||||||
public ILog Logger { get { return m_log; } }
|
public ILog Logger { get { return m_log; } }
|
||||||
|
|
||||||
private bool m_initialized = false;
|
// If non-zero, the number of simulation steps between calls to the physics
|
||||||
|
// engine to output detailed physics stats. Debug logging level must be on also.
|
||||||
private int m_detailedStatsStep = 0;
|
private int m_detailedStatsStep = 0;
|
||||||
|
|
||||||
public IMesher mesher;
|
public IMesher mesher;
|
||||||
|
@ -106,29 +99,31 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
public float MeshMegaPrimThreshold { get; private set; }
|
public float MeshMegaPrimThreshold { get; private set; }
|
||||||
public float SculptLOD { get; private set; }
|
public float SculptLOD { get; private set; }
|
||||||
|
|
||||||
private BulletSim m_worldSim;
|
public uint WorldID { get; private set; }
|
||||||
public BulletSim World
|
public BulletSim World { get; private set; }
|
||||||
{
|
|
||||||
get { return m_worldSim; }
|
|
||||||
}
|
|
||||||
private BSConstraintCollection m_constraintCollection;
|
|
||||||
public BSConstraintCollection Constraints
|
|
||||||
{
|
|
||||||
get { return m_constraintCollection; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// All the constraints that have been allocated in this instance.
|
||||||
|
public BSConstraintCollection Constraints { get; private set; }
|
||||||
|
|
||||||
|
// Simulation parameters
|
||||||
private int m_maxSubSteps;
|
private int m_maxSubSteps;
|
||||||
private float m_fixedTimeStep;
|
private float m_fixedTimeStep;
|
||||||
private long m_simulationStep = 0;
|
private long m_simulationStep = 0;
|
||||||
public long SimulationStep { get { return m_simulationStep; } }
|
public long SimulationStep { get { return m_simulationStep; } }
|
||||||
|
|
||||||
|
// The length of the last timestep we were asked to simulate.
|
||||||
|
// This is used by the vehicle code. Since the vehicle code is called
|
||||||
|
// once per simulation step, its constants need to be scaled by this.
|
||||||
public float LastSimulatedTimestep { get; private set; }
|
public float LastSimulatedTimestep { get; private set; }
|
||||||
|
|
||||||
// A value of the time now so all the collision and update routines do not have to get their own
|
// 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
|
// Set to 'now' just before all the prims and actors are called for collisions and updates
|
||||||
private int m_simulationNowTime;
|
public int SimulationNowTime { get; private set; }
|
||||||
public int SimulationNowTime { get { return m_simulationNowTime; } }
|
|
||||||
|
|
||||||
|
// True if initialized and ready to do simulation steps
|
||||||
|
private bool m_initialized = false;
|
||||||
|
|
||||||
|
// Pinned memory used to pass step information between managed and unmanaged
|
||||||
private int m_maxCollisionsPerFrame;
|
private int m_maxCollisionsPerFrame;
|
||||||
private CollisionDesc[] m_collisionArray;
|
private CollisionDesc[] m_collisionArray;
|
||||||
private GCHandle m_collisionArrayPinnedHandle;
|
private GCHandle m_collisionArrayPinnedHandle;
|
||||||
|
@ -145,6 +140,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
|
|
||||||
public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero
|
public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero
|
||||||
public const uint GROUNDPLANE_ID = 1;
|
public const uint GROUNDPLANE_ID = 1;
|
||||||
|
public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
|
||||||
|
|
||||||
|
private float m_waterLevel;
|
||||||
|
public BSTerrainManager TerrainManager { get; private set; }
|
||||||
|
|
||||||
public ConfigurationParameters Params
|
public ConfigurationParameters Params
|
||||||
{
|
{
|
||||||
|
@ -155,12 +154,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
get { return new Vector3(0f, 0f, Params.gravity); }
|
get { return new Vector3(0f, 0f, Params.gravity); }
|
||||||
}
|
}
|
||||||
|
|
||||||
private float m_maximumObjectMass;
|
public float MaximumObjectMass { get; private set; }
|
||||||
public float MaximumObjectMass
|
|
||||||
{
|
|
||||||
get { return m_maximumObjectMass; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// When functions in the unmanaged code must be called, it is only
|
||||||
|
// done at a known time just before the simulation step. The taint
|
||||||
|
// system saves all these function calls and executes them in
|
||||||
|
// order before the simulation.
|
||||||
public delegate void TaintCallback();
|
public delegate void TaintCallback();
|
||||||
private struct TaintCallbackEntry
|
private struct TaintCallbackEntry
|
||||||
{
|
{
|
||||||
|
@ -176,6 +175,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
private Object _taintLock = new Object();
|
private Object _taintLock = new Object();
|
||||||
|
|
||||||
// A pointer to an instance if this structure is passed to the C++ code
|
// A pointer to an instance if this structure is passed to the C++ code
|
||||||
|
// Used to pass basic configuration values to the unmanaged code.
|
||||||
ConfigurationParameters[] m_params;
|
ConfigurationParameters[] m_params;
|
||||||
GCHandle m_paramsHandle;
|
GCHandle m_paramsHandle;
|
||||||
|
|
||||||
|
@ -190,10 +190,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
private string m_physicsLoggingDir;
|
private string m_physicsLoggingDir;
|
||||||
private string m_physicsLoggingPrefix;
|
private string m_physicsLoggingPrefix;
|
||||||
private int m_physicsLoggingFileMinutes;
|
private int m_physicsLoggingFileMinutes;
|
||||||
|
// 'true' of the vehicle code is to log lots of details
|
||||||
|
public bool VehicleLoggingEnabled { get; private set; }
|
||||||
|
|
||||||
private bool m_vehicleLoggingEnabled;
|
#region Construction and Initialization
|
||||||
public bool VehicleLoggingEnabled { get { return m_vehicleLoggingEnabled; } }
|
|
||||||
|
|
||||||
public BSScene(string identifier)
|
public BSScene(string identifier)
|
||||||
{
|
{
|
||||||
m_initialized = false;
|
m_initialized = false;
|
||||||
|
@ -216,6 +216,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
m_updateArray = new EntityProperties[m_maxUpdatesPerFrame];
|
m_updateArray = new EntityProperties[m_maxUpdatesPerFrame];
|
||||||
m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned);
|
m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned);
|
||||||
|
|
||||||
|
mesher = meshmerizer;
|
||||||
|
_taintedObjects = new List<TaintCallbackEntry>();
|
||||||
|
|
||||||
// Enable very detailed logging.
|
// Enable very detailed logging.
|
||||||
// By creating an empty logger when not logging, the log message invocation code
|
// By creating an empty logger when not logging, the log message invocation code
|
||||||
// can be left in and every call doesn't have to check for null.
|
// can be left in and every call doesn't have to check for null.
|
||||||
|
@ -228,11 +231,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
PhysicsLogging = new Logging.LogWriter();
|
PhysicsLogging = new Logging.LogWriter();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the version of the DLL
|
|
||||||
// TODO: this doesn't work yet. Something wrong with marshaling the returned string.
|
|
||||||
// BulletSimVersion = BulletSimAPI.GetVersion();
|
|
||||||
// m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion);
|
|
||||||
|
|
||||||
// If Debug logging level, enable logging from the unmanaged code
|
// If Debug logging level, enable logging from the unmanaged code
|
||||||
if (m_log.IsDebugEnabled || PhysicsLogging.Enabled)
|
if (m_log.IsDebugEnabled || PhysicsLogging.Enabled)
|
||||||
{
|
{
|
||||||
|
@ -245,22 +243,32 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
BulletSimAPI.SetDebugLogCallback(m_DebugLogCallbackHandle);
|
BulletSimAPI.SetDebugLogCallback(m_DebugLogCallbackHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
_taintedObjects = new List<TaintCallbackEntry>();
|
// Get the version of the DLL
|
||||||
|
// TODO: this doesn't work yet. Something wrong with marshaling the returned string.
|
||||||
|
// BulletSimVersion = BulletSimAPI.GetVersion();
|
||||||
|
// m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion);
|
||||||
|
|
||||||
mesher = meshmerizer;
|
// The bounding box for the simulated world. The origin is 0,0,0 unless we're
|
||||||
|
// a child in a mega-region.
|
||||||
// The bounding box for the simulated world
|
// Turns out that Bullet really doesn't care about the extents of the simulated
|
||||||
|
// area. It tracks active objects no matter where they are.
|
||||||
Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, 8192f);
|
Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, 8192f);
|
||||||
|
|
||||||
// m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
|
// m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
|
||||||
m_worldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(),
|
WorldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(),
|
||||||
m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(),
|
m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(),
|
||||||
m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject());
|
m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject());
|
||||||
|
|
||||||
// Initialization to support the transition to a new API which puts most of the logic
|
// Initialization to support the transition to a new API which puts most of the logic
|
||||||
// into the C# code so it is easier to modify and add to.
|
// into the C# code so it is easier to modify and add to.
|
||||||
m_worldSim = new BulletSim(m_worldID, this, BulletSimAPI.GetSimHandle2(m_worldID));
|
World = new BulletSim(WorldID, this, BulletSimAPI.GetSimHandle2(WorldID));
|
||||||
m_constraintCollection = new BSConstraintCollection(World);
|
|
||||||
|
Constraints = new BSConstraintCollection(World);
|
||||||
|
|
||||||
|
// Note: choose one of the two following lines
|
||||||
|
// BulletSimAPI.CreateInitialGroundPlaneAndTerrain(WorldID);
|
||||||
|
TerrainManager = new BSTerrainManager(this);
|
||||||
|
TerrainManager.CreateInitialGroundPlaneAndTerrain();
|
||||||
|
|
||||||
m_initialized = true;
|
m_initialized = true;
|
||||||
}
|
}
|
||||||
|
@ -288,7 +296,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
|
m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
|
||||||
m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
|
m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
|
||||||
// Very detailed logging for vehicle debugging
|
// Very detailed logging for vehicle debugging
|
||||||
m_vehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
|
VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
|
||||||
|
|
||||||
// Do any replacements in the parameters
|
// Do any replacements in the parameters
|
||||||
m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
|
m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
|
||||||
|
@ -323,6 +331,38 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
PhysicsLogging.Write("[BULLETS UNMANAGED]:" + msg);
|
PhysicsLogging.Write("[BULLETS UNMANAGED]:" + msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
// m_log.DebugFormat("{0}: Dispose()", LogHeader);
|
||||||
|
|
||||||
|
// make sure no stepping happens while we're deleting stuff
|
||||||
|
m_initialized = false;
|
||||||
|
|
||||||
|
TerrainManager.ReleaseGroundPlaneAndTerrain();
|
||||||
|
|
||||||
|
foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
|
||||||
|
{
|
||||||
|
kvp.Value.Destroy();
|
||||||
|
}
|
||||||
|
PhysObjects.Clear();
|
||||||
|
|
||||||
|
// Now that the prims are all cleaned up, there should be no constraints left
|
||||||
|
if (Constraints != null)
|
||||||
|
{
|
||||||
|
Constraints.Dispose();
|
||||||
|
Constraints = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anything left in the unmanaged code should be cleaned out
|
||||||
|
BulletSimAPI.Shutdown(WorldID);
|
||||||
|
|
||||||
|
// Not logging any more
|
||||||
|
PhysicsLogging.Close();
|
||||||
|
}
|
||||||
|
#endregion // Construction and Initialization
|
||||||
|
|
||||||
|
#region Prim and Avatar addition and removal
|
||||||
|
|
||||||
public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
|
public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
|
||||||
{
|
{
|
||||||
m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
|
m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
|
||||||
|
@ -413,6 +453,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
// information call is not needed.
|
// information call is not needed.
|
||||||
public override void AddPhysicsActorTaint(PhysicsActor prim) { }
|
public override void AddPhysicsActorTaint(PhysicsActor prim) { }
|
||||||
|
|
||||||
|
#endregion // Prim and Avatar addition and removal
|
||||||
|
|
||||||
|
#region Simulation
|
||||||
// Simulate one timestep
|
// Simulate one timestep
|
||||||
public override float Simulate(float timeStep)
|
public override float Simulate(float timeStep)
|
||||||
{
|
{
|
||||||
|
@ -429,6 +472,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
int simulateStartTime = Util.EnvironmentTickCount();
|
int simulateStartTime = Util.EnvironmentTickCount();
|
||||||
|
|
||||||
// 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
|
||||||
|
int numTaints = _taintedObjects.Count;
|
||||||
ProcessTaints();
|
ProcessTaints();
|
||||||
|
|
||||||
// Some of the prims operate with special vehicle properties
|
// Some of the prims operate with special vehicle properties
|
||||||
|
@ -440,14 +484,17 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
int numSubSteps = 0;
|
int numSubSteps = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
numSubSteps = BulletSimAPI.PhysicsStep(m_worldID, timeStep, m_maxSubSteps, m_fixedTimeStep,
|
numSubSteps = BulletSimAPI.PhysicsStep(WorldID, timeStep, m_maxSubSteps, m_fixedTimeStep,
|
||||||
out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
|
out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
|
||||||
DetailLog("{0},Simulate,call, substeps={1}, updates={2}, colliders={3}", DetailLogZero, numSubSteps, updatedEntityCount, collidersCount);
|
DetailLog("{0},Simulate,call, nTaints= {1}, substeps={2}, updates={3}, colliders={4}",
|
||||||
|
DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
m_log.WarnFormat("{0},PhysicsStep Exception: substeps={1}, updates={2}, colliders={3}, e={4}", LogHeader, numSubSteps, updatedEntityCount, collidersCount, e);
|
m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}",
|
||||||
// DetailLog("{0},PhysicsStepException,call, substeps={1}, updates={2}, colliders={3}", DetailLogZero, numSubSteps, updatedEntityCount, collidersCount);
|
LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e);
|
||||||
|
DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}",
|
||||||
|
DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
|
||||||
updatedEntityCount = 0;
|
updatedEntityCount = 0;
|
||||||
collidersCount = 0;
|
collidersCount = 0;
|
||||||
}
|
}
|
||||||
|
@ -456,7 +503,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
// Don't have to use the pointers passed back since we know it is the same pinned memory we passed in
|
// 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
|
// Get a value for 'now' so all the collision and update routines don't have to get their own
|
||||||
m_simulationNowTime = Util.EnvironmentTickCount();
|
SimulationNowTime = Util.EnvironmentTickCount();
|
||||||
|
|
||||||
// If there were collisions, process them by sending the event to the prim.
|
// If there were collisions, process them by sending the event to the prim.
|
||||||
// Collisions must be processed before updates.
|
// Collisions must be processed before updates.
|
||||||
|
@ -527,7 +574,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
// Something has collided
|
// Something has collided
|
||||||
private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration)
|
private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration)
|
||||||
{
|
{
|
||||||
if (localID == TERRAIN_ID || localID == GROUNDPLANE_ID)
|
if (localID <= TerrainManager.HighestTerrainID)
|
||||||
{
|
{
|
||||||
return; // don't send collisions to the terrain
|
return; // don't send collisions to the terrain
|
||||||
}
|
}
|
||||||
|
@ -539,7 +586,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
BSPhysObject collidee = null;
|
BSPhysObject collidee = null;
|
||||||
|
|
||||||
ActorTypes type = ActorTypes.Prim;
|
ActorTypes type = ActorTypes.Prim;
|
||||||
if (collidingWith == TERRAIN_ID || collidingWith == GROUNDPLANE_ID)
|
if (collidingWith <= TerrainManager.HighestTerrainID)
|
||||||
{
|
{
|
||||||
type = ActorTypes.Ground;
|
type = ActorTypes.Ground;
|
||||||
}
|
}
|
||||||
|
@ -558,28 +605,14 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion // Simulation
|
||||||
|
|
||||||
public override void GetResults() { }
|
public override void GetResults() { }
|
||||||
|
|
||||||
|
#region Terrain
|
||||||
|
|
||||||
public override void SetTerrain(float[] heightMap) {
|
public override void SetTerrain(float[] heightMap) {
|
||||||
m_heightMap = heightMap;
|
TerrainManager.SetTerrain(heightMap);
|
||||||
this.TaintedObject("BSScene.SetTerrain", delegate()
|
|
||||||
{
|
|
||||||
BulletSimAPI.SetHeightmap(m_worldID, m_heightMap);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Someday we will have complex terrain with caves and tunnels
|
|
||||||
// For the moment, it's flat and convex
|
|
||||||
public float GetTerrainHeightAtXYZ(Vector3 loc)
|
|
||||||
{
|
|
||||||
return GetTerrainHeightAtXY(loc.X, loc.Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float GetTerrainHeightAtXY(float tX, float tY)
|
|
||||||
{
|
|
||||||
if (tX < 0 || tX >= Constants.RegionSize || tY < 0 || tY >= Constants.RegionSize)
|
|
||||||
return 30;
|
|
||||||
return m_heightMap[((int)tX) * Constants.RegionSize + ((int)tY)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetWaterLevel(float baseheight)
|
public override void SetWaterLevel(float baseheight)
|
||||||
|
@ -597,33 +630,27 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
// m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
|
// m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Dispose()
|
// Although no one seems to check this, I do support combining.
|
||||||
|
public override bool SupportsCombining()
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("{0}: Dispose()", LogHeader);
|
return TerrainManager.SupportsCombining();
|
||||||
|
|
||||||
// make sure no stepping happens while we're deleting stuff
|
|
||||||
m_initialized = false;
|
|
||||||
|
|
||||||
foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
|
|
||||||
{
|
|
||||||
kvp.Value.Destroy();
|
|
||||||
}
|
}
|
||||||
PhysObjects.Clear();
|
// This call says I am a child to region zero in a mega-region. 'pScene' is that
|
||||||
|
// of region zero, 'offset' is my offset from regions zero's origin, and
|
||||||
// Now that the prims are all cleaned up, there should be no constraints left
|
// 'extents' is the largest XY that is handled in my region.
|
||||||
if (m_constraintCollection != null)
|
public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
|
||||||
{
|
{
|
||||||
m_constraintCollection.Dispose();
|
TerrainManager.Combine(pScene, offset, extents);
|
||||||
m_constraintCollection = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anything left in the unmanaged code should be cleaned out
|
// Unhook all the combining that I know about.
|
||||||
BulletSimAPI.Shutdown(WorldID);
|
public override void UnCombine(PhysicsScene pScene)
|
||||||
|
{
|
||||||
// Not logging any more
|
TerrainManager.UnCombine(pScene);
|
||||||
PhysicsLogging.Close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion // Terrain
|
||||||
|
|
||||||
public override Dictionary<uint, float> GetTopColliders()
|
public override Dictionary<uint, float> GetTopColliders()
|
||||||
{
|
{
|
||||||
return new Dictionary<uint, float>();
|
return new Dictionary<uint, float>();
|
||||||
|
@ -833,14 +860,14 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
// no locking because only called when physics engine is not busy
|
// no locking because only called when physics engine is not busy
|
||||||
private void ProcessVehicles(float timeStep)
|
private void ProcessVehicles(float timeStep)
|
||||||
{
|
{
|
||||||
foreach (BSPrim prim in m_vehicles)
|
foreach (BSPhysObject pobj in m_vehicles)
|
||||||
{
|
{
|
||||||
prim.StepVehicle(timeStep);
|
pobj.StepVehicle(timeStep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion Vehicles
|
#endregion Vehicles
|
||||||
|
|
||||||
#region Parameters
|
#region INI and command line parameter processing
|
||||||
|
|
||||||
delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
|
delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
|
||||||
delegate float ParamGet(BSScene scene);
|
delegate float ParamGet(BSScene scene);
|
||||||
|
@ -943,9 +970,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
(s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
|
(s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
|
||||||
new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)",
|
new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)",
|
||||||
10000.01f,
|
10000.01f,
|
||||||
(s,cf,p,v) => { s.m_maximumObjectMass = cf.GetFloat(p, v); },
|
(s,cf,p,v) => { s.MaximumObjectMass = cf.GetFloat(p, v); },
|
||||||
(s) => { return (float)s.m_maximumObjectMass; },
|
(s) => { return (float)s.MaximumObjectMass; },
|
||||||
(s,p,l,v) => { s.m_maximumObjectMass = v; } ),
|
(s,p,l,v) => { s.MaximumObjectMass = v; } ),
|
||||||
|
|
||||||
new ParameterDefn("PID_D", "Derivitive factor for motion smoothing",
|
new ParameterDefn("PID_D", "Derivitive factor for motion smoothing",
|
||||||
2200f,
|
2200f,
|
||||||
|
@ -1207,6 +1234,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
|
|
||||||
private PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1];
|
private PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1];
|
||||||
|
|
||||||
|
// This creates an array in the correct format for returning the list of
|
||||||
|
// parameters. This is used by the 'list' option of the 'physics' command.
|
||||||
private void BuildParameterTable()
|
private void BuildParameterTable()
|
||||||
{
|
{
|
||||||
if (SettableParameters.Length < ParameterDefinitions.Length)
|
if (SettableParameters.Length < ParameterDefinitions.Length)
|
||||||
|
@ -1283,7 +1312,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
TaintedObject("BSScene.UpdateParameterSet", delegate() {
|
TaintedObject("BSScene.UpdateParameterSet", delegate() {
|
||||||
foreach (uint lID in objectIDs)
|
foreach (uint lID in objectIDs)
|
||||||
{
|
{
|
||||||
BulletSimAPI.UpdateParameter(m_worldID, lID, xparm, xval);
|
BulletSimAPI.UpdateParameter(WorldID, lID, xparm, xval);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -1301,7 +1330,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
string xparm = parm.ToLower();
|
string xparm = parm.ToLower();
|
||||||
float xval = val;
|
float xval = val;
|
||||||
TaintedObject("BSScene.TaintedUpdateParameter", delegate() {
|
TaintedObject("BSScene.TaintedUpdateParameter", delegate() {
|
||||||
BulletSimAPI.UpdateParameter(m_worldID, xlocalID, xparm, xval);
|
BulletSimAPI.UpdateParameter(WorldID, xlocalID, xparm, xval);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,307 @@
|
||||||
|
/*
|
||||||
|
* 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 OpenSim.Framework;
|
||||||
|
using OpenSim.Region.Framework;
|
||||||
|
using OpenSim.Region.CoreModules;
|
||||||
|
using OpenSim.Region.Physics.Manager;
|
||||||
|
|
||||||
|
using Nini.Config;
|
||||||
|
using log4net;
|
||||||
|
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
|
{
|
||||||
|
public class BSTerrainManager
|
||||||
|
{
|
||||||
|
static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
|
||||||
|
|
||||||
|
BSScene m_physicsScene;
|
||||||
|
|
||||||
|
private BulletBody m_groundPlane;
|
||||||
|
|
||||||
|
// If doing mega-regions, if we're region zero we will be managing multiple
|
||||||
|
// region terrains since region zero does the physics for the whole mega-region.
|
||||||
|
private Dictionary<Vector2, BulletBody> m_terrains;
|
||||||
|
private Dictionary<Vector2, BulletHeightMapInfo> m_heightMaps;
|
||||||
|
|
||||||
|
// If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount.
|
||||||
|
// This is incremented before assigning to new region so it is the last ID allocated.
|
||||||
|
private uint m_terrainCount = BSScene.CHILDTERRAIN_ID - 1;
|
||||||
|
public uint HighestTerrainID { get {return m_terrainCount; } }
|
||||||
|
|
||||||
|
// If doing mega-regions, this holds our offset from region zero of
|
||||||
|
// the mega-regions. "parentScene" points to the PhysicsScene of region zero.
|
||||||
|
private Vector3 m_worldOffset = Vector3.Zero;
|
||||||
|
public Vector2 WorldExtents = new Vector2((int)Constants.RegionSize, (int)Constants.RegionSize);
|
||||||
|
private PhysicsScene m_parentScene = null;
|
||||||
|
|
||||||
|
public BSTerrainManager(BSScene physicsScene)
|
||||||
|
{
|
||||||
|
m_physicsScene = physicsScene;
|
||||||
|
m_terrains = new Dictionary<Vector2,BulletBody>();
|
||||||
|
m_heightMaps = new Dictionary<Vector2,BulletHeightMapInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the initial instance of terrain and the underlying ground plane.
|
||||||
|
// The objects are allocated in the unmanaged space and the pointers are tracked
|
||||||
|
// by the managed code.
|
||||||
|
// The terrains and the groundPlane are not added to the list of PhysObjects.
|
||||||
|
// This is called from the initialization routine so we presume it is
|
||||||
|
// safe to call Bullet in real time. We hope no one is moving around prim yet.
|
||||||
|
public void CreateInitialGroundPlaneAndTerrain()
|
||||||
|
{
|
||||||
|
// The ground plane is here to catch things that are trying to drop to negative infinity
|
||||||
|
m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
|
||||||
|
BulletSimAPI.CreateGroundPlaneBody2(BSScene.GROUNDPLANE_ID, 1f, 0.4f));
|
||||||
|
BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr);
|
||||||
|
|
||||||
|
Vector3 minTerrainCoords = new Vector3(0f, 0f, 24f);
|
||||||
|
Vector3 maxTerrainCoords = new Vector3(Constants.RegionSize, Constants.RegionSize, 25f);
|
||||||
|
int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y;
|
||||||
|
float[] initialMap = new float[totalHeights];
|
||||||
|
for (int ii = 0; ii < totalHeights; ii++)
|
||||||
|
{
|
||||||
|
initialMap[ii] = 25f;
|
||||||
|
}
|
||||||
|
CreateNewTerrainSegment(BSScene.TERRAIN_ID, initialMap, minTerrainCoords, maxTerrainCoords);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReleaseGroundPlaneAndTerrain()
|
||||||
|
{
|
||||||
|
if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr))
|
||||||
|
{
|
||||||
|
BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, m_groundPlane.Ptr);
|
||||||
|
}
|
||||||
|
m_groundPlane.Ptr = IntPtr.Zero;
|
||||||
|
|
||||||
|
foreach (KeyValuePair<Vector2, BulletBody> kvp in m_terrains)
|
||||||
|
{
|
||||||
|
if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, kvp.Value.Ptr))
|
||||||
|
{
|
||||||
|
BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, kvp.Value.Ptr);
|
||||||
|
BulletSimAPI.ReleaseHeightmapInfo2(m_heightMaps[kvp.Key].Ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_terrains.Clear();
|
||||||
|
m_heightMaps.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new terrain description. This is used for mega-regions where
|
||||||
|
// the children of region zero give region zero all of the terrain
|
||||||
|
// segments since region zero does all the physics for the mega-region.
|
||||||
|
// Call at taint time!!
|
||||||
|
public void CreateNewTerrainSegment(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords)
|
||||||
|
{
|
||||||
|
// The Z coordinates are recalculated to be the min and max height of the terrain
|
||||||
|
// itself. The caller may have passed us the real region extent.
|
||||||
|
float minZ = float.MaxValue;
|
||||||
|
float maxZ = float.MinValue;
|
||||||
|
int hSize = heightMap.Length;
|
||||||
|
for (int ii = 0; ii < hSize; ii++)
|
||||||
|
{
|
||||||
|
minZ = heightMap[ii] < minZ ? heightMap[ii] : minZ;
|
||||||
|
maxZ = heightMap[ii] > maxZ ? heightMap[ii] : maxZ;
|
||||||
|
}
|
||||||
|
minCoords.Z = minZ;
|
||||||
|
maxCoords.Z = maxZ;
|
||||||
|
// If the terrain is flat, make a difference so we get a good bounding box
|
||||||
|
if (minZ == maxZ)
|
||||||
|
minZ -= 0.2f;
|
||||||
|
Vector2 terrainRegionBase = new Vector2(minCoords.X, minCoords.Y);
|
||||||
|
|
||||||
|
// Create the heightmap data structure in the unmanaged space
|
||||||
|
BulletHeightMapInfo mapInfo = new BulletHeightMapInfo(
|
||||||
|
BulletSimAPI.CreateHeightmap2(minCoords, maxCoords, heightMap), heightMap);
|
||||||
|
mapInfo.terrainRegionBase = terrainRegionBase;
|
||||||
|
mapInfo.maxRegionExtent = maxCoords;
|
||||||
|
mapInfo.minZ = minZ;
|
||||||
|
mapInfo.maxZ = maxZ;
|
||||||
|
mapInfo.sizeX = maxCoords.X - minCoords.X;
|
||||||
|
mapInfo.sizeY = maxCoords.Y - minCoords.Y;
|
||||||
|
|
||||||
|
DetailLog("{0},BSScene.CreateNewTerrainSegment,call,minZ={1},maxZ={2},hMapPtr={3},minC={4},maxC={5}",
|
||||||
|
BSScene.DetailLogZero, minZ, maxZ, mapInfo.Ptr, minCoords, maxCoords);
|
||||||
|
// Create the terrain body from that heightmap
|
||||||
|
BulletBody terrainBody = new BulletBody(id, BulletSimAPI.CreateTerrainBody2(id, mapInfo.Ptr, 0.01f));
|
||||||
|
|
||||||
|
BulletSimAPI.SetFriction2(terrainBody.Ptr, m_physicsScene.Params.terrainFriction);
|
||||||
|
BulletSimAPI.SetHitFraction2(terrainBody.Ptr, m_physicsScene.Params.terrainHitFraction);
|
||||||
|
BulletSimAPI.SetRestitution2(terrainBody.Ptr, m_physicsScene.Params.terrainRestitution);
|
||||||
|
BulletSimAPI.SetCollisionFlags2(terrainBody.Ptr, CollisionFlags.CF_STATIC_OBJECT);
|
||||||
|
BulletSimAPI.Activate2(terrainBody.Ptr, true);
|
||||||
|
|
||||||
|
// Add the new terrain to the dynamics world
|
||||||
|
BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, terrainBody.Ptr);
|
||||||
|
BulletSimAPI.UpdateSingleAabb2(m_physicsScene.World.Ptr, terrainBody.Ptr);
|
||||||
|
|
||||||
|
|
||||||
|
// Add the created terrain to the management set. If we are doing mega-regions,
|
||||||
|
// the terrains of our children will be added.
|
||||||
|
m_terrains.Add(terrainRegionBase, terrainBody);
|
||||||
|
m_heightMaps.Add(terrainRegionBase, mapInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTerrain(float[] heightMap) {
|
||||||
|
if (m_worldOffset != Vector3.Zero && m_parentScene != null)
|
||||||
|
{
|
||||||
|
// If doing the mega-prim stuff and we are the child of the zero region,
|
||||||
|
// the terrain is really added to our parent
|
||||||
|
if (m_parentScene is BSScene)
|
||||||
|
{
|
||||||
|
((BSScene)m_parentScene).TerrainManager.SetTerrain(heightMap, m_worldOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if not doing the mega-prim thing, just change the terrain
|
||||||
|
SetTerrain(heightMap, m_worldOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetTerrain(float[] heightMap, Vector3 tOffset)
|
||||||
|
{
|
||||||
|
float minZ = float.MaxValue;
|
||||||
|
float maxZ = float.MinValue;
|
||||||
|
|
||||||
|
// Copy heightMap local and compute some statistics.
|
||||||
|
// Not really sure if we need to do this deep copy but, given
|
||||||
|
// the magic that happens to make the closure for taint
|
||||||
|
// below, I don't want there to be any problem with sharing
|
||||||
|
// locations of there are multiple calls to this routine
|
||||||
|
// within one tick.
|
||||||
|
int heightMapSize = heightMap.Length;
|
||||||
|
float[] localHeightMap = new float[heightMapSize];
|
||||||
|
for (int ii = 0; ii < heightMapSize; ii++)
|
||||||
|
{
|
||||||
|
float height = heightMap[ii];
|
||||||
|
if (height < minZ) minZ = height;
|
||||||
|
if (height > maxZ) maxZ = height;
|
||||||
|
localHeightMap[ii] = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 terrainRegionBase = new Vector2(tOffset.X, tOffset.Y);
|
||||||
|
BulletHeightMapInfo mapInfo;
|
||||||
|
if (m_heightMaps.TryGetValue(terrainRegionBase, out mapInfo))
|
||||||
|
{
|
||||||
|
// If this is terrain we know about, it's easy to update
|
||||||
|
mapInfo.heightMap = localHeightMap;
|
||||||
|
m_physicsScene.TaintedObject("BSScene.SetTerrain:UpdateExisting", delegate()
|
||||||
|
{
|
||||||
|
DetailLog("{0},SetTerrain:UpdateExisting,baseX={1},baseY={2},minZ={3},maxZ={4}",
|
||||||
|
BSScene.DetailLogZero, tOffset.X, tOffset.Y, minZ, maxZ);
|
||||||
|
BulletSimAPI.UpdateHeightMap2(m_physicsScene.World.Ptr, mapInfo.Ptr, mapInfo.heightMap);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Our mega-prim child is giving us a new terrain to add to the phys world
|
||||||
|
uint newTerrainID = ++m_terrainCount;
|
||||||
|
|
||||||
|
Vector3 minCoords = tOffset;
|
||||||
|
minCoords.Z = minZ;
|
||||||
|
Vector3 maxCoords = new Vector3(tOffset.X + Constants.RegionSize,
|
||||||
|
tOffset.Y + Constants.RegionSize,
|
||||||
|
maxZ);
|
||||||
|
m_physicsScene.TaintedObject("BSScene.SetTerrain:NewTerrain", delegate()
|
||||||
|
{
|
||||||
|
DetailLog("{0},SetTerrain:NewTerrain,baseX={1},baseY={2}", BSScene.DetailLogZero, tOffset.X, tOffset.Y);
|
||||||
|
CreateNewTerrainSegment(newTerrainID, heightMap, minCoords, maxCoords);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Someday we will have complex terrain with caves and tunnels
|
||||||
|
// For the moment, it's flat and convex
|
||||||
|
public float GetTerrainHeightAtXYZ(Vector3 loc)
|
||||||
|
{
|
||||||
|
return GetTerrainHeightAtXY(loc.X, loc.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given an X and Y, find the height of the terrain.
|
||||||
|
// Since we could be handling multiple terrains for a mega-region,
|
||||||
|
// the base of the region is calcuated assuming all regions are
|
||||||
|
// the same size and that is the default.
|
||||||
|
// Once the heightMapInfo is found, we have all the information to
|
||||||
|
// compute the offset into the array.
|
||||||
|
public float GetTerrainHeightAtXY(float tX, float tY)
|
||||||
|
{
|
||||||
|
float ret = 30f;
|
||||||
|
|
||||||
|
int offsetX = ((int)(tX / (int)Constants.RegionSize)) * (int)Constants.RegionSize;
|
||||||
|
int offsetY = ((int)(tY / (int)Constants.RegionSize)) * (int)Constants.RegionSize;
|
||||||
|
Vector2 terrainBaseXY = new Vector2(offsetX, offsetY);
|
||||||
|
|
||||||
|
BulletHeightMapInfo mapInfo;
|
||||||
|
if (m_heightMaps.TryGetValue(terrainBaseXY, out mapInfo))
|
||||||
|
{
|
||||||
|
float regionX = tX - offsetX;
|
||||||
|
float regionY = tY - offsetY;
|
||||||
|
regionX = regionX > mapInfo.sizeX ? 0 : regionX;
|
||||||
|
regionY = regionY > mapInfo.sizeY ? 0 : regionY;
|
||||||
|
ret = mapInfo.heightMap[(int)(regionX * mapInfo.sizeX + regionY)];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: x={1}, y={2}",
|
||||||
|
LogHeader, tX, tY);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Although no one seems to check this, I do support combining.
|
||||||
|
public bool SupportsCombining()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// This call says I am a child to region zero in a mega-region. 'pScene' is that
|
||||||
|
// of region zero, 'offset' is my offset from regions zero's origin, and
|
||||||
|
// 'extents' is the largest XY that is handled in my region.
|
||||||
|
public void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
|
||||||
|
{
|
||||||
|
m_worldOffset = offset;
|
||||||
|
WorldExtents = new Vector2(extents.X, extents.Y);
|
||||||
|
m_parentScene = pScene;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unhook all the combining that I know about.
|
||||||
|
public void UnCombine(PhysicsScene pScene)
|
||||||
|
{
|
||||||
|
// Just like ODE, for the moment a NOP
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void DetailLog(string msg, params Object[] args)
|
||||||
|
{
|
||||||
|
m_physicsScene.PhysicsLogging.Write(msg, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,9 @@ using OpenMetaverse;
|
||||||
namespace OpenSim.Region.Physics.BulletSPlugin {
|
namespace OpenSim.Region.Physics.BulletSPlugin {
|
||||||
|
|
||||||
// Classes to allow some type checking for the API
|
// Classes to allow some type checking for the API
|
||||||
|
// These hold pointers to allocated objects in the unmanaged space.
|
||||||
|
|
||||||
|
// The physics engine controller class created at initialization
|
||||||
public struct BulletSim
|
public struct BulletSim
|
||||||
{
|
{
|
||||||
public BulletSim(uint id, BSScene bss, IntPtr xx) { ID = id; scene = bss; Ptr = xx; }
|
public BulletSim(uint id, BSScene bss, IntPtr xx) { ID = id; scene = bss; Ptr = xx; }
|
||||||
|
@ -42,6 +45,7 @@ public struct BulletSim
|
||||||
public IntPtr Ptr;
|
public IntPtr Ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An allocated Bullet btRigidBody
|
||||||
public struct BulletBody
|
public struct BulletBody
|
||||||
{
|
{
|
||||||
public BulletBody(uint id, IntPtr xx) { ID = id; Ptr = xx; }
|
public BulletBody(uint id, IntPtr xx) { ID = id; Ptr = xx; }
|
||||||
|
@ -49,12 +53,35 @@ public struct BulletBody
|
||||||
public uint ID;
|
public uint ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An allocated Bullet btConstraint
|
||||||
public struct BulletConstraint
|
public struct BulletConstraint
|
||||||
{
|
{
|
||||||
public BulletConstraint(IntPtr xx) { Ptr = xx; }
|
public BulletConstraint(IntPtr xx) { Ptr = xx; }
|
||||||
public IntPtr Ptr;
|
public IntPtr Ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An allocated HeightMapThing which hold various heightmap info
|
||||||
|
// Made a class rather than a struct so there would be only one
|
||||||
|
// instance of this and C# will pass around pointers rather
|
||||||
|
// than making copies.
|
||||||
|
public class BulletHeightMapInfo
|
||||||
|
{
|
||||||
|
public BulletHeightMapInfo(IntPtr xx, float[] hm) {
|
||||||
|
Ptr = xx;
|
||||||
|
heightMap = hm;
|
||||||
|
terrainRegionBase = new Vector2(0f, 0f);
|
||||||
|
maxRegionExtent = new Vector3(100f, 100f, 25f);
|
||||||
|
minZ = maxZ = 0f;
|
||||||
|
sizeX = sizeY = 256f;
|
||||||
|
}
|
||||||
|
public IntPtr Ptr;
|
||||||
|
public float[] heightMap;
|
||||||
|
public Vector2 terrainRegionBase;
|
||||||
|
public Vector3 maxRegionExtent;
|
||||||
|
public float sizeX, sizeY;
|
||||||
|
public float minZ, maxZ;
|
||||||
|
}
|
||||||
|
|
||||||
// ===============================================================================
|
// ===============================================================================
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public struct ConvexHull
|
public struct ConvexHull
|
||||||
|
@ -230,6 +257,9 @@ public static extern uint Initialize(Vector3 maxPosition, IntPtr parms,
|
||||||
int maxCollisions, IntPtr collisionArray,
|
int maxCollisions, IntPtr collisionArray,
|
||||||
int maxUpdates, IntPtr updateArray);
|
int maxUpdates, IntPtr updateArray);
|
||||||
|
|
||||||
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
|
public static extern void CreateInitialGroundPlaneAndTerrain(uint worldID);
|
||||||
|
|
||||||
[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);
|
||||||
|
|
||||||
|
@ -414,11 +444,22 @@ public static extern bool DeleteCollisionShape2(IntPtr world, IntPtr shape);
|
||||||
|
|
||||||
// =====================================================================================
|
// =====================================================================================
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern IntPtr CreateGroundPlaneBody2(uint id, Vector3 center, float collisionMargin);
|
public static extern IntPtr CreateGroundPlaneBody2(uint id, float height, float collisionMargin);
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern IntPtr CreateTerrainBody2(uint id,
|
public static extern IntPtr CreateTerrainBody2(uint id,
|
||||||
Vector3 minCoords, Vector3 maxCoords, float collisionMargin,
|
IntPtr heightMapInfo,
|
||||||
|
float collisionMargin);
|
||||||
|
|
||||||
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
|
public static extern IntPtr CreateHeightmap2(Vector3 minCoords, Vector3 maxCoords,
|
||||||
|
[MarshalAs(UnmanagedType.LPArray)] float[] heightMap);
|
||||||
|
|
||||||
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
|
public static extern bool ReleaseHeightmapInfo2(IntPtr heightMapInfo);
|
||||||
|
|
||||||
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
|
public static extern void UpdateHeightMap2(IntPtr world, IntPtr heightMapInfo,
|
||||||
[MarshalAs(UnmanagedType.LPArray)] float[] heightMap);
|
[MarshalAs(UnmanagedType.LPArray)] float[] heightMap);
|
||||||
|
|
||||||
// =====================================================================================
|
// =====================================================================================
|
||||||
|
@ -473,11 +514,16 @@ public static extern bool SetConstraintParam2(IntPtr constrain, ConstraintParams
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern bool DestroyConstraint2(IntPtr world, IntPtr constrain);
|
public static extern bool DestroyConstraint2(IntPtr world, IntPtr constrain);
|
||||||
|
|
||||||
|
// =====================================================================================
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern Vector3 AddObjectToWorld2(IntPtr world, IntPtr obj);
|
public static extern bool AddObjectToWorld2(IntPtr world, IntPtr obj);
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern Vector3 RemoveObjectFromWorld2(IntPtr world, IntPtr obj);
|
public static extern bool RemoveObjectFromWorld2(IntPtr world, IntPtr obj);
|
||||||
|
|
||||||
|
// =====================================================================================
|
||||||
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
|
public static extern void Activate2(IntPtr obj, bool forceActivation);
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern Vector3 GetPosition2(IntPtr obj);
|
public static extern Vector3 GetPosition2(IntPtr obj);
|
||||||
|
@ -521,6 +567,9 @@ public static extern bool SetContactProcessingThreshold2(IntPtr obj, float val);
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern bool SetFriction2(IntPtr obj, float val);
|
public static extern bool SetFriction2(IntPtr obj, float val);
|
||||||
|
|
||||||
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
|
public static extern bool SetHitFraction2(IntPtr obj, float val);
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern bool SetRestitution2(IntPtr obj, float val);
|
public static extern bool SetRestitution2(IntPtr obj, float val);
|
||||||
|
|
||||||
|
@ -564,7 +613,7 @@ public static extern bool SetMargin2(IntPtr obj, float val);
|
||||||
public static extern bool UpdateSingleAabb2(IntPtr world, IntPtr obj);
|
public static extern bool UpdateSingleAabb2(IntPtr world, IntPtr obj);
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern bool DestroyObject2(IntPtr world, uint id);
|
public static extern bool DestroyObject2(IntPtr world, IntPtr obj);
|
||||||
|
|
||||||
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
|
||||||
public static extern void DumpPhysicsStatistics2(IntPtr sim);
|
public static extern void DumpPhysicsStatistics2(IntPtr sim);
|
||||||
|
|
Loading…
Reference in New Issue