diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | 948 |
1 files changed, 204 insertions, 744 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 27a78d1..8075b73 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | |||
@@ -26,6 +26,7 @@ | |||
26 | */ | 26 | */ |
27 | using System; | 27 | using System; |
28 | using System.Collections.Generic; | 28 | using System.Collections.Generic; |
29 | using System.Reflection; | ||
29 | using System.Runtime.InteropServices; | 30 | using System.Runtime.InteropServices; |
30 | using System.Text; | 31 | using System.Text; |
31 | using System.Threading; | 32 | using System.Threading; |
@@ -38,40 +39,22 @@ using Nini.Config; | |||
38 | using log4net; | 39 | using log4net; |
39 | using OpenMetaverse; | 40 | using OpenMetaverse; |
40 | 41 | ||
41 | // TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) | ||
42 | // Test sculpties (verified that they don't work) | ||
43 | // Compute physics FPS reasonably | ||
44 | // Based on material, set density and friction | ||
45 | // Don't use constraints in linksets of non-physical objects. Means having to move children manually. | ||
46 | // Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly? | ||
47 | // In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground) | ||
48 | // At the moment, physical and phantom causes object to drop through the terrain | ||
49 | // Physical phantom objects and related typing (collision options ) | ||
50 | // Check out llVolumeDetect. Must do something for that. | ||
51 | // Use collision masks for collision with terrain and phantom objects | ||
52 | // More efficient memory usage when passing hull information from BSPrim to BulletSim | ||
53 | // Should prim.link() and prim.delink() membership checking happen at taint time? | ||
54 | // Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once. | ||
55 | // Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect | ||
56 | // Implement LockAngularMotion | ||
57 | // Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation) | ||
58 | // Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet. | ||
59 | // Add PID movement operations. What does ScenePresence.MoveToTarget do? | ||
60 | // Check terrain size. 128 or 127? | ||
61 | // Raycast | ||
62 | // | ||
63 | namespace OpenSim.Region.Physics.BulletSPlugin | 42 | namespace OpenSim.Region.Physics.BulletSPlugin |
64 | { | 43 | { |
65 | public sealed class BSScene : PhysicsScene, IPhysicsParameters | 44 | public sealed class BSScene : PhysicsScene, IPhysicsParameters |
66 | { | 45 | { |
67 | private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | 46 | internal static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); |
68 | private static readonly string LogHeader = "[BULLETS SCENE]"; | 47 | internal static readonly string LogHeader = "[BULLETS SCENE]"; |
69 | 48 | ||
70 | // The name of the region we're working for. | 49 | // The name of the region we're working for. |
71 | public string RegionName { get; private set; } | 50 | public string RegionName { get; private set; } |
72 | 51 | ||
73 | public string BulletSimVersion = "?"; | 52 | public string BulletSimVersion = "?"; |
74 | 53 | ||
54 | // The handle to the underlying managed or unmanaged version of Bullet being used. | ||
55 | public string BulletEngineName { get; private set; } | ||
56 | public BSAPITemplate PE; | ||
57 | |||
75 | public Dictionary<uint, BSPhysObject> PhysObjects; | 58 | public Dictionary<uint, BSPhysObject> PhysObjects; |
76 | public BSShapeCollection Shapes; | 59 | public BSShapeCollection Shapes; |
77 | 60 | ||
@@ -82,32 +65,29 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
82 | // every tick so OpenSim will update its animation. | 65 | // every tick so OpenSim will update its animation. |
83 | private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>(); | 66 | private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>(); |
84 | 67 | ||
85 | // List of all the objects that have vehicle properties and should be called | ||
86 | // to update each physics step. | ||
87 | private List<BSPhysObject> m_vehicles = new List<BSPhysObject>(); | ||
88 | |||
89 | // let my minuions use my logger | 68 | // let my minuions use my logger |
90 | public ILog Logger { get { return m_log; } } | 69 | public ILog Logger { get { return m_log; } } |
91 | 70 | ||
92 | public IMesher mesher; | 71 | public IMesher mesher; |
93 | // Level of Detail values kept as float because that's what the Meshmerizer wants | ||
94 | public float MeshLOD { get; private set; } | ||
95 | public float MeshMegaPrimLOD { get; private set; } | ||
96 | public float MeshMegaPrimThreshold { get; private set; } | ||
97 | public float SculptLOD { get; private set; } | ||
98 | |||
99 | public uint WorldID { get; private set; } | 72 | public uint WorldID { get; private set; } |
100 | public BulletSim World { get; private set; } | 73 | public BulletWorld World { get; private set; } |
101 | 74 | ||
102 | // All the constraints that have been allocated in this instance. | 75 | // All the constraints that have been allocated in this instance. |
103 | public BSConstraintCollection Constraints { get; private set; } | 76 | public BSConstraintCollection Constraints { get; private set; } |
104 | 77 | ||
105 | // Simulation parameters | 78 | // Simulation parameters |
106 | private int m_maxSubSteps; | 79 | internal int m_maxSubSteps; |
107 | private float m_fixedTimeStep; | 80 | internal float m_fixedTimeStep; |
108 | private long m_simulationStep = 0; | 81 | internal long m_simulationStep = 0; |
82 | internal float NominalFrameRate { get; set; } | ||
109 | public long SimulationStep { get { return m_simulationStep; } } | 83 | public long SimulationStep { get { return m_simulationStep; } } |
110 | private int m_taintsToProcessPerStep; | 84 | internal float LastTimeStep { get; private set; } |
85 | |||
86 | // Physical objects can register for prestep or poststep events | ||
87 | public delegate void PreStepAction(float timeStep); | ||
88 | public delegate void PostStepAction(float timeStep); | ||
89 | public event PreStepAction BeforeStep; | ||
90 | public event PreStepAction AfterStep; | ||
111 | 91 | ||
112 | // A value of the time now so all the collision and update routines do not have to get their own | 92 | // A value of the time now so all the collision and update routines do not have to get their own |
113 | // Set to 'now' just before all the prims and actors are called for collisions and updates | 93 | // Set to 'now' just before all the prims and actors are called for collisions and updates |
@@ -121,31 +101,22 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
121 | public bool InTaintTime { get; private set; } | 101 | public bool InTaintTime { get; private set; } |
122 | 102 | ||
123 | // Pinned memory used to pass step information between managed and unmanaged | 103 | // Pinned memory used to pass step information between managed and unmanaged |
124 | private int m_maxCollisionsPerFrame; | 104 | internal int m_maxCollisionsPerFrame; |
125 | private CollisionDesc[] m_collisionArray; | 105 | internal CollisionDesc[] m_collisionArray; |
126 | private GCHandle m_collisionArrayPinnedHandle; | ||
127 | 106 | ||
128 | private int m_maxUpdatesPerFrame; | 107 | internal int m_maxUpdatesPerFrame; |
129 | private EntityProperties[] m_updateArray; | 108 | internal EntityProperties[] m_updateArray; |
130 | private GCHandle m_updateArrayPinnedHandle; | ||
131 | |||
132 | public bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed | ||
133 | public bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes | ||
134 | public bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects | ||
135 | |||
136 | public float PID_D { get; private set; } // derivative | ||
137 | public float PID_P { get; private set; } // proportional | ||
138 | 109 | ||
139 | public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero | 110 | public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero |
140 | public const uint GROUNDPLANE_ID = 1; | 111 | public const uint GROUNDPLANE_ID = 1; |
141 | public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here | 112 | public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here |
142 | 113 | ||
143 | private float m_waterLevel; | 114 | public float SimpleWaterLevel { get; set; } |
144 | public BSTerrainManager TerrainManager { get; private set; } | 115 | public BSTerrainManager TerrainManager { get; private set; } |
145 | 116 | ||
146 | public ConfigurationParameters Params | 117 | public ConfigurationParameters Params |
147 | { | 118 | { |
148 | get { return m_params[0]; } | 119 | get { return UnmanagedParams[0]; } |
149 | } | 120 | } |
150 | public Vector3 DefaultGravity | 121 | public Vector3 DefaultGravity |
151 | { | 122 | { |
@@ -157,8 +128,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
157 | get { return Params.gravity; } | 128 | get { return Params.gravity; } |
158 | } | 129 | } |
159 | 130 | ||
160 | public float MaximumObjectMass { get; private set; } | ||
161 | |||
162 | // When functions in the unmanaged code must be called, it is only | 131 | // When functions in the unmanaged code must be called, it is only |
163 | // done at a known time just before the simulation step. The taint | 132 | // done at a known time just before the simulation step. The taint |
164 | // system saves all these function calls and executes them in | 133 | // system saves all these function calls and executes them in |
@@ -181,13 +150,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
181 | 150 | ||
182 | // A pointer to an instance if this structure is passed to the C++ code | 151 | // A pointer to an instance if this structure is passed to the C++ code |
183 | // Used to pass basic configuration values to the unmanaged code. | 152 | // Used to pass basic configuration values to the unmanaged code. |
184 | ConfigurationParameters[] m_params; | 153 | internal ConfigurationParameters[] UnmanagedParams; |
185 | GCHandle m_paramsHandle; | ||
186 | |||
187 | // Handle to the callback used by the unmanaged code to call into the managed code. | ||
188 | // Used for debug logging. | ||
189 | // Need to store the handle in a persistant variable so it won't be freed. | ||
190 | private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle; | ||
191 | 154 | ||
192 | // Sometimes you just have to log everything. | 155 | // Sometimes you just have to log everything. |
193 | public Logging.LogWriter PhysicsLogging; | 156 | public Logging.LogWriter PhysicsLogging; |
@@ -195,15 +158,24 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
195 | private string m_physicsLoggingDir; | 158 | private string m_physicsLoggingDir; |
196 | private string m_physicsLoggingPrefix; | 159 | private string m_physicsLoggingPrefix; |
197 | private int m_physicsLoggingFileMinutes; | 160 | private int m_physicsLoggingFileMinutes; |
161 | private bool m_physicsLoggingDoFlush; | ||
162 | private bool m_physicsPhysicalDumpEnabled; | ||
163 | public float PhysicsMetricDumpFrames { get; set; } | ||
198 | // 'true' of the vehicle code is to log lots of details | 164 | // 'true' of the vehicle code is to log lots of details |
199 | public bool VehicleLoggingEnabled { get; private set; } | 165 | public bool VehicleLoggingEnabled { get; private set; } |
166 | public bool VehiclePhysicalLoggingEnabled { get; private set; } | ||
200 | 167 | ||
201 | #region Construction and Initialization | 168 | #region Construction and Initialization |
202 | public BSScene(string identifier) | 169 | public BSScene(string engineType, string identifier) |
203 | { | 170 | { |
204 | m_initialized = false; | 171 | m_initialized = false; |
205 | // we are passed the name of the region we're working for. | 172 | |
173 | // The name of the region we're working for is passed to us. Keep for identification. | ||
206 | RegionName = identifier; | 174 | RegionName = identifier; |
175 | |||
176 | // Set identifying variables in the PhysicsScene interface. | ||
177 | EngineType = engineType; | ||
178 | Name = EngineType + "/" + RegionName; | ||
207 | } | 179 | } |
208 | 180 | ||
209 | public override void Initialise(IMesher meshmerizer, IConfigSource config) | 181 | public override void Initialise(IMesher meshmerizer, IConfigSource config) |
@@ -216,17 +188,13 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
216 | Shapes = new BSShapeCollection(this); | 188 | Shapes = new BSShapeCollection(this); |
217 | 189 | ||
218 | // Allocate pinned memory to pass parameters. | 190 | // Allocate pinned memory to pass parameters. |
219 | m_params = new ConfigurationParameters[1]; | 191 | UnmanagedParams = new ConfigurationParameters[1]; |
220 | m_paramsHandle = GCHandle.Alloc(m_params, GCHandleType.Pinned); | ||
221 | 192 | ||
222 | // Set default values for physics parameters plus any overrides from the ini file | 193 | // Set default values for physics parameters plus any overrides from the ini file |
223 | GetInitialParameterValues(config); | 194 | GetInitialParameterValues(config); |
224 | 195 | ||
225 | // allocate more pinned memory close to the above in an attempt to get the memory all together | 196 | // Get the connection to the physics engine (could be native or one of many DLLs) |
226 | m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame]; | 197 | PE = SelectUnderlyingBulletEngine(BulletEngineName); |
227 | m_collisionArrayPinnedHandle = GCHandle.Alloc(m_collisionArray, GCHandleType.Pinned); | ||
228 | m_updateArray = new EntityProperties[m_maxUpdatesPerFrame]; | ||
229 | m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned); | ||
230 | 198 | ||
231 | // Enable very detailed logging. | 199 | // Enable very detailed logging. |
232 | // By creating an empty logger when not logging, the log message invocation code | 200 | // By creating an empty logger when not logging, the log message invocation code |
@@ -234,28 +202,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
234 | if (m_physicsLoggingEnabled) | 202 | if (m_physicsLoggingEnabled) |
235 | { | 203 | { |
236 | PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); | 204 | PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); |
205 | PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output error messages. | ||
237 | } | 206 | } |
238 | else | 207 | else |
239 | { | 208 | { |
240 | PhysicsLogging = new Logging.LogWriter(); | 209 | PhysicsLogging = new Logging.LogWriter(); |
241 | } | 210 | } |
242 | 211 | ||
243 | // If Debug logging level, enable logging from the unmanaged code | 212 | // Allocate memory for returning of the updates and collisions from the physics engine |
244 | m_DebugLogCallbackHandle = null; | 213 | m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame]; |
245 | if (m_log.IsDebugEnabled || PhysicsLogging.Enabled) | 214 | m_updateArray = new EntityProperties[m_maxUpdatesPerFrame]; |
246 | { | ||
247 | m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader); | ||
248 | if (PhysicsLogging.Enabled) | ||
249 | // The handle is saved in a variable to make sure it doesn't get freed after this call | ||
250 | m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLoggerPhysLog); | ||
251 | else | ||
252 | m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger); | ||
253 | } | ||
254 | |||
255 | // Get the version of the DLL | ||
256 | // TODO: this doesn't work yet. Something wrong with marshaling the returned string. | ||
257 | // BulletSimVersion = BulletSimAPI.GetVersion(); | ||
258 | // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion); | ||
259 | 215 | ||
260 | // The bounding box for the simulated world. The origin is 0,0,0 unless we're | 216 | // The bounding box for the simulated world. The origin is 0,0,0 unless we're |
261 | // a child in a mega-region. | 217 | // a child in a mega-region. |
@@ -263,18 +219,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
263 | // area. It tracks active objects no matter where they are. | 219 | // area. It tracks active objects no matter where they are. |
264 | Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); | 220 | Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); |
265 | 221 | ||
266 | // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); | 222 | World = PE.Initialize(worldExtent, Params, m_maxCollisionsPerFrame, ref m_collisionArray, m_maxUpdatesPerFrame, ref m_updateArray); |
267 | World = new BulletSim(0, this, BulletSimAPI.Initialize2(worldExtent, m_paramsHandle.AddrOfPinnedObject(), | ||
268 | m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(), | ||
269 | m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject(), | ||
270 | m_DebugLogCallbackHandle)); | ||
271 | 223 | ||
272 | Constraints = new BSConstraintCollection(World); | 224 | Constraints = new BSConstraintCollection(World); |
273 | 225 | ||
274 | TerrainManager = new BSTerrainManager(this); | 226 | TerrainManager = new BSTerrainManager(this); |
275 | TerrainManager.CreateInitialGroundPlaneAndTerrain(); | 227 | TerrainManager.CreateInitialGroundPlaneAndTerrain(); |
276 | 228 | ||
277 | m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)Params.linksetImplementation); | 229 | m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation); |
278 | 230 | ||
279 | InTaintTime = false; | 231 | InTaintTime = false; |
280 | m_initialized = true; | 232 | m_initialized = true; |
@@ -285,9 +237,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
285 | private void GetInitialParameterValues(IConfigSource config) | 237 | private void GetInitialParameterValues(IConfigSource config) |
286 | { | 238 | { |
287 | ConfigurationParameters parms = new ConfigurationParameters(); | 239 | ConfigurationParameters parms = new ConfigurationParameters(); |
288 | m_params[0] = parms; | 240 | UnmanagedParams[0] = parms; |
289 | 241 | ||
290 | SetParameterDefaultValues(); | 242 | BSParam.SetParameterDefaultValues(this); |
291 | 243 | ||
292 | if (config != null) | 244 | if (config != null) |
293 | { | 245 | { |
@@ -295,19 +247,34 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
295 | IConfig pConfig = config.Configs["BulletSim"]; | 247 | IConfig pConfig = config.Configs["BulletSim"]; |
296 | if (pConfig != null) | 248 | if (pConfig != null) |
297 | { | 249 | { |
298 | SetParameterConfigurationValues(pConfig); | 250 | BSParam.SetParameterConfigurationValues(this, pConfig); |
251 | |||
252 | // There are two Bullet implementations to choose from | ||
253 | BulletEngineName = pConfig.GetString("BulletEngine", "BulletUnmanaged"); | ||
299 | 254 | ||
300 | // Very detailed logging for physics debugging | 255 | // Very detailed logging for physics debugging |
256 | // TODO: the boolean values can be moved to the normal parameter processing. | ||
301 | m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false); | 257 | m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false); |
302 | m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); | 258 | m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); |
303 | m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); | 259 | m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); |
304 | m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); | 260 | m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); |
261 | m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false); | ||
262 | m_physicsPhysicalDumpEnabled = pConfig.GetBoolean("PhysicsPhysicalDumpEnabled", false); | ||
305 | // Very detailed logging for vehicle debugging | 263 | // Very detailed logging for vehicle debugging |
306 | VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); | 264 | VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); |
265 | VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false); | ||
307 | 266 | ||
308 | // Do any replacements in the parameters | 267 | // Do any replacements in the parameters |
309 | m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); | 268 | m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); |
310 | } | 269 | } |
270 | |||
271 | // The material characteristics. | ||
272 | BSMaterials.InitializeFromDefaults(Params); | ||
273 | if (pConfig != null) | ||
274 | { | ||
275 | // Let the user add new and interesting material property values. | ||
276 | BSMaterials.InitializefromParameters(pConfig); | ||
277 | } | ||
311 | } | 278 | } |
312 | } | 279 | } |
313 | 280 | ||
@@ -326,16 +293,41 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
326 | return ret; | 293 | return ret; |
327 | } | 294 | } |
328 | 295 | ||
329 | // Called directly from unmanaged code so don't do much | 296 | // Select the connection to the actual Bullet implementation. |
330 | private void BulletLogger(string msg) | 297 | // The main engine selection is the engineName up to the first hypen. |
298 | // So "Bullet-2.80-OpenCL-Intel" specifies the 'bullet' class here and the whole name | ||
299 | // is passed to the engine to do its special selection, etc. | ||
300 | private BSAPITemplate SelectUnderlyingBulletEngine(string engineName) | ||
331 | { | 301 | { |
332 | m_log.Debug("[BULLETS UNMANAGED]:" + msg); | 302 | // For the moment, do a simple switch statement. |
333 | } | 303 | // Someday do fancyness with looking up the interfaces in the assembly. |
304 | BSAPITemplate ret = null; | ||
334 | 305 | ||
335 | // Called directly from unmanaged code so don't do much | 306 | string selectionName = engineName.ToLower(); |
336 | private void BulletLoggerPhysLog(string msg) | 307 | int hyphenIndex = engineName.IndexOf("-"); |
337 | { | 308 | if (hyphenIndex > 0) |
338 | DetailLog("[BULLETS UNMANAGED]:" + msg); | 309 | selectionName = engineName.ToLower().Substring(0, hyphenIndex - 1); |
310 | |||
311 | switch (selectionName) | ||
312 | { | ||
313 | case "bulletunmanaged": | ||
314 | ret = new BSAPIUnman(engineName, this); | ||
315 | break; | ||
316 | case "bulletxna": | ||
317 | ret = new BSAPIXNA(engineName, this); | ||
318 | break; | ||
319 | } | ||
320 | |||
321 | if (ret == null) | ||
322 | { | ||
323 | m_log.ErrorFormat("{0) COULD NOT SELECT BULLET ENGINE: '[BulletSim]PhysicsEngine' must be either 'BulletUnmanaged-*' or 'BulletXNA-*'", LogHeader); | ||
324 | } | ||
325 | else | ||
326 | { | ||
327 | m_log.WarnFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion); | ||
328 | } | ||
329 | |||
330 | return ret; | ||
339 | } | 331 | } |
340 | 332 | ||
341 | public override void Dispose() | 333 | public override void Dispose() |
@@ -345,8 +337,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
345 | // make sure no stepping happens while we're deleting stuff | 337 | // make sure no stepping happens while we're deleting stuff |
346 | m_initialized = false; | 338 | m_initialized = false; |
347 | 339 | ||
348 | TerrainManager.ReleaseGroundPlaneAndTerrain(); | ||
349 | |||
350 | foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects) | 340 | foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects) |
351 | { | 341 | { |
352 | kvp.Value.Destroy(); | 342 | kvp.Value.Destroy(); |
@@ -366,8 +356,15 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
366 | Shapes = null; | 356 | Shapes = null; |
367 | } | 357 | } |
368 | 358 | ||
359 | if (TerrainManager != null) | ||
360 | { | ||
361 | TerrainManager.ReleaseGroundPlaneAndTerrain(); | ||
362 | TerrainManager.Dispose(); | ||
363 | TerrainManager = null; | ||
364 | } | ||
365 | |||
369 | // Anything left in the unmanaged code should be cleaned out | 366 | // Anything left in the unmanaged code should be cleaned out |
370 | BulletSimAPI.Shutdown2(World.ptr); | 367 | PE.Shutdown(World); |
371 | 368 | ||
372 | // Not logging any more | 369 | // Not logging any more |
373 | PhysicsLogging.Close(); | 370 | PhysicsLogging.Close(); |
@@ -389,12 +386,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
389 | if (!m_initialized) return null; | 386 | if (!m_initialized) return null; |
390 | 387 | ||
391 | BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); | 388 | BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); |
392 | lock (PhysObjects) PhysObjects.Add(localID, actor); | 389 | lock (PhysObjects) |
390 | PhysObjects.Add(localID, actor); | ||
393 | 391 | ||
394 | // TODO: Remove kludge someday. | 392 | // TODO: Remove kludge someday. |
395 | // We must generate a collision for avatars whether they collide or not. | 393 | // We must generate a collision for avatars whether they collide or not. |
396 | // This is required by OpenSim to update avatar animations, etc. | 394 | // This is required by OpenSim to update avatar animations, etc. |
397 | lock (m_avatars) m_avatars.Add(actor); | 395 | lock (m_avatars) |
396 | m_avatars.Add(actor); | ||
398 | 397 | ||
399 | return actor; | 398 | return actor; |
400 | } | 399 | } |
@@ -410,9 +409,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
410 | { | 409 | { |
411 | try | 410 | try |
412 | { | 411 | { |
413 | lock (PhysObjects) PhysObjects.Remove(actor.LocalID); | 412 | lock (PhysObjects) |
413 | PhysObjects.Remove(bsactor.LocalID); | ||
414 | // Remove kludge someday | 414 | // Remove kludge someday |
415 | lock (m_avatars) m_avatars.Remove(bsactor); | 415 | lock (m_avatars) |
416 | m_avatars.Remove(bsactor); | ||
416 | } | 417 | } |
417 | catch (Exception e) | 418 | catch (Exception e) |
418 | { | 419 | { |
@@ -421,6 +422,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
421 | bsactor.Destroy(); | 422 | bsactor.Destroy(); |
422 | // bsactor.dispose(); | 423 | // bsactor.dispose(); |
423 | } | 424 | } |
425 | else | ||
426 | { | ||
427 | m_log.ErrorFormat("{0}: Requested to remove avatar that is not a BSCharacter. ID={1}, type={2}", | ||
428 | LogHeader, actor.LocalID, actor.GetType().Name); | ||
429 | } | ||
424 | } | 430 | } |
425 | 431 | ||
426 | public override void RemovePrim(PhysicsActor prim) | 432 | public override void RemovePrim(PhysicsActor prim) |
@@ -474,41 +480,56 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
474 | // Simulate one timestep | 480 | // Simulate one timestep |
475 | public override float Simulate(float timeStep) | 481 | public override float Simulate(float timeStep) |
476 | { | 482 | { |
483 | // prevent simulation until we've been initialized | ||
484 | if (!m_initialized) return 5.0f; | ||
485 | |||
486 | LastTimeStep = timeStep; | ||
487 | |||
477 | int updatedEntityCount = 0; | 488 | int updatedEntityCount = 0; |
478 | IntPtr updatedEntitiesPtr; | ||
479 | int collidersCount = 0; | 489 | int collidersCount = 0; |
480 | IntPtr collidersPtr; | ||
481 | 490 | ||
482 | int beforeTime = 0; | 491 | int beforeTime = 0; |
483 | int simTime = 0; | 492 | int simTime = 0; |
484 | 493 | ||
485 | // prevent simulation until we've been initialized | ||
486 | if (!m_initialized) return 5.0f; | ||
487 | |||
488 | // update the prim states while we know the physics engine is not busy | 494 | // update the prim states while we know the physics engine is not busy |
489 | int numTaints = _taintOperations.Count; | 495 | int numTaints = _taintOperations.Count; |
496 | |||
497 | InTaintTime = true; // Only used for debugging so locking is not necessary. | ||
498 | |||
499 | ProcessTaints(); | ||
500 | |||
501 | // Some of the physical objects requre individual, pre-step calls | ||
502 | // (vehicles and avatar movement, in particular) | ||
503 | TriggerPreStepEvent(timeStep); | ||
504 | |||
505 | // the prestep actions might have added taints | ||
506 | numTaints += _taintOperations.Count; | ||
490 | ProcessTaints(); | 507 | ProcessTaints(); |
491 | 508 | ||
492 | // Some of the prims operate with special vehicle properties | 509 | InTaintTime = false; // Only used for debugging so locking is not necessary. |
493 | ProcessVehicles(timeStep); | 510 | |
494 | ProcessTaints(); // the vehicles might have added taints | 511 | // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. |
512 | // Only enable this in a limited test world with few objects. | ||
513 | if (m_physicsPhysicalDumpEnabled) | ||
514 | PE.DumpAllInfo(World); | ||
495 | 515 | ||
496 | // step the physical world one interval | 516 | // step the physical world one interval |
497 | m_simulationStep++; | 517 | m_simulationStep++; |
498 | int numSubSteps = 0; | 518 | int numSubSteps = 0; |
499 | |||
500 | try | 519 | try |
501 | { | 520 | { |
502 | if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG | 521 | if (PhysicsLogging.Enabled) |
503 | if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); | 522 | beforeTime = Util.EnvironmentTickCount(); |
504 | 523 | ||
505 | numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep, | 524 | numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount); |
506 | out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); | ||
507 | 525 | ||
508 | if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime); | 526 | if (PhysicsLogging.Enabled) |
509 | DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}", | 527 | { |
510 | DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount); | 528 | simTime = Util.EnvironmentTickCountSubtract(beforeTime); |
511 | if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG | 529 | DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}", |
530 | DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, | ||
531 | updatedEntityCount, collidersCount, ObjectsWithCollisions.Count); | ||
532 | } | ||
512 | } | 533 | } |
513 | catch (Exception e) | 534 | catch (Exception e) |
514 | { | 535 | { |
@@ -520,9 +541,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
520 | collidersCount = 0; | 541 | collidersCount = 0; |
521 | } | 542 | } |
522 | 543 | ||
523 | // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in | 544 | if ((m_simulationStep % PhysicsMetricDumpFrames) == 0) |
545 | PE.DumpPhysicsStatistics(World); | ||
524 | 546 | ||
525 | // Get a value for 'now' so all the collision and update routines don't have to get their own | 547 | // Get a value for 'now' so all the collision and update routines don't have to get their own. |
526 | SimulationNowTime = Util.EnvironmentTickCount(); | 548 | SimulationNowTime = Util.EnvironmentTickCount(); |
527 | 549 | ||
528 | // If there were collisions, process them by sending the event to the prim. | 550 | // If there were collisions, process them by sending the event to the prim. |
@@ -562,12 +584,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
562 | 584 | ||
563 | // Objects that are done colliding are removed from the ObjectsWithCollisions list. | 585 | // Objects that are done colliding are removed from the ObjectsWithCollisions list. |
564 | // Not done above because it is inside an iteration of ObjectWithCollisions. | 586 | // Not done above because it is inside an iteration of ObjectWithCollisions. |
587 | // This complex collision processing is required to create an empty collision | ||
588 | // event call after all real collisions have happened on an object. This enables | ||
589 | // the simulator to generate the 'collision end' event. | ||
565 | if (ObjectsWithNoMoreCollisions.Count > 0) | 590 | if (ObjectsWithNoMoreCollisions.Count > 0) |
566 | { | 591 | { |
567 | foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) | 592 | foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) |
568 | ObjectsWithCollisions.Remove(po); | 593 | ObjectsWithCollisions.Remove(po); |
569 | ObjectsWithNoMoreCollisions.Clear(); | 594 | ObjectsWithNoMoreCollisions.Clear(); |
570 | } | 595 | } |
596 | // Done with collisions. | ||
571 | 597 | ||
572 | // If any of the objects had updated properties, tell the object it has been changed by the physics engine | 598 | // If any of the objects had updated properties, tell the object it has been changed by the physics engine |
573 | if (updatedEntityCount > 0) | 599 | if (updatedEntityCount > 0) |
@@ -583,17 +609,17 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
583 | } | 609 | } |
584 | } | 610 | } |
585 | 611 | ||
586 | ProcessPostStepTaints(); | 612 | TriggerPostStepEvent(timeStep); |
587 | 613 | ||
588 | // This causes the unmanaged code to output ALL the values found in ALL the objects in the world. | 614 | // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. |
589 | // Only enable this in a limited test world with few objects. | 615 | // Only enable this in a limited test world with few objects. |
590 | // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG | 616 | if (m_physicsPhysicalDumpEnabled) |
617 | PE.DumpAllInfo(World); | ||
591 | 618 | ||
592 | // The physics engine returns the number of milliseconds it simulated this call. | 619 | // The physics engine returns the number of milliseconds it simulated this call. |
593 | // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. | 620 | // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. |
594 | // We multiply by 55 to give a recognizable running rate (55 or less). | 621 | // Multiply by a fixed nominal frame rate to give a rate similar to the simulator (usually 55). |
595 | return numSubSteps * m_fixedTimeStep * 1000 * 55; | 622 | return (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate; |
596 | // return timeStep * 1000 * 55; | ||
597 | } | 623 | } |
598 | 624 | ||
599 | // Something has collided | 625 | // Something has collided |
@@ -639,12 +665,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
639 | 665 | ||
640 | public override void SetWaterLevel(float baseheight) | 666 | public override void SetWaterLevel(float baseheight) |
641 | { | 667 | { |
642 | m_waterLevel = baseheight; | 668 | SimpleWaterLevel = baseheight; |
643 | } | ||
644 | // Someday.... | ||
645 | public float GetWaterLevelAtXYZ(Vector3 loc) | ||
646 | { | ||
647 | return m_waterLevel; | ||
648 | } | 669 | } |
649 | 670 | ||
650 | public override void DeleteTerrain() | 671 | public override void DeleteTerrain() |
@@ -681,6 +702,15 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
681 | public override bool IsThreaded { get { return false; } } | 702 | public override bool IsThreaded { get { return false; } } |
682 | 703 | ||
683 | #region Taints | 704 | #region Taints |
705 | // The simulation execution order is: | ||
706 | // Simulate() | ||
707 | // DoOneTimeTaints | ||
708 | // TriggerPreStepEvent | ||
709 | // DoOneTimeTaints | ||
710 | // Step() | ||
711 | // ProcessAndForwardCollisions | ||
712 | // ProcessAndForwardPropertyUpdates | ||
713 | // TriggerPostStepEvent | ||
684 | 714 | ||
685 | // Calls to the PhysicsActors can't directly call into the physics engine | 715 | // Calls to the PhysicsActors can't directly call into the physics engine |
686 | // because it might be busy. We delay changes to a known time. | 716 | // because it might be busy. We delay changes to a known time. |
@@ -707,58 +737,35 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
707 | TaintedObject(ident, callback); | 737 | TaintedObject(ident, callback); |
708 | } | 738 | } |
709 | 739 | ||
740 | private void TriggerPreStepEvent(float timeStep) | ||
741 | { | ||
742 | PreStepAction actions = BeforeStep; | ||
743 | if (actions != null) | ||
744 | actions(timeStep); | ||
745 | |||
746 | } | ||
747 | |||
748 | private void TriggerPostStepEvent(float timeStep) | ||
749 | { | ||
750 | PreStepAction actions = AfterStep; | ||
751 | if (actions != null) | ||
752 | actions(timeStep); | ||
753 | |||
754 | } | ||
755 | |||
710 | // When someone tries to change a property on a BSPrim or BSCharacter, the object queues | 756 | // When someone tries to change a property on a BSPrim or BSCharacter, the object queues |
711 | // a callback into itself to do the actual property change. That callback is called | 757 | // a callback into itself to do the actual property change. That callback is called |
712 | // here just before the physics engine is called to step the simulation. | 758 | // here just before the physics engine is called to step the simulation. |
713 | public void ProcessTaints() | 759 | public void ProcessTaints() |
714 | { | 760 | { |
715 | InTaintTime = true; // Only used for debugging so locking is not necessary. | ||
716 | ProcessRegularTaints(); | 761 | ProcessRegularTaints(); |
717 | ProcessPostTaintTaints(); | 762 | ProcessPostTaintTaints(); |
718 | InTaintTime = false; | ||
719 | } | 763 | } |
720 | 764 | ||
721 | private void ProcessRegularTaints() | 765 | private void ProcessRegularTaints() |
722 | { | 766 | { |
723 | if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process | 767 | if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process |
724 | { | 768 | { |
725 | /* | ||
726 | // Code to limit the number of taints processed per step. Meant to limit step time. | ||
727 | // Unsure if a good idea as code assumes that taints are done before the step. | ||
728 | int taintCount = m_taintsToProcessPerStep; | ||
729 | TaintCallbackEntry oneCallback = new TaintCallbackEntry(); | ||
730 | while (_taintOperations.Count > 0 && taintCount-- > 0) | ||
731 | { | ||
732 | bool gotOne = false; | ||
733 | lock (_taintLock) | ||
734 | { | ||
735 | if (_taintOperations.Count > 0) | ||
736 | { | ||
737 | oneCallback = _taintOperations[0]; | ||
738 | _taintOperations.RemoveAt(0); | ||
739 | gotOne = true; | ||
740 | } | ||
741 | } | ||
742 | if (gotOne) | ||
743 | { | ||
744 | try | ||
745 | { | ||
746 | DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, oneCallback.ident); | ||
747 | oneCallback.callback(); | ||
748 | } | ||
749 | catch (Exception e) | ||
750 | { | ||
751 | DetailLog("{0},BSScene.ProcessTaints,doTaintException,id={1}", DetailLogZero, oneCallback.ident); // DEBUG DEBUG DEBUG | ||
752 | m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, oneCallback.ident, e); | ||
753 | } | ||
754 | } | ||
755 | } | ||
756 | if (_taintOperations.Count > 0) | ||
757 | { | ||
758 | DetailLog("{0},BSScene.ProcessTaints,leftTaintsOnList,numNotProcessed={1}", DetailLogZero, _taintOperations.Count); | ||
759 | } | ||
760 | */ | ||
761 | |||
762 | // swizzle a new list into the list location so we can process what's there | 769 | // swizzle a new list into the list location so we can process what's there |
763 | List<TaintCallbackEntry> oldList; | 770 | List<TaintCallbackEntry> oldList; |
764 | lock (_taintLock) | 771 | lock (_taintLock) |
@@ -797,6 +804,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
797 | return; | 804 | return; |
798 | } | 805 | } |
799 | 806 | ||
807 | // Taints that happen after the normal taint processing but before the simulation step. | ||
800 | private void ProcessPostTaintTaints() | 808 | private void ProcessPostTaintTaints() |
801 | { | 809 | { |
802 | if (_postTaintOperations.Count > 0) | 810 | if (_postTaintOperations.Count > 0) |
@@ -824,45 +832,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
824 | } | 832 | } |
825 | } | 833 | } |
826 | 834 | ||
827 | public void PostStepTaintObject(String ident, TaintCallback callback) | ||
828 | { | ||
829 | if (!m_initialized) return; | ||
830 | |||
831 | lock (_taintLock) | ||
832 | { | ||
833 | _postStepOperations.Add(new TaintCallbackEntry(ident, callback)); | ||
834 | } | ||
835 | |||
836 | return; | ||
837 | } | ||
838 | |||
839 | private void ProcessPostStepTaints() | ||
840 | { | ||
841 | if (_postStepOperations.Count > 0) | ||
842 | { | ||
843 | List<TaintCallbackEntry> oldList; | ||
844 | lock (_taintLock) | ||
845 | { | ||
846 | oldList = _postStepOperations; | ||
847 | _postStepOperations = new List<TaintCallbackEntry>(); | ||
848 | } | ||
849 | |||
850 | foreach (TaintCallbackEntry tcbe in oldList) | ||
851 | { | ||
852 | try | ||
853 | { | ||
854 | DetailLog("{0},BSScene.ProcessPostStepTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG | ||
855 | tcbe.callback(); | ||
856 | } | ||
857 | catch (Exception e) | ||
858 | { | ||
859 | m_log.ErrorFormat("{0}: ProcessPostStepTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e); | ||
860 | } | ||
861 | } | ||
862 | oldList.Clear(); | ||
863 | } | ||
864 | } | ||
865 | |||
866 | // Only used for debugging. Does not change state of anything so locking is not necessary. | 835 | // Only used for debugging. Does not change state of anything so locking is not necessary. |
867 | public bool AssertInTaintTime(string whereFrom) | 836 | public bool AssertInTaintTime(string whereFrom) |
868 | { | 837 | { |
@@ -870,517 +839,19 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
870 | { | 839 | { |
871 | DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom); | 840 | DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom); |
872 | m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom); | 841 | m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom); |
873 | Util.PrintCallStack(); // Prints the stack into the DEBUG log file. | 842 | Util.PrintCallStack(DetailLog); |
874 | } | 843 | } |
875 | return InTaintTime; | 844 | return InTaintTime; |
876 | } | 845 | } |
877 | 846 | ||
878 | #endregion // Taints | 847 | #endregion // Taints |
879 | 848 | ||
880 | #region Vehicles | ||
881 | |||
882 | public void VehicleInSceneTypeChanged(BSPrim vehic, Vehicle newType) | ||
883 | { | ||
884 | RemoveVehiclePrim(vehic); | ||
885 | if (newType != Vehicle.TYPE_NONE) | ||
886 | { | ||
887 | // make it so the scene will call us each tick to do vehicle things | ||
888 | AddVehiclePrim(vehic); | ||
889 | } | ||
890 | } | ||
891 | |||
892 | // Make so the scene will call this prim for vehicle actions each tick. | ||
893 | // Safe to call if prim is already in the vehicle list. | ||
894 | public void AddVehiclePrim(BSPrim vehicle) | ||
895 | { | ||
896 | lock (m_vehicles) | ||
897 | { | ||
898 | if (!m_vehicles.Contains(vehicle)) | ||
899 | { | ||
900 | m_vehicles.Add(vehicle); | ||
901 | } | ||
902 | } | ||
903 | } | ||
904 | |||
905 | // Remove a prim from our list of vehicles. | ||
906 | // Safe to call if the prim is not in the vehicle list. | ||
907 | public void RemoveVehiclePrim(BSPrim vehicle) | ||
908 | { | ||
909 | lock (m_vehicles) | ||
910 | { | ||
911 | if (m_vehicles.Contains(vehicle)) | ||
912 | { | ||
913 | m_vehicles.Remove(vehicle); | ||
914 | } | ||
915 | } | ||
916 | } | ||
917 | |||
918 | // Some prims have extra vehicle actions | ||
919 | // Called at taint time! | ||
920 | private void ProcessVehicles(float timeStep) | ||
921 | { | ||
922 | foreach (BSPhysObject pobj in m_vehicles) | ||
923 | { | ||
924 | pobj.StepVehicle(timeStep); | ||
925 | } | ||
926 | } | ||
927 | #endregion Vehicles | ||
928 | |||
929 | #region INI and command line parameter processing | ||
930 | |||
931 | delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val); | ||
932 | delegate float ParamGet(BSScene scene); | ||
933 | delegate void ParamSet(BSScene scene, string paramName, uint localID, float val); | ||
934 | delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val); | ||
935 | |||
936 | private struct ParameterDefn | ||
937 | { | ||
938 | public string name; // string name of the parameter | ||
939 | public string desc; // a short description of what the parameter means | ||
940 | public float defaultValue; // default value if not specified anywhere else | ||
941 | public ParamUser userParam; // get the value from the configuration file | ||
942 | public ParamGet getter; // return the current value stored for this parameter | ||
943 | public ParamSet setter; // set the current value for this parameter | ||
944 | public SetOnObject onObject; // set the value on an object in the physical domain | ||
945 | public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s) | ||
946 | { | ||
947 | name = n; | ||
948 | desc = d; | ||
949 | defaultValue = v; | ||
950 | userParam = u; | ||
951 | getter = g; | ||
952 | setter = s; | ||
953 | onObject = null; | ||
954 | } | ||
955 | public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o) | ||
956 | { | ||
957 | name = n; | ||
958 | desc = d; | ||
959 | defaultValue = v; | ||
960 | userParam = u; | ||
961 | getter = g; | ||
962 | setter = s; | ||
963 | onObject = o; | ||
964 | } | ||
965 | } | ||
966 | |||
967 | // List of all of the externally visible parameters. | ||
968 | // For each parameter, this table maps a text name to getter and setters. | ||
969 | // To add a new externally referencable/settable parameter, add the paramter storage | ||
970 | // location somewhere in the program and make an entry in this table with the | ||
971 | // getters and setters. | ||
972 | // It is easiest to find an existing definition and copy it. | ||
973 | // Parameter values are floats. Booleans are converted to a floating value. | ||
974 | // | ||
975 | // A ParameterDefn() takes the following parameters: | ||
976 | // -- the text name of the parameter. This is used for console input and ini file. | ||
977 | // -- a short text description of the parameter. This shows up in the console listing. | ||
978 | // -- a delegate for fetching the parameter from the ini file. | ||
979 | // Should handle fetching the right type from the ini file and converting it. | ||
980 | // -- a delegate for getting the value as a float | ||
981 | // -- a delegate for setting the value from a float | ||
982 | // | ||
983 | // The single letter parameters for the delegates are: | ||
984 | // s = BSScene | ||
985 | // o = BSPhysObject | ||
986 | // p = string parameter name | ||
987 | // l = localID of referenced object | ||
988 | // v = float value | ||
989 | // cf = parameter configuration class (for fetching values from ini file) | ||
990 | private ParameterDefn[] ParameterDefinitions = | ||
991 | { | ||
992 | new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties", | ||
993 | ConfigurationParameters.numericTrue, | ||
994 | (s,cf,p,v) => { s.ShouldMeshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); }, | ||
995 | (s) => { return s.NumericBool(s.ShouldMeshSculptedPrim); }, | ||
996 | (s,p,l,v) => { s.ShouldMeshSculptedPrim = s.BoolNumeric(v); } ), | ||
997 | new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects", | ||
998 | ConfigurationParameters.numericFalse, | ||
999 | (s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); }, | ||
1000 | (s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); }, | ||
1001 | (s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ), | ||
1002 | new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects", | ||
1003 | ConfigurationParameters.numericTrue, | ||
1004 | (s,cf,p,v) => { s.ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, s.BoolNumeric(v)); }, | ||
1005 | (s) => { return s.NumericBool(s.ShouldUseHullsForPhysicalObjects); }, | ||
1006 | (s,p,l,v) => { s.ShouldUseHullsForPhysicalObjects = s.BoolNumeric(v); } ), | ||
1007 | |||
1008 | new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", | ||
1009 | 8f, | ||
1010 | (s,cf,p,v) => { s.MeshLOD = (float)cf.GetInt(p, (int)v); }, | ||
1011 | (s) => { return s.MeshLOD; }, | ||
1012 | (s,p,l,v) => { s.MeshLOD = v; } ), | ||
1013 | new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters", | ||
1014 | 16f, | ||
1015 | (s,cf,p,v) => { s.MeshMegaPrimLOD = (float)cf.GetInt(p, (int)v); }, | ||
1016 | (s) => { return s.MeshMegaPrimLOD; }, | ||
1017 | (s,p,l,v) => { s.MeshMegaPrimLOD = v; } ), | ||
1018 | new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD", | ||
1019 | 10f, | ||
1020 | (s,cf,p,v) => { s.MeshMegaPrimThreshold = (float)cf.GetInt(p, (int)v); }, | ||
1021 | (s) => { return s.MeshMegaPrimThreshold; }, | ||
1022 | (s,p,l,v) => { s.MeshMegaPrimThreshold = v; } ), | ||
1023 | new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)", | ||
1024 | 32f, | ||
1025 | (s,cf,p,v) => { s.SculptLOD = (float)cf.GetInt(p, (int)v); }, | ||
1026 | (s) => { return s.SculptLOD; }, | ||
1027 | (s,p,l,v) => { s.SculptLOD = v; } ), | ||
1028 | |||
1029 | new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps", | ||
1030 | 10f, | ||
1031 | (s,cf,p,v) => { s.m_maxSubSteps = cf.GetInt(p, (int)v); }, | ||
1032 | (s) => { return (float)s.m_maxSubSteps; }, | ||
1033 | (s,p,l,v) => { s.m_maxSubSteps = (int)v; } ), | ||
1034 | new ParameterDefn("FixedTimeStep", "In simulation step, seconds of one substep (1/60)", | ||
1035 | 1f / 60f, | ||
1036 | (s,cf,p,v) => { s.m_fixedTimeStep = cf.GetFloat(p, v); }, | ||
1037 | (s) => { return (float)s.m_fixedTimeStep; }, | ||
1038 | (s,p,l,v) => { s.m_fixedTimeStep = v; } ), | ||
1039 | new ParameterDefn("MaxCollisionsPerFrame", "Max collisions returned at end of each frame", | ||
1040 | 2048f, | ||
1041 | (s,cf,p,v) => { s.m_maxCollisionsPerFrame = cf.GetInt(p, (int)v); }, | ||
1042 | (s) => { return (float)s.m_maxCollisionsPerFrame; }, | ||
1043 | (s,p,l,v) => { s.m_maxCollisionsPerFrame = (int)v; } ), | ||
1044 | new ParameterDefn("MaxUpdatesPerFrame", "Max updates returned at end of each frame", | ||
1045 | 8000f, | ||
1046 | (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); }, | ||
1047 | (s) => { return (float)s.m_maxUpdatesPerFrame; }, | ||
1048 | (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ), | ||
1049 | new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step", | ||
1050 | 500f, | ||
1051 | (s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); }, | ||
1052 | (s) => { return (float)s.m_taintsToProcessPerStep; }, | ||
1053 | (s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ), | ||
1054 | new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", | ||
1055 | 10000.01f, | ||
1056 | (s,cf,p,v) => { s.MaximumObjectMass = cf.GetFloat(p, v); }, | ||
1057 | (s) => { return (float)s.MaximumObjectMass; }, | ||
1058 | (s,p,l,v) => { s.MaximumObjectMass = v; } ), | ||
1059 | |||
1060 | new ParameterDefn("PID_D", "Derivitive factor for motion smoothing", | ||
1061 | 2200f, | ||
1062 | (s,cf,p,v) => { s.PID_D = cf.GetFloat(p, v); }, | ||
1063 | (s) => { return (float)s.PID_D; }, | ||
1064 | (s,p,l,v) => { s.PID_D = v; } ), | ||
1065 | new ParameterDefn("PID_P", "Parameteric factor for motion smoothing", | ||
1066 | 900f, | ||
1067 | (s,cf,p,v) => { s.PID_P = cf.GetFloat(p, v); }, | ||
1068 | (s) => { return (float)s.PID_P; }, | ||
1069 | (s,p,l,v) => { s.PID_P = v; } ), | ||
1070 | |||
1071 | new ParameterDefn("DefaultFriction", "Friction factor used on new objects", | ||
1072 | 0.5f, | ||
1073 | (s,cf,p,v) => { s.m_params[0].defaultFriction = cf.GetFloat(p, v); }, | ||
1074 | (s) => { return s.m_params[0].defaultFriction; }, | ||
1075 | (s,p,l,v) => { s.m_params[0].defaultFriction = v; } ), | ||
1076 | new ParameterDefn("DefaultDensity", "Density for new objects" , | ||
1077 | 10.000006836f, // Aluminum g/cm3 | ||
1078 | (s,cf,p,v) => { s.m_params[0].defaultDensity = cf.GetFloat(p, v); }, | ||
1079 | (s) => { return s.m_params[0].defaultDensity; }, | ||
1080 | (s,p,l,v) => { s.m_params[0].defaultDensity = v; } ), | ||
1081 | new ParameterDefn("DefaultRestitution", "Bouncyness of an object" , | ||
1082 | 0f, | ||
1083 | (s,cf,p,v) => { s.m_params[0].defaultRestitution = cf.GetFloat(p, v); }, | ||
1084 | (s) => { return s.m_params[0].defaultRestitution; }, | ||
1085 | (s,p,l,v) => { s.m_params[0].defaultRestitution = v; } ), | ||
1086 | new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)", | ||
1087 | 0f, | ||
1088 | (s,cf,p,v) => { s.m_params[0].collisionMargin = cf.GetFloat(p, v); }, | ||
1089 | (s) => { return s.m_params[0].collisionMargin; }, | ||
1090 | (s,p,l,v) => { s.m_params[0].collisionMargin = v; } ), | ||
1091 | new ParameterDefn("Gravity", "Vertical force of gravity (negative means down)", | ||
1092 | -9.80665f, | ||
1093 | (s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); }, | ||
1094 | (s) => { return s.m_params[0].gravity; }, | ||
1095 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].gravity, p, PhysParameterEntry.APPLY_TO_NONE, v); }, | ||
1096 | (s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ), | ||
1097 | |||
1098 | |||
1099 | new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)", | ||
1100 | 0f, | ||
1101 | (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); }, | ||
1102 | (s) => { return s.m_params[0].linearDamping; }, | ||
1103 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); }, | ||
1104 | (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, v, s.m_params[0].angularDamping); } ), | ||
1105 | new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)", | ||
1106 | 0f, | ||
1107 | (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); }, | ||
1108 | (s) => { return s.m_params[0].angularDamping; }, | ||
1109 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); }, | ||
1110 | (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, s.m_params[0].linearDamping, v); } ), | ||
1111 | new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static", | ||
1112 | 0.2f, | ||
1113 | (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); }, | ||
1114 | (s) => { return s.m_params[0].deactivationTime; }, | ||
1115 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); }, | ||
1116 | (s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.PhysBody.ptr, v); } ), | ||
1117 | new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static", | ||
1118 | 0.8f, | ||
1119 | (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); }, | ||
1120 | (s) => { return s.m_params[0].linearSleepingThreshold; }, | ||
1121 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); }, | ||
1122 | (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ), | ||
1123 | new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static", | ||
1124 | 1.0f, | ||
1125 | (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); }, | ||
1126 | (s) => { return s.m_params[0].angularSleepingThreshold; }, | ||
1127 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); }, | ||
1128 | (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ), | ||
1129 | new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" , | ||
1130 | 0f, // set to zero to disable | ||
1131 | (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); }, | ||
1132 | (s) => { return s.m_params[0].ccdMotionThreshold; }, | ||
1133 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); }, | ||
1134 | (s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.PhysBody.ptr, v); } ), | ||
1135 | new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , | ||
1136 | 0f, | ||
1137 | (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); }, | ||
1138 | (s) => { return s.m_params[0].ccdSweptSphereRadius; }, | ||
1139 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); }, | ||
1140 | (s,o,v) => { BulletSimAPI.SetCcdSweptSphereRadius2(o.PhysBody.ptr, v); } ), | ||
1141 | new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" , | ||
1142 | 0.1f, | ||
1143 | (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); }, | ||
1144 | (s) => { return s.m_params[0].contactProcessingThreshold; }, | ||
1145 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); }, | ||
1146 | (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ), | ||
1147 | |||
1148 | new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)", | ||
1149 | (float)BSTerrainPhys.TerrainImplementation.Mesh, | ||
1150 | (s,cf,p,v) => { s.m_params[0].terrainImplementation = cf.GetFloat(p,v); }, | ||
1151 | (s) => { return s.m_params[0].terrainImplementation; }, | ||
1152 | (s,p,l,v) => { s.m_params[0].terrainImplementation = v; } ), | ||
1153 | new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , | ||
1154 | 0.5f, | ||
1155 | (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, | ||
1156 | (s) => { return s.m_params[0].terrainFriction; }, | ||
1157 | (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ), | ||
1158 | new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" , | ||
1159 | 0.8f, | ||
1160 | (s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); }, | ||
1161 | (s) => { return s.m_params[0].terrainHitFraction; }, | ||
1162 | (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; /* TODO: set on real terrain */ } ), | ||
1163 | new ParameterDefn("TerrainRestitution", "Bouncyness" , | ||
1164 | 0f, | ||
1165 | (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); }, | ||
1166 | (s) => { return s.m_params[0].terrainRestitution; }, | ||
1167 | (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ), | ||
1168 | new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", | ||
1169 | 0.2f, | ||
1170 | (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, | ||
1171 | (s) => { return s.m_params[0].avatarFriction; }, | ||
1172 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ), | ||
1173 | new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.", | ||
1174 | 10f, | ||
1175 | (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); }, | ||
1176 | (s) => { return s.m_params[0].avatarStandingFriction; }, | ||
1177 | (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ), | ||
1178 | new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.", | ||
1179 | 60f, | ||
1180 | (s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); }, | ||
1181 | (s) => { return s.m_params[0].avatarDensity; }, | ||
1182 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarDensity, p, l, v); } ), | ||
1183 | new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.", | ||
1184 | 0f, | ||
1185 | (s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); }, | ||
1186 | (s) => { return s.m_params[0].avatarRestitution; }, | ||
1187 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarRestitution, p, l, v); } ), | ||
1188 | new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule", | ||
1189 | 0.6f, | ||
1190 | (s,cf,p,v) => { s.m_params[0].avatarCapsuleWidth = cf.GetFloat(p, v); }, | ||
1191 | (s) => { return s.m_params[0].avatarCapsuleWidth; }, | ||
1192 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleWidth, p, l, v); } ), | ||
1193 | new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule", | ||
1194 | 0.45f, | ||
1195 | (s,cf,p,v) => { s.m_params[0].avatarCapsuleDepth = cf.GetFloat(p, v); }, | ||
1196 | (s) => { return s.m_params[0].avatarCapsuleDepth; }, | ||
1197 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleDepth, p, l, v); } ), | ||
1198 | new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar", | ||
1199 | 1.5f, | ||
1200 | (s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); }, | ||
1201 | (s) => { return s.m_params[0].avatarCapsuleHeight; }, | ||
1202 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleHeight, p, l, v); } ), | ||
1203 | new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions", | ||
1204 | 0.1f, | ||
1205 | (s,cf,p,v) => { s.m_params[0].avatarContactProcessingThreshold = cf.GetFloat(p, v); }, | ||
1206 | (s) => { return s.m_params[0].avatarContactProcessingThreshold; }, | ||
1207 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ), | ||
1208 | |||
1209 | |||
1210 | new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", | ||
1211 | 0f, | ||
1212 | (s,cf,p,v) => { s.m_params[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); }, | ||
1213 | (s) => { return s.m_params[0].maxPersistantManifoldPoolSize; }, | ||
1214 | (s,p,l,v) => { s.m_params[0].maxPersistantManifoldPoolSize = v; } ), | ||
1215 | new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)", | ||
1216 | 0f, | ||
1217 | (s,cf,p,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); }, | ||
1218 | (s) => { return s.m_params[0].maxCollisionAlgorithmPoolSize; }, | ||
1219 | (s,p,l,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = v; } ), | ||
1220 | new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count", | ||
1221 | ConfigurationParameters.numericFalse, | ||
1222 | (s,cf,p,v) => { s.m_params[0].shouldDisableContactPoolDynamicAllocation = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, | ||
1223 | (s) => { return s.m_params[0].shouldDisableContactPoolDynamicAllocation; }, | ||
1224 | (s,p,l,v) => { s.m_params[0].shouldDisableContactPoolDynamicAllocation = v; } ), | ||
1225 | new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step", | ||
1226 | ConfigurationParameters.numericFalse, | ||
1227 | (s,cf,p,v) => { s.m_params[0].shouldForceUpdateAllAabbs = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, | ||
1228 | (s) => { return s.m_params[0].shouldForceUpdateAllAabbs; }, | ||
1229 | (s,p,l,v) => { s.m_params[0].shouldForceUpdateAllAabbs = v; } ), | ||
1230 | new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction", | ||
1231 | ConfigurationParameters.numericTrue, | ||
1232 | (s,cf,p,v) => { s.m_params[0].shouldRandomizeSolverOrder = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, | ||
1233 | (s) => { return s.m_params[0].shouldRandomizeSolverOrder; }, | ||
1234 | (s,p,l,v) => { s.m_params[0].shouldRandomizeSolverOrder = v; } ), | ||
1235 | new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands", | ||
1236 | ConfigurationParameters.numericTrue, | ||
1237 | (s,cf,p,v) => { s.m_params[0].shouldSplitSimulationIslands = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, | ||
1238 | (s) => { return s.m_params[0].shouldSplitSimulationIslands; }, | ||
1239 | (s,p,l,v) => { s.m_params[0].shouldSplitSimulationIslands = v; } ), | ||
1240 | new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching", | ||
1241 | ConfigurationParameters.numericFalse, | ||
1242 | (s,cf,p,v) => { s.m_params[0].shouldEnableFrictionCaching = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, | ||
1243 | (s) => { return s.m_params[0].shouldEnableFrictionCaching; }, | ||
1244 | (s,p,l,v) => { s.m_params[0].shouldEnableFrictionCaching = v; } ), | ||
1245 | new ParameterDefn("NumberOfSolverIterations", "Number of internal iterations (0 means default)", | ||
1246 | 0f, // zero says use Bullet default | ||
1247 | (s,cf,p,v) => { s.m_params[0].numberOfSolverIterations = cf.GetFloat(p, v); }, | ||
1248 | (s) => { return s.m_params[0].numberOfSolverIterations; }, | ||
1249 | (s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ), | ||
1250 | |||
1251 | new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)", | ||
1252 | (float)BSLinkset.LinksetImplementation.Compound, | ||
1253 | (s,cf,p,v) => { s.m_params[0].linksetImplementation = cf.GetFloat(p,v); }, | ||
1254 | (s) => { return s.m_params[0].linksetImplementation; }, | ||
1255 | (s,p,l,v) => { s.m_params[0].linksetImplementation = v; } ), | ||
1256 | new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.", | ||
1257 | ConfigurationParameters.numericFalse, | ||
1258 | (s,cf,p,v) => { s.m_params[0].linkConstraintUseFrameOffset = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, | ||
1259 | (s) => { return s.m_params[0].linkConstraintUseFrameOffset; }, | ||
1260 | (s,p,l,v) => { s.m_params[0].linkConstraintUseFrameOffset = v; } ), | ||
1261 | new ParameterDefn("LinkConstraintEnableTransMotor", "Whether to enable translational motor on linkset constraints", | ||
1262 | ConfigurationParameters.numericTrue, | ||
1263 | (s,cf,p,v) => { s.m_params[0].linkConstraintEnableTransMotor = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, | ||
1264 | (s) => { return s.m_params[0].linkConstraintEnableTransMotor; }, | ||
1265 | (s,p,l,v) => { s.m_params[0].linkConstraintEnableTransMotor = v; } ), | ||
1266 | new ParameterDefn("LinkConstraintTransMotorMaxVel", "Maximum velocity to be applied by translational motor in linkset constraints", | ||
1267 | 5.0f, | ||
1268 | (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxVel = cf.GetFloat(p, v); }, | ||
1269 | (s) => { return s.m_params[0].linkConstraintTransMotorMaxVel; }, | ||
1270 | (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxVel = v; } ), | ||
1271 | new ParameterDefn("LinkConstraintTransMotorMaxForce", "Maximum force to be applied by translational motor in linkset constraints", | ||
1272 | 0.1f, | ||
1273 | (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); }, | ||
1274 | (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; }, | ||
1275 | (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ), | ||
1276 | new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1", | ||
1277 | 0.1f, | ||
1278 | (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); }, | ||
1279 | (s) => { return s.m_params[0].linkConstraintCFM; }, | ||
1280 | (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ), | ||
1281 | new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2", | ||
1282 | 0.1f, | ||
1283 | (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); }, | ||
1284 | (s) => { return s.m_params[0].linkConstraintERP; }, | ||
1285 | (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ), | ||
1286 | new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)", | ||
1287 | 40, | ||
1288 | (s,cf,p,v) => { s.m_params[0].linkConstraintSolverIterations = cf.GetFloat(p, v); }, | ||
1289 | (s) => { return s.m_params[0].linkConstraintSolverIterations; }, | ||
1290 | (s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ), | ||
1291 | |||
1292 | new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)", | ||
1293 | 0f, | ||
1294 | (s,cf,p,v) => { s.m_params[0].physicsLoggingFrames = cf.GetInt(p, (int)v); }, | ||
1295 | (s) => { return (float)s.m_params[0].physicsLoggingFrames; }, | ||
1296 | (s,p,l,v) => { s.m_params[0].physicsLoggingFrames = (int)v; } ), | ||
1297 | }; | ||
1298 | |||
1299 | // Convert a boolean to our numeric true and false values | ||
1300 | public float NumericBool(bool b) | ||
1301 | { | ||
1302 | return (b ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse); | ||
1303 | } | ||
1304 | |||
1305 | // Convert numeric true and false values to a boolean | ||
1306 | public bool BoolNumeric(float b) | ||
1307 | { | ||
1308 | return (b == ConfigurationParameters.numericTrue ? true : false); | ||
1309 | } | ||
1310 | |||
1311 | // Search through the parameter definitions and return the matching | ||
1312 | // ParameterDefn structure. | ||
1313 | // Case does not matter as names are compared after converting to lower case. | ||
1314 | // Returns 'false' if the parameter is not found. | ||
1315 | private bool TryGetParameter(string paramName, out ParameterDefn defn) | ||
1316 | { | ||
1317 | bool ret = false; | ||
1318 | ParameterDefn foundDefn = new ParameterDefn(); | ||
1319 | string pName = paramName.ToLower(); | ||
1320 | |||
1321 | foreach (ParameterDefn parm in ParameterDefinitions) | ||
1322 | { | ||
1323 | if (pName == parm.name.ToLower()) | ||
1324 | { | ||
1325 | foundDefn = parm; | ||
1326 | ret = true; | ||
1327 | break; | ||
1328 | } | ||
1329 | } | ||
1330 | defn = foundDefn; | ||
1331 | return ret; | ||
1332 | } | ||
1333 | |||
1334 | // Pass through the settable parameters and set the default values | ||
1335 | private void SetParameterDefaultValues() | ||
1336 | { | ||
1337 | foreach (ParameterDefn parm in ParameterDefinitions) | ||
1338 | { | ||
1339 | parm.setter(this, parm.name, PhysParameterEntry.APPLY_TO_NONE, parm.defaultValue); | ||
1340 | } | ||
1341 | } | ||
1342 | |||
1343 | // Get user set values out of the ini file. | ||
1344 | private void SetParameterConfigurationValues(IConfig cfg) | ||
1345 | { | ||
1346 | foreach (ParameterDefn parm in ParameterDefinitions) | ||
1347 | { | ||
1348 | parm.userParam(this, cfg, parm.name, parm.defaultValue); | ||
1349 | } | ||
1350 | } | ||
1351 | |||
1352 | private PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1]; | ||
1353 | |||
1354 | // This creates an array in the correct format for returning the list of | ||
1355 | // parameters. This is used by the 'list' option of the 'physics' command. | ||
1356 | private void BuildParameterTable() | ||
1357 | { | ||
1358 | if (SettableParameters.Length < ParameterDefinitions.Length) | ||
1359 | { | ||
1360 | List<PhysParameterEntry> entries = new List<PhysParameterEntry>(); | ||
1361 | for (int ii = 0; ii < ParameterDefinitions.Length; ii++) | ||
1362 | { | ||
1363 | ParameterDefn pd = ParameterDefinitions[ii]; | ||
1364 | entries.Add(new PhysParameterEntry(pd.name, pd.desc)); | ||
1365 | } | ||
1366 | |||
1367 | // make the list in alphabetical order for estetic reasons | ||
1368 | entries.Sort(delegate(PhysParameterEntry ppe1, PhysParameterEntry ppe2) | ||
1369 | { | ||
1370 | return ppe1.name.CompareTo(ppe2.name); | ||
1371 | }); | ||
1372 | |||
1373 | SettableParameters = entries.ToArray(); | ||
1374 | } | ||
1375 | } | ||
1376 | |||
1377 | |||
1378 | #region IPhysicsParameters | 849 | #region IPhysicsParameters |
1379 | // Get the list of parameters this physics engine supports | 850 | // Get the list of parameters this physics engine supports |
1380 | public PhysParameterEntry[] GetParameterList() | 851 | public PhysParameterEntry[] GetParameterList() |
1381 | { | 852 | { |
1382 | BuildParameterTable(); | 853 | BSParam.BuildParameterTable(); |
1383 | return SettableParameters; | 854 | return BSParam.SettableParameters; |
1384 | } | 855 | } |
1385 | 856 | ||
1386 | // Set parameter on a specific or all instances. | 857 | // Set parameter on a specific or all instances. |
@@ -1392,8 +863,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
1392 | public bool SetPhysicsParameter(string parm, float val, uint localID) | 863 | public bool SetPhysicsParameter(string parm, float val, uint localID) |
1393 | { | 864 | { |
1394 | bool ret = false; | 865 | bool ret = false; |
1395 | ParameterDefn theParam; | 866 | BSParam.ParameterDefn theParam; |
1396 | if (TryGetParameter(parm, out theParam)) | 867 | if (BSParam.TryGetParameter(parm, out theParam)) |
1397 | { | 868 | { |
1398 | theParam.setter(this, parm, localID, val); | 869 | theParam.setter(this, parm, localID, val); |
1399 | ret = true; | 870 | ret = true; |
@@ -1405,19 +876,20 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
1405 | // If the local ID is APPLY_TO_NONE, just change the default value | 876 | // If the local ID is APPLY_TO_NONE, just change the default value |
1406 | // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs | 877 | // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs |
1407 | // If the localID is a specific object, apply the parameter change to only that object | 878 | // If the localID is a specific object, apply the parameter change to only that object |
1408 | private void UpdateParameterObject(ref float defaultLoc, string parm, uint localID, float val) | 879 | internal delegate void AssignVal(float x); |
880 | internal void UpdateParameterObject(AssignVal setDefault, string parm, uint localID, float val) | ||
1409 | { | 881 | { |
1410 | List<uint> objectIDs = new List<uint>(); | 882 | List<uint> objectIDs = new List<uint>(); |
1411 | switch (localID) | 883 | switch (localID) |
1412 | { | 884 | { |
1413 | case PhysParameterEntry.APPLY_TO_NONE: | 885 | case PhysParameterEntry.APPLY_TO_NONE: |
1414 | defaultLoc = val; // setting only the default value | 886 | setDefault(val); // setting only the default value |
1415 | // This will cause a call into the physical world if some operation is specified (SetOnObject). | 887 | // This will cause a call into the physical world if some operation is specified (SetOnObject). |
1416 | objectIDs.Add(TERRAIN_ID); | 888 | objectIDs.Add(TERRAIN_ID); |
1417 | TaintedUpdateParameter(parm, objectIDs, val); | 889 | TaintedUpdateParameter(parm, objectIDs, val); |
1418 | break; | 890 | break; |
1419 | case PhysParameterEntry.APPLY_TO_ALL: | 891 | case PhysParameterEntry.APPLY_TO_ALL: |
1420 | defaultLoc = val; // setting ALL also sets the default value | 892 | setDefault(val); // setting ALL also sets the default value |
1421 | lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys); | 893 | lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys); |
1422 | TaintedUpdateParameter(parm, objectIDs, val); | 894 | TaintedUpdateParameter(parm, objectIDs, val); |
1423 | break; | 895 | break; |
@@ -1436,8 +908,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
1436 | List<uint> xlIDs = lIDs; | 908 | List<uint> xlIDs = lIDs; |
1437 | string xparm = parm; | 909 | string xparm = parm; |
1438 | TaintedObject("BSScene.UpdateParameterSet", delegate() { | 910 | TaintedObject("BSScene.UpdateParameterSet", delegate() { |
1439 | ParameterDefn thisParam; | 911 | BSParam.ParameterDefn thisParam; |
1440 | if (TryGetParameter(xparm, out thisParam)) | 912 | if (BSParam.TryGetParameter(xparm, out thisParam)) |
1441 | { | 913 | { |
1442 | if (thisParam.onObject != null) | 914 | if (thisParam.onObject != null) |
1443 | { | 915 | { |
@@ -1458,8 +930,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
1458 | { | 930 | { |
1459 | float val = 0f; | 931 | float val = 0f; |
1460 | bool ret = false; | 932 | bool ret = false; |
1461 | ParameterDefn theParam; | 933 | BSParam.ParameterDefn theParam; |
1462 | if (TryGetParameter(parm, out theParam)) | 934 | if (BSParam.TryGetParameter(parm, out theParam)) |
1463 | { | 935 | { |
1464 | val = theParam.getter(this); | 936 | val = theParam.getter(this); |
1465 | ret = true; | 937 | ret = true; |
@@ -1470,24 +942,12 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters | |||
1470 | 942 | ||
1471 | #endregion IPhysicsParameters | 943 | #endregion IPhysicsParameters |
1472 | 944 | ||
1473 | #endregion Runtime settable parameters | ||
1474 | |||
1475 | // Debugging routine for dumping detailed physical information for vehicle prims | ||
1476 | private void DumpVehicles() | ||
1477 | { | ||
1478 | foreach (BSPrim prim in m_vehicles) | ||
1479 | { | ||
1480 | BulletSimAPI.DumpRigidBody2(World.ptr, prim.PhysBody.ptr); | ||
1481 | BulletSimAPI.DumpCollisionShape2(World.ptr, prim.PhysShape.ptr); | ||
1482 | } | ||
1483 | } | ||
1484 | |||
1485 | // Invoke the detailed logger and output something if it's enabled. | 945 | // Invoke the detailed logger and output something if it's enabled. |
1486 | public void DetailLog(string msg, params Object[] args) | 946 | public void DetailLog(string msg, params Object[] args) |
1487 | { | 947 | { |
1488 | PhysicsLogging.Write(msg, args); | 948 | PhysicsLogging.Write(msg, args); |
1489 | // Add the Flush() if debugging crashes. Gets all the messages written out. | 949 | // Add the Flush() if debugging crashes. Gets all the messages written out. |
1490 | // PhysicsLogging.Flush(); | 950 | if (m_physicsLoggingDoFlush) PhysicsLogging.Flush(); |
1491 | } | 951 | } |
1492 | // Used to fill in the LocalID when there isn't one. It's the correct number of characters. | 952 | // Used to fill in the LocalID when there isn't one. It's the correct number of characters. |
1493 | public const string DetailLogZero = "0000000000"; | 953 | public const string DetailLogZero = "0000000000"; |