aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs
diff options
context:
space:
mode:
authorDiva Canto2015-08-31 13:02:51 -0700
committerDiva Canto2015-08-31 13:02:51 -0700
commit134d4300f0b6e9b0df0326cfe0b5df9f41f42865 (patch)
tree86420f55292ec2f566c10b30cdb52a54e6c0f14c /OpenSim/Region/PhysicsModules/BulletS/BSScene.cs
parentFixed a namespace dependency. Also started preparing prebuild.xml for making ... (diff)
downloadopensim-SC-134d4300f0b6e9b0df0326cfe0b5df9f41f42865.zip
opensim-SC-134d4300f0b6e9b0df0326cfe0b5df9f41f42865.tar.gz
opensim-SC-134d4300f0b6e9b0df0326cfe0b5df9f41f42865.tar.bz2
opensim-SC-134d4300f0b6e9b0df0326cfe0b5df9f41f42865.tar.xz
All physics plugins are now region modules. Compiles but doesn't run.
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/PhysicsModules/BulletS/BSScene.cs1995
1 files changed, 1020 insertions, 975 deletions
diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs
index 80f98ef..26af343 100644
--- a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs
+++ b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs
@@ -33,1249 +33,1294 @@ using System.Text;
33using System.Threading; 33using System.Threading;
34using OpenSim.Framework; 34using OpenSim.Framework;
35using OpenSim.Framework.Monitoring; 35using OpenSim.Framework.Monitoring;
36using OpenSim.Region.Framework; 36using OpenSim.Region.Framework.Scenes;
37using OpenSim.Region.CoreModules; 37using OpenSim.Region.Framework.Interfaces;
38using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging;
39using OpenSim.Region.PhysicsModules.SharedBase; 38using OpenSim.Region.PhysicsModules.SharedBase;
40using Nini.Config; 39using Nini.Config;
41using log4net; 40using log4net;
42using OpenMetaverse; 41using OpenMetaverse;
42using Mono.Addins;
43 43
44namespace OpenSim.Region.PhysicsModule.BulletS 44namespace OpenSim.Region.PhysicsModule.BulletS
45{ 45{
46public sealed class BSScene : PhysicsScene, IPhysicsParameters 46 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "BulletSPhysicsScene")]
47{ 47 public sealed class BSScene : PhysicsScene, IPhysicsParameters, INonSharedRegionModule
48 internal static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 48 {
49 internal static readonly string LogHeader = "[BULLETS SCENE]"; 49 internal static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
50 internal static readonly string LogHeader = "[BULLETS SCENE]";
50 51
51 // The name of the region we're working for. 52 private bool m_Enabled = false;
52 public string RegionName { get; private set; } 53 private IConfigSource m_Config;
53 54
54 public string BulletSimVersion = "?"; 55 // The name of the region we're working for.
56 public string RegionName { get; private set; }
55 57
56 // The handle to the underlying managed or unmanaged version of Bullet being used. 58 public string BulletSimVersion = "?";
57 public string BulletEngineName { get; private set; }
58 public BSAPITemplate PE;
59 59
60 // If the physics engine is running on a separate thread 60 // The handle to the underlying managed or unmanaged version of Bullet being used.
61 public Thread m_physicsThread; 61 public string BulletEngineName { get; private set; }
62 public BSAPITemplate PE;
62 63
63 public Dictionary<uint, BSPhysObject> PhysObjects; 64 // If the physics engine is running on a separate thread
64 public BSShapeCollection Shapes; 65 public Thread m_physicsThread;
65 66
66 // Keeping track of the objects with collisions so we can report begin and end of a collision 67 public Dictionary<uint, BSPhysObject> PhysObjects;
67 public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>(); 68 public BSShapeCollection Shapes;
68 public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>();
69 69
70 // All the collision processing is protected with this lock object 70 // Keeping track of the objects with collisions so we can report begin and end of a collision
71 public Object CollisionLock = new Object(); 71 public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>();
72 public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>();
72 73
73 // Properties are updated here 74 // All the collision processing is protected with this lock object
74 public Object UpdateLock = new Object(); 75 public Object CollisionLock = new Object();
75 public HashSet<BSPhysObject> ObjectsWithUpdates = new HashSet<BSPhysObject>();
76 76
77 // Keep track of all the avatars so we can send them a collision event 77 // Properties are updated here
78 // every tick so OpenSim will update its animation. 78 public Object UpdateLock = new Object();
79 private HashSet<BSPhysObject> AvatarsInScene = new HashSet<BSPhysObject>(); 79 public HashSet<BSPhysObject> ObjectsWithUpdates = new HashSet<BSPhysObject>();
80 private Object AvatarsInSceneLock = new Object();
81 80
82 // let my minuions use my logger 81 // Keep track of all the avatars so we can send them a collision event
83 public ILog Logger { get { return m_log; } } 82 // every tick so OpenSim will update its animation.
83 private HashSet<BSPhysObject> AvatarsInScene = new HashSet<BSPhysObject>();
84 private Object AvatarsInSceneLock = new Object();
84 85
85 public IMesher mesher; 86 // let my minuions use my logger
86 public uint WorldID { get; private set; } 87 public ILog Logger { get { return m_log; } }
87 public BulletWorld World { get; private set; }
88 88
89 // All the constraints that have been allocated in this instance. 89 public IMesher mesher;
90 public BSConstraintCollection Constraints { get; private set; } 90 public uint WorldID { get; private set; }
91 public BulletWorld World { get; private set; }
91 92
92 // Simulation parameters 93 // All the constraints that have been allocated in this instance.
93 //internal float m_physicsStepTime; // if running independently, the interval simulated by default 94 public BSConstraintCollection Constraints { get; private set; }
94 95
95 internal int m_maxSubSteps; 96 // Simulation parameters
96 internal float m_fixedTimeStep; 97 //internal float m_physicsStepTime; // if running independently, the interval simulated by default
97 98
98 internal float m_simulatedTime; // the time simulated previously. Used for physics framerate calc. 99 internal int m_maxSubSteps;
100 internal float m_fixedTimeStep;
99 101
100 internal long m_simulationStep = 0; // The current simulation step. 102 internal float m_simulatedTime; // the time simulated previously. Used for physics framerate calc.
101 public long SimulationStep { get { return m_simulationStep; } }
102 // A number to use for SimulationStep that is probably not any step value
103 // Used by the collision code (which remembers the step when a collision happens) to remember not any simulation step.
104 public static long NotASimulationStep = -1234;
105 103
106 internal float LastTimeStep { get; private set; } // The simulation time from the last invocation of Simulate() 104 internal long m_simulationStep = 0; // The current simulation step.
105 public long SimulationStep { get { return m_simulationStep; } }
106 // A number to use for SimulationStep that is probably not any step value
107 // Used by the collision code (which remembers the step when a collision happens) to remember not any simulation step.
108 public static long NotASimulationStep = -1234;
107 109
108 internal float NominalFrameRate { get; set; } // Parameterized ideal frame rate that simulation is scaled to 110 internal float LastTimeStep { get; private set; } // The simulation time from the last invocation of Simulate()
109 111
110 // Physical objects can register for prestep or poststep events 112 internal float NominalFrameRate { get; set; } // Parameterized ideal frame rate that simulation is scaled to
111 public delegate void PreStepAction(float timeStep);
112 public delegate void PostStepAction(float timeStep);
113 public event PreStepAction BeforeStep;
114 public event PostStepAction AfterStep;
115 113
116 // A value of the time 'now' so all the collision and update routines do not have to get their own 114 // Physical objects can register for prestep or poststep events
117 // Set to 'now' just before all the prims and actors are called for collisions and updates 115 public delegate void PreStepAction(float timeStep);
118 public int SimulationNowTime { get; private set; } 116 public delegate void PostStepAction(float timeStep);
117 public event PreStepAction BeforeStep;
118 public event PostStepAction AfterStep;
119 119
120 // True if initialized and ready to do simulation steps 120 // A value of the time 'now' so all the collision and update routines do not have to get their own
121 private bool m_initialized = false; 121 // Set to 'now' just before all the prims and actors are called for collisions and updates
122 public int SimulationNowTime { get; private set; }
122 123
123 // Flag which is true when processing taints. 124 // True if initialized and ready to do simulation steps
124 // Not guaranteed to be correct all the time (don't depend on this) but good for debugging. 125 private bool m_initialized = false;
125 public bool InTaintTime { get; private set; }
126 126
127 // Pinned memory used to pass step information between managed and unmanaged 127 // Flag which is true when processing taints.
128 internal int m_maxCollisionsPerFrame; 128 // Not guaranteed to be correct all the time (don't depend on this) but good for debugging.
129 internal CollisionDesc[] m_collisionArray; 129 public bool InTaintTime { get; private set; }
130 130
131 internal int m_maxUpdatesPerFrame; 131 // Pinned memory used to pass step information between managed and unmanaged
132 internal EntityProperties[] m_updateArray; 132 internal int m_maxCollisionsPerFrame;
133 internal CollisionDesc[] m_collisionArray;
133 134
134 /// <summary> 135 internal int m_maxUpdatesPerFrame;
135 /// Used to control physics simulation timing if Bullet is running on its own thread. 136 internal EntityProperties[] m_updateArray;
136 /// </summary>
137 private ManualResetEvent m_updateWaitEvent;
138 137
139 public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero 138 /// <summary>
140 public const uint GROUNDPLANE_ID = 1; 139 /// Used to control physics simulation timing if Bullet is running on its own thread.
141 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here 140 /// </summary>
141 private ManualResetEvent m_updateWaitEvent;
142 142
143 public float SimpleWaterLevel { get; set; } 143 public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero
144 public BSTerrainManager TerrainManager { get; private set; } 144 public const uint GROUNDPLANE_ID = 1;
145 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
145 146
146 public ConfigurationParameters Params 147 public float SimpleWaterLevel { get; set; }
147 { 148 public BSTerrainManager TerrainManager { get; private set; }
148 get { return UnmanagedParams[0]; }
149 }
150 public Vector3 DefaultGravity
151 {
152 get { return new Vector3(0f, 0f, Params.gravity); }
153 }
154 // Just the Z value of the gravity
155 public float DefaultGravityZ
156 {
157 get { return Params.gravity; }
158 }
159 149
160 // When functions in the unmanaged code must be called, it is only 150 public ConfigurationParameters Params
161 // done at a known time just before the simulation step. The taint
162 // system saves all these function calls and executes them in
163 // order before the simulation.
164 public delegate void TaintCallback();
165 private struct TaintCallbackEntry
166 {
167 public String originator;
168 public String ident;
169 public TaintCallback callback;
170 public TaintCallbackEntry(string pIdent, TaintCallback pCallBack)
171 { 151 {
172 originator = BSScene.DetailLogZero; 152 get { return UnmanagedParams[0]; }
173 ident = pIdent;
174 callback = pCallBack;
175 } 153 }
176 public TaintCallbackEntry(string pOrigin, string pIdent, TaintCallback pCallBack) 154 public Vector3 DefaultGravity
177 { 155 {
178 originator = pOrigin; 156 get { return new Vector3(0f, 0f, Params.gravity); }
179 ident = pIdent; 157 }
180 callback = pCallBack; 158 // Just the Z value of the gravity
159 public float DefaultGravityZ
160 {
161 get { return Params.gravity; }
181 } 162 }
182 }
183 private Object _taintLock = new Object(); // lock for using the next object
184 private List<TaintCallbackEntry> _taintOperations;
185 private Dictionary<string, TaintCallbackEntry> _postTaintOperations;
186 private List<TaintCallbackEntry> _postStepOperations;
187
188 // A pointer to an instance if this structure is passed to the C++ code
189 // Used to pass basic configuration values to the unmanaged code.
190 internal ConfigurationParameters[] UnmanagedParams;
191
192 // Sometimes you just have to log everything.
193 public Logging.LogWriter PhysicsLogging;
194 private bool m_physicsLoggingEnabled;
195 private string m_physicsLoggingDir;
196 private string m_physicsLoggingPrefix;
197 private int m_physicsLoggingFileMinutes;
198 private bool m_physicsLoggingDoFlush;
199 private bool m_physicsPhysicalDumpEnabled;
200 public int PhysicsMetricDumpFrames { get; set; }
201 // 'true' of the vehicle code is to log lots of details
202 public bool VehicleLoggingEnabled { get; private set; }
203 public bool VehiclePhysicalLoggingEnabled { get; private set; }
204
205 #region Construction and Initialization
206 public BSScene(string engineType, string identifier)
207 {
208 m_initialized = false;
209
210 // The name of the region we're working for is passed to us. Keep for identification.
211 RegionName = identifier;
212
213 // Set identifying variables in the PhysicsScene interface.
214 EngineType = engineType;
215 Name = EngineType + "/" + RegionName;
216 }
217
218 // Old version of initialization that assumes legacy sized regions (256x256)
219 public override void Initialise(IMesher meshmerizer, IConfigSource config)
220 {
221 m_log.ErrorFormat("{0} WARNING WARNING WARNING! BulletSim initialized without region extent specification. Terrain will be messed up.");
222 Vector3 regionExtent = new Vector3( Constants.RegionSize, Constants.RegionSize, Constants.RegionSize);
223 Initialise(meshmerizer, config, regionExtent);
224
225 }
226 163
227 public override void Initialise(IMesher meshmerizer, IConfigSource config, Vector3 regionExtent) 164 // When functions in the unmanaged code must be called, it is only
228 { 165 // done at a known time just before the simulation step. The taint
229 mesher = meshmerizer; 166 // system saves all these function calls and executes them in
230 _taintOperations = new List<TaintCallbackEntry>(); 167 // order before the simulation.
231 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>(); 168 public delegate void TaintCallback();
232 _postStepOperations = new List<TaintCallbackEntry>(); 169 private struct TaintCallbackEntry
233 PhysObjects = new Dictionary<uint, BSPhysObject>(); 170 {
234 Shapes = new BSShapeCollection(this); 171 public String originator;
172 public String ident;
173 public TaintCallback callback;
174 public TaintCallbackEntry(string pIdent, TaintCallback pCallBack)
175 {
176 originator = BSScene.DetailLogZero;
177 ident = pIdent;
178 callback = pCallBack;
179 }
180 public TaintCallbackEntry(string pOrigin, string pIdent, TaintCallback pCallBack)
181 {
182 originator = pOrigin;
183 ident = pIdent;
184 callback = pCallBack;
185 }
186 }
187 private Object _taintLock = new Object(); // lock for using the next object
188 private List<TaintCallbackEntry> _taintOperations;
189 private Dictionary<string, TaintCallbackEntry> _postTaintOperations;
190 private List<TaintCallbackEntry> _postStepOperations;
191
192 // A pointer to an instance if this structure is passed to the C++ code
193 // Used to pass basic configuration values to the unmanaged code.
194 internal ConfigurationParameters[] UnmanagedParams;
195
196 // Sometimes you just have to log everything.
197 public LogWriter PhysicsLogging;
198 private bool m_physicsLoggingEnabled;
199 private string m_physicsLoggingDir;
200 private string m_physicsLoggingPrefix;
201 private int m_physicsLoggingFileMinutes;
202 private bool m_physicsLoggingDoFlush;
203 private bool m_physicsPhysicalDumpEnabled;
204 public int PhysicsMetricDumpFrames { get; set; }
205 // 'true' of the vehicle code is to log lots of details
206 public bool VehicleLoggingEnabled { get; private set; }
207 public bool VehiclePhysicalLoggingEnabled { get; private set; }
208
209 #region INonSharedRegionModule
210 public string Name
211 {
212 get { return "BulletSim"; }
213 }
235 214
236 m_simulatedTime = 0f; 215 public Type ReplaceableInterface
237 LastTimeStep = 0.1f; 216 {
217 get { return null; }
218 }
238 219
239 // Allocate pinned memory to pass parameters. 220 public void Initialise(IConfigSource source)
240 UnmanagedParams = new ConfigurationParameters[1]; 221 {
222 // TODO: Move this out of Startup
223 IConfig config = source.Configs["Startup"];
224 if (config != null)
225 {
226 string physics = config.GetString("physics", string.Empty);
227 if (physics == Name)
228 {
229 m_Enabled = true;
230 m_Config = source;
231 }
232 }
241 233
242 // Set default values for physics parameters plus any overrides from the ini file 234 }
243 GetInitialParameterValues(config);
244 235
245 // Force some parameters to values depending on other configurations 236 public void Close()
246 // Only use heightmap terrain implementation if terrain larger than legacy size
247 if ((uint)regionExtent.X > Constants.RegionSize || (uint)regionExtent.Y > Constants.RegionSize)
248 { 237 {
249 m_log.WarnFormat("{0} Forcing terrain implementation to heightmap for large region", LogHeader);
250 BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap;
251 } 238 }
252 239
253 // Get the connection to the physics engine (could be native or one of many DLLs) 240 public void AddRegion(Scene scene)
254 PE = SelectUnderlyingBulletEngine(BulletEngineName);
255
256 // Enable very detailed logging.
257 // By creating an empty logger when not logging, the log message invocation code
258 // can be left in and every call doesn't have to check for null.
259 if (m_physicsLoggingEnabled)
260 { 241 {
261 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes, m_physicsLoggingDoFlush); 242 if (!m_Enabled)
262 PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output its own error messages. 243 return;
244
245 EngineType = Name;
246 RegionName = scene.RegionInfo.RegionName;
247 PhysicsSceneName = EngineType + "/" + RegionName;
248
249 scene.RegisterModuleInterface<PhysicsScene>(this);
250 Vector3 extent = new Vector3(scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY, scene.RegionInfo.RegionSizeZ);
251 Initialise(m_Config, extent);
263 } 252 }
264 else 253
254 public void RemoveRegion(Scene scene)
265 { 255 {
266 PhysicsLogging = new Logging.LogWriter(); 256 if (!m_Enabled)
257 return;
267 } 258 }
268 259
269 // Allocate memory for returning of the updates and collisions from the physics engine 260 public void RegionLoaded(Scene scene)
270 m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame]; 261 {
271 m_updateArray = new EntityProperties[m_maxUpdatesPerFrame]; 262 if (!m_Enabled)
272 263 return;
273 // The bounding box for the simulated world. The origin is 0,0,0 unless we're
274 // a child in a mega-region.
275 // Bullet actually doesn't care about the extents of the simulated
276 // area. It tracks active objects no matter where they are.
277 Vector3 worldExtent = regionExtent;
278 264
279 World = PE.Initialize(worldExtent, Params, m_maxCollisionsPerFrame, ref m_collisionArray, m_maxUpdatesPerFrame, ref m_updateArray); 265 mesher = scene.RequestModuleInterface<IMesher>();
266 if (mesher == null)
267 m_log.WarnFormat("{0} No mesher. Things will not work well.", LogHeader);
268 }
269 #endregion
280 270
281 Constraints = new BSConstraintCollection(World); 271 #region Initialization
282 272
283 TerrainManager = new BSTerrainManager(this, worldExtent); 273 private void Initialise(IConfigSource config, Vector3 regionExtent)
284 TerrainManager.CreateInitialGroundPlaneAndTerrain(); 274 {
275 _taintOperations = new List<TaintCallbackEntry>();
276 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
277 _postStepOperations = new List<TaintCallbackEntry>();
278 PhysObjects = new Dictionary<uint, BSPhysObject>();
279 Shapes = new BSShapeCollection(this);
285 280
286 // Put some informational messages into the log file. 281 m_simulatedTime = 0f;
287 m_log.InfoFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation); 282 LastTimeStep = 0.1f;
288 283
289 InTaintTime = false; 284 // Allocate pinned memory to pass parameters.
290 m_initialized = true; 285 UnmanagedParams = new ConfigurationParameters[1];
291 286
292 // If the physics engine runs on its own thread, start same. 287 // Set default values for physics parameters plus any overrides from the ini file
293 if (BSParam.UseSeparatePhysicsThread) 288 GetInitialParameterValues(config);
294 {
295 // The physics simulation should happen independently of the heartbeat loop
296 m_physicsThread
297 = WorkManager.StartThread(
298 BulletSPluginPhysicsThread,
299 string.Format("{0} ({1})", BulletEngineName, RegionName),
300 ThreadPriority.Normal,
301 true,
302 true);
303 }
304 }
305 289
306 // All default parameter values are set here. There should be no values set in the 290 // Force some parameters to values depending on other configurations
307 // variable definitions. 291 // Only use heightmap terrain implementation if terrain larger than legacy size
308 private void GetInitialParameterValues(IConfigSource config) 292 if ((uint)regionExtent.X > Constants.RegionSize || (uint)regionExtent.Y > Constants.RegionSize)
309 { 293 {
310 ConfigurationParameters parms = new ConfigurationParameters(); 294 m_log.WarnFormat("{0} Forcing terrain implementation to heightmap for large region", LogHeader);
311 UnmanagedParams[0] = parms; 295 BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap;
296 }
312 297
313 BSParam.SetParameterDefaultValues(this); 298 // Get the connection to the physics engine (could be native or one of many DLLs)
299 PE = SelectUnderlyingBulletEngine(BulletEngineName);
314 300
315 if (config != null) 301 // Enable very detailed logging.
316 { 302 // By creating an empty logger when not logging, the log message invocation code
317 // If there are specifications in the ini file, use those values 303 // can be left in and every call doesn't have to check for null.
318 IConfig pConfig = config.Configs["BulletSim"]; 304 if (m_physicsLoggingEnabled)
319 if (pConfig != null)
320 { 305 {
321 BSParam.SetParameterConfigurationValues(this, pConfig); 306 PhysicsLogging = new LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes, m_physicsLoggingDoFlush);
322 307 PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output its own error messages.
323 // There are two Bullet implementations to choose from
324 BulletEngineName = pConfig.GetString("BulletEngine", "BulletUnmanaged");
325
326 // Very detailed logging for physics debugging
327 // TODO: the boolean values can be moved to the normal parameter processing.
328 m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false);
329 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
330 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
331 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
332 m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
333 m_physicsPhysicalDumpEnabled = pConfig.GetBoolean("PhysicsPhysicalDumpEnabled", false);
334 // Very detailed logging for vehicle debugging
335 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
336 VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);
337
338 // Do any replacements in the parameters
339 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
340 } 308 }
341 else 309 else
342 { 310 {
343 // Nothing in the configuration INI file so assume unmanaged and other defaults. 311 PhysicsLogging = new LogWriter();
344 BulletEngineName = "BulletUnmanaged";
345 m_physicsLoggingEnabled = false;
346 VehicleLoggingEnabled = false;
347 } 312 }
348 313
349 // The material characteristics. 314 // Allocate memory for returning of the updates and collisions from the physics engine
350 BSMaterials.InitializeFromDefaults(Params); 315 m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame];
351 if (pConfig != null) 316 m_updateArray = new EntityProperties[m_maxUpdatesPerFrame];
317
318 // The bounding box for the simulated world. The origin is 0,0,0 unless we're
319 // a child in a mega-region.
320 // Bullet actually doesn't care about the extents of the simulated
321 // area. It tracks active objects no matter where they are.
322 Vector3 worldExtent = regionExtent;
323
324 World = PE.Initialize(worldExtent, Params, m_maxCollisionsPerFrame, ref m_collisionArray, m_maxUpdatesPerFrame, ref m_updateArray);
325
326 Constraints = new BSConstraintCollection(World);
327
328 TerrainManager = new BSTerrainManager(this, worldExtent);
329 TerrainManager.CreateInitialGroundPlaneAndTerrain();
330
331 // Put some informational messages into the log file.
332 m_log.InfoFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation);
333
334 InTaintTime = false;
335 m_initialized = true;
336
337 // If the physics engine runs on its own thread, start same.
338 if (BSParam.UseSeparatePhysicsThread)
352 { 339 {
353 // Let the user add new and interesting material property values. 340 // The physics simulation should happen independently of the heartbeat loop
354 BSMaterials.InitializefromParameters(pConfig); 341 m_physicsThread
342 = WorkManager.StartThread(
343 BulletSPluginPhysicsThread,
344 string.Format("{0} ({1})", BulletEngineName, RegionName),
345 ThreadPriority.Normal,
346 true,
347 true);
355 } 348 }
356 } 349 }
357 }
358 350
359 // A helper function that handles a true/false parameter and returns the proper float number encoding 351 // All default parameter values are set here. There should be no values set in the
360 float ParamBoolean(IConfig config, string parmName, float deflt) 352 // variable definitions.
361 { 353 private void GetInitialParameterValues(IConfigSource config)
362 float ret = deflt;
363 if (config.Contains(parmName))
364 { 354 {
365 ret = ConfigurationParameters.numericFalse; 355 ConfigurationParameters parms = new ConfigurationParameters();
366 if (config.GetBoolean(parmName, false)) 356 UnmanagedParams[0] = parms;
367 {
368 ret = ConfigurationParameters.numericTrue;
369 }
370 }
371 return ret;
372 }
373 357
374 // Select the connection to the actual Bullet implementation. 358 BSParam.SetParameterDefaultValues(this);
375 // The main engine selection is the engineName up to the first hypen.
376 // So "Bullet-2.80-OpenCL-Intel" specifies the 'bullet' class here and the whole name
377 // is passed to the engine to do its special selection, etc.
378 private BSAPITemplate SelectUnderlyingBulletEngine(string engineName)
379 {
380 // For the moment, do a simple switch statement.
381 // Someday do fancyness with looking up the interfaces in the assembly.
382 BSAPITemplate ret = null;
383 359
384 string selectionName = engineName.ToLower(); 360 if (config != null)
385 int hyphenIndex = engineName.IndexOf("-"); 361 {
386 if (hyphenIndex > 0) 362 // If there are specifications in the ini file, use those values
387 selectionName = engineName.ToLower().Substring(0, hyphenIndex - 1); 363 IConfig pConfig = config.Configs["BulletSim"];
364 if (pConfig != null)
365 {
366 BSParam.SetParameterConfigurationValues(this, pConfig);
367
368 // There are two Bullet implementations to choose from
369 BulletEngineName = pConfig.GetString("BulletEngine", "BulletUnmanaged");
370
371 // Very detailed logging for physics debugging
372 // TODO: the boolean values can be moved to the normal parameter processing.
373 m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false);
374 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
375 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
376 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
377 m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
378 m_physicsPhysicalDumpEnabled = pConfig.GetBoolean("PhysicsPhysicalDumpEnabled", false);
379 // Very detailed logging for vehicle debugging
380 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
381 VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);
382
383 // Do any replacements in the parameters
384 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
385 }
386 else
387 {
388 // Nothing in the configuration INI file so assume unmanaged and other defaults.
389 BulletEngineName = "BulletUnmanaged";
390 m_physicsLoggingEnabled = false;
391 VehicleLoggingEnabled = false;
392 }
388 393
389 switch (selectionName) 394 // The material characteristics.
390 { 395 BSMaterials.InitializeFromDefaults(Params);
391 case "bullet": 396 if (pConfig != null)
392 case "bulletunmanaged": 397 {
393 ret = new BSAPIUnman(engineName, this); 398 // Let the user add new and interesting material property values.
394 break; 399 BSMaterials.InitializefromParameters(pConfig);
395 case "bulletxna": 400 }
396 ret = new BSAPIXNA(engineName, this); 401 }
397 // Disable some features that are not implemented in BulletXNA
398 m_log.InfoFormat("{0} Disabling some physics features not implemented by BulletXNA", LogHeader);
399 m_log.InfoFormat("{0} Disabling ShouldUseBulletHACD", LogHeader);
400 BSParam.ShouldUseBulletHACD = false;
401 m_log.InfoFormat("{0} Disabling ShouldUseSingleConvexHullForPrims", LogHeader);
402 BSParam.ShouldUseSingleConvexHullForPrims = false;
403 m_log.InfoFormat("{0} Disabling ShouldUseGImpactShapeForPrims", LogHeader);
404 BSParam.ShouldUseGImpactShapeForPrims = false;
405 m_log.InfoFormat("{0} Setting terrain implimentation to Heightmap", LogHeader);
406 BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap;
407 break;
408 } 402 }
409 403
410 if (ret == null) 404 // A helper function that handles a true/false parameter and returns the proper float number encoding
405 float ParamBoolean(IConfig config, string parmName, float deflt)
411 { 406 {
412 m_log.ErrorFormat("{0} COULD NOT SELECT BULLET ENGINE: '[BulletSim]PhysicsEngine' must be either 'BulletUnmanaged-*' or 'BulletXNA-*'", LogHeader); 407 float ret = deflt;
413 } 408 if (config.Contains(parmName))
414 else 409 {
415 { 410 ret = ConfigurationParameters.numericFalse;
416 m_log.InfoFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion); 411 if (config.GetBoolean(parmName, false))
412 {
413 ret = ConfigurationParameters.numericTrue;
414 }
415 }
416 return ret;
417 } 417 }
418 418
419 return ret; 419 // Select the connection to the actual Bullet implementation.
420 } 420 // The main engine selection is the engineName up to the first hypen.
421 // So "Bullet-2.80-OpenCL-Intel" specifies the 'bullet' class here and the whole name
422 // is passed to the engine to do its special selection, etc.
423 private BSAPITemplate SelectUnderlyingBulletEngine(string engineName)
424 {
425 // For the moment, do a simple switch statement.
426 // Someday do fancyness with looking up the interfaces in the assembly.
427 BSAPITemplate ret = null;
421 428
422 public override void Dispose() 429 string selectionName = engineName.ToLower();
423 { 430 int hyphenIndex = engineName.IndexOf("-");
424 // m_log.DebugFormat("{0}: Dispose()", LogHeader); 431 if (hyphenIndex > 0)
432 selectionName = engineName.ToLower().Substring(0, hyphenIndex - 1);
425 433
426 // make sure no stepping happens while we're deleting stuff 434 switch (selectionName)
427 m_initialized = false; 435 {
436 case "bullet":
437 case "bulletunmanaged":
438 ret = new BSAPIUnman(engineName, this);
439 break;
440 case "bulletxna":
441 ret = new BSAPIXNA(engineName, this);
442 // Disable some features that are not implemented in BulletXNA
443 m_log.InfoFormat("{0} Disabling some physics features not implemented by BulletXNA", LogHeader);
444 m_log.InfoFormat("{0} Disabling ShouldUseBulletHACD", LogHeader);
445 BSParam.ShouldUseBulletHACD = false;
446 m_log.InfoFormat("{0} Disabling ShouldUseSingleConvexHullForPrims", LogHeader);
447 BSParam.ShouldUseSingleConvexHullForPrims = false;
448 m_log.InfoFormat("{0} Disabling ShouldUseGImpactShapeForPrims", LogHeader);
449 BSParam.ShouldUseGImpactShapeForPrims = false;
450 m_log.InfoFormat("{0} Setting terrain implimentation to Heightmap", LogHeader);
451 BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap;
452 break;
453 }
428 454
429 lock (PhysObjects) 455 if (ret == null)
430 {
431 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
432 { 456 {
433 kvp.Value.Destroy(); 457 m_log.ErrorFormat("{0} COULD NOT SELECT BULLET ENGINE: '[BulletSim]PhysicsEngine' must be either 'BulletUnmanaged-*' or 'BulletXNA-*'", LogHeader);
458 }
459 else
460 {
461 m_log.InfoFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion);
434 } 462 }
435 PhysObjects.Clear();
436 }
437 463
438 // Now that the prims are all cleaned up, there should be no constraints left 464 return ret;
439 if (Constraints != null)
440 {
441 Constraints.Dispose();
442 Constraints = null;
443 } 465 }
444 466
445 if (Shapes != null) 467 public override void Dispose()
446 { 468 {
447 Shapes.Dispose(); 469 // m_log.DebugFormat("{0}: Dispose()", LogHeader);
448 Shapes = null;
449 }
450 470
451 if (TerrainManager != null) 471 // make sure no stepping happens while we're deleting stuff
452 { 472 m_initialized = false;
453 TerrainManager.ReleaseGroundPlaneAndTerrain();
454 TerrainManager.Dispose();
455 TerrainManager = null;
456 }
457 473
458 // Anything left in the unmanaged code should be cleaned out 474 lock (PhysObjects)
459 PE.Shutdown(World); 475 {
476 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
477 {
478 kvp.Value.Destroy();
479 }
480 PhysObjects.Clear();
481 }
460 482
461 // Not logging any more 483 // Now that the prims are all cleaned up, there should be no constraints left
462 PhysicsLogging.Close(); 484 if (Constraints != null)
463 } 485 {
464 #endregion // Construction and Initialization 486 Constraints.Dispose();
487 Constraints = null;
488 }
465 489
466 #region Prim and Avatar addition and removal 490 if (Shapes != null)
491 {
492 Shapes.Dispose();
493 Shapes = null;
494 }
467 495
468 public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 velocity, Vector3 size, bool isFlying) 496 if (TerrainManager != null)
469 { 497 {
470 m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader); 498 TerrainManager.ReleaseGroundPlaneAndTerrain();
471 return null; 499 TerrainManager.Dispose();
472 } 500 TerrainManager = null;
501 }
473 502
474 public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 velocity, Vector3 size, bool isFlying) 503 // Anything left in the unmanaged code should be cleaned out
475 { 504 PE.Shutdown(World);
476 // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName);
477 505
478 if (!m_initialized) return null; 506 // Not logging any more
507 PhysicsLogging.Close();
508 }
509 #endregion // Construction and Initialization
479 510
480 BSCharacter actor = new BSCharacter(localID, avName, this, position, velocity, size, isFlying); 511 #region Prim and Avatar addition and removal
481 lock (PhysObjects)
482 PhysObjects.Add(localID, actor);
483 512
484 // TODO: Remove kludge someday. 513 public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 velocity, Vector3 size, bool isFlying)
485 // We must generate a collision for avatars whether they collide or not. 514 {
486 // This is required by OpenSim to update avatar animations, etc. 515 m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
487 lock (AvatarsInSceneLock) 516 return null;
488 AvatarsInScene.Add(actor); 517 }
489 518
490 return actor; 519 public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 velocity, Vector3 size, bool isFlying)
491 } 520 {
521 // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName);
492 522
493 public override void RemoveAvatar(PhysicsActor actor) 523 if (!m_initialized) return null;
494 { 524
495 // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader); 525 BSCharacter actor = new BSCharacter(localID, avName, this, position, velocity, size, isFlying);
526 lock (PhysObjects)
527 PhysObjects.Add(localID, actor);
528
529 // TODO: Remove kludge someday.
530 // We must generate a collision for avatars whether they collide or not.
531 // This is required by OpenSim to update avatar animations, etc.
532 lock (AvatarsInSceneLock)
533 AvatarsInScene.Add(actor);
496 534
497 if (!m_initialized) return; 535 return actor;
536 }
498 537
499 BSCharacter bsactor = actor as BSCharacter; 538 public override void RemoveAvatar(PhysicsActor actor)
500 if (bsactor != null)
501 { 539 {
502 try 540 // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader);
541
542 if (!m_initialized) return;
543
544 BSCharacter bsactor = actor as BSCharacter;
545 if (bsactor != null)
503 { 546 {
504 lock (PhysObjects) 547 try
505 PhysObjects.Remove(bsactor.LocalID); 548 {
506 // Remove kludge someday 549 lock (PhysObjects)
507 lock (AvatarsInSceneLock) 550 PhysObjects.Remove(bsactor.LocalID);
508 AvatarsInScene.Remove(bsactor); 551 // Remove kludge someday
552 lock (AvatarsInSceneLock)
553 AvatarsInScene.Remove(bsactor);
554 }
555 catch (Exception e)
556 {
557 m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e);
558 }
559 bsactor.Destroy();
560 // bsactor.dispose();
509 } 561 }
510 catch (Exception e) 562 else
511 { 563 {
512 m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e); 564 m_log.ErrorFormat("{0}: Requested to remove avatar that is not a BSCharacter. ID={1}, type={2}",
565 LogHeader, actor.LocalID, actor.GetType().Name);
513 } 566 }
514 bsactor.Destroy();
515 // bsactor.dispose();
516 }
517 else
518 {
519 m_log.ErrorFormat("{0}: Requested to remove avatar that is not a BSCharacter. ID={1}, type={2}",
520 LogHeader, actor.LocalID, actor.GetType().Name);
521 } 567 }
522 }
523
524 public override void RemovePrim(PhysicsActor prim)
525 {
526 if (!m_initialized) return;
527 568
528 BSPhysObject bsprim = prim as BSPhysObject; 569 public override void RemovePrim(PhysicsActor prim)
529 if (bsprim != null)
530 { 570 {
531 DetailLog("{0},RemovePrim,call", bsprim.LocalID); 571 if (!m_initialized) return;
532 // m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID); 572
533 try 573 BSPhysObject bsprim = prim as BSPhysObject;
574 if (bsprim != null)
534 { 575 {
535 lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID); 576 DetailLog("{0},RemovePrim,call", bsprim.LocalID);
577 // m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID);
578 try
579 {
580 lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID);
581 }
582 catch (Exception e)
583 {
584 m_log.ErrorFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e);
585 }
586 bsprim.Destroy();
587 // bsprim.dispose();
536 } 588 }
537 catch (Exception e) 589 else
538 { 590 {
539 m_log.ErrorFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e); 591 m_log.ErrorFormat("{0}: Attempt to remove prim that is not a BSPrim type.", LogHeader);
540 } 592 }
541 bsprim.Destroy();
542 // bsprim.dispose();
543 }
544 else
545 {
546 m_log.ErrorFormat("{0}: Attempt to remove prim that is not a BSPrim type.", LogHeader);
547 } 593 }
548 }
549 594
550 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position, 595 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
551 Vector3 size, Quaternion rotation, bool isPhysical, uint localID) 596 Vector3 size, Quaternion rotation, bool isPhysical, uint localID)
552 { 597 {
553 // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName); 598 // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName);
554 599
555 if (!m_initialized) return null; 600 if (!m_initialized) return null;
556 601
557 // DetailLog("{0},BSScene.AddPrimShape,call", localID); 602 // DetailLog("{0},BSScene.AddPrimShape,call", localID);
558 603
559 BSPhysObject prim = new BSPrimLinkable(localID, primName, this, position, size, rotation, pbs, isPhysical); 604 BSPhysObject prim = new BSPrimLinkable(localID, primName, this, position, size, rotation, pbs, isPhysical);
560 lock (PhysObjects) PhysObjects.Add(localID, prim); 605 lock (PhysObjects) PhysObjects.Add(localID, prim);
561 return prim; 606 return prim;
562 } 607 }
563 608
564 // This is a call from the simulator saying that some physical property has been updated. 609 // This is a call from the simulator saying that some physical property has been updated.
565 // The BulletSim driver senses the changing of relevant properties so this taint 610 // The BulletSim driver senses the changing of relevant properties so this taint
566 // information call is not needed. 611 // information call is not needed.
567 public override void AddPhysicsActorTaint(PhysicsActor prim) { } 612 public override void AddPhysicsActorTaint(PhysicsActor prim) { }
568 613
569 #endregion // Prim and Avatar addition and removal 614 #endregion // Prim and Avatar addition and removal
570 615
571 #region Simulation 616 #region Simulation
572 617
573 // Call from the simulator to send physics information to the simulator objects. 618 // Call from the simulator to send physics information to the simulator objects.
574 // This pushes all the collision and property update events into the objects in 619 // This pushes all the collision and property update events into the objects in
575 // the simulator and, since it is on the heartbeat thread, there is an implicit 620 // the simulator and, since it is on the heartbeat thread, there is an implicit
576 // locking of those data structures from other heartbeat events. 621 // locking of those data structures from other heartbeat events.
577 // If the physics engine is running on a separate thread, the update information 622 // If the physics engine is running on a separate thread, the update information
578 // will be in the ObjectsWithCollions and ObjectsWithUpdates structures. 623 // will be in the ObjectsWithCollions and ObjectsWithUpdates structures.
579 public override float Simulate(float timeStep) 624 public override float Simulate(float timeStep)
580 {
581 if (!BSParam.UseSeparatePhysicsThread)
582 { 625 {
583 DoPhysicsStep(timeStep); 626 if (!BSParam.UseSeparatePhysicsThread)
627 {
628 DoPhysicsStep(timeStep);
629 }
630 return SendUpdatesToSimulator(timeStep);
584 } 631 }
585 return SendUpdatesToSimulator(timeStep);
586 }
587 632
588 // Call the physics engine to do one 'timeStep' and collect collisions and updates 633 // Call the physics engine to do one 'timeStep' and collect collisions and updates
589 // into ObjectsWithCollisions and ObjectsWithUpdates data structures. 634 // into ObjectsWithCollisions and ObjectsWithUpdates data structures.
590 private void DoPhysicsStep(float timeStep) 635 private void DoPhysicsStep(float timeStep)
591 { 636 {
592 // prevent simulation until we've been initialized 637 // prevent simulation until we've been initialized
593 if (!m_initialized) return; 638 if (!m_initialized) return;
594 639
595 LastTimeStep = timeStep; 640 LastTimeStep = timeStep;
596 641
597 int updatedEntityCount = 0; 642 int updatedEntityCount = 0;
598 int collidersCount = 0; 643 int collidersCount = 0;
599 644
600 int beforeTime = Util.EnvironmentTickCount(); 645 int beforeTime = Util.EnvironmentTickCount();
601 int simTime = 0; 646 int simTime = 0;
602 647
603 int numTaints = _taintOperations.Count; 648 int numTaints = _taintOperations.Count;
604 InTaintTime = true; // Only used for debugging so locking is not necessary. 649 InTaintTime = true; // Only used for debugging so locking is not necessary.
605 650
606 // update the prim states while we know the physics engine is not busy 651 // update the prim states while we know the physics engine is not busy
607 ProcessTaints(); 652 ProcessTaints();
608 653
609 // Some of the physical objects requre individual, pre-step calls 654 // Some of the physical objects requre individual, pre-step calls
610 // (vehicles and avatar movement, in particular) 655 // (vehicles and avatar movement, in particular)
611 TriggerPreStepEvent(timeStep); 656 TriggerPreStepEvent(timeStep);
612 657
613 // the prestep actions might have added taints 658 // the prestep actions might have added taints
614 numTaints += _taintOperations.Count; 659 numTaints += _taintOperations.Count;
615 ProcessTaints(); 660 ProcessTaints();
616 661
617 InTaintTime = false; // Only used for debugging so locking is not necessary. 662 InTaintTime = false; // Only used for debugging so locking is not necessary.
618 663
619 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. 664 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
620 // Only enable this in a limited test world with few objects. 665 // Only enable this in a limited test world with few objects.
621 if (m_physicsPhysicalDumpEnabled) 666 if (m_physicsPhysicalDumpEnabled)
622 PE.DumpAllInfo(World); 667 PE.DumpAllInfo(World);
623 668
624 // step the physical world one interval 669 // step the physical world one interval
625 m_simulationStep++; 670 m_simulationStep++;
626 int numSubSteps = 0; 671 int numSubSteps = 0;
627 try 672 try
628 { 673 {
629 numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount); 674 numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount);
630 675
631 } 676 }
632 catch (Exception e) 677 catch (Exception e)
633 { 678 {
634 m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}", 679 m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}",
635 LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e); 680 LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e);
636 DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}", 681 DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}",
637 DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount); 682 DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
638 updatedEntityCount = 0; 683 updatedEntityCount = 0;
639 collidersCount = 0; 684 collidersCount = 0;
640 } 685 }
641 686
642 // Make the physics engine dump useful statistics periodically 687 // Make the physics engine dump useful statistics periodically
643 if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0)) 688 if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0))
644 PE.DumpPhysicsStatistics(World); 689 PE.DumpPhysicsStatistics(World);
645 690
646 // Get a value for 'now' so all the collision and update routines don't have to get their own. 691 // Get a value for 'now' so all the collision and update routines don't have to get their own.
647 SimulationNowTime = Util.EnvironmentTickCount(); 692 SimulationNowTime = Util.EnvironmentTickCount();
648 693
649 // Send collision information to the colliding objects. The objects decide if the collision 694 // Send collision information to the colliding objects. The objects decide if the collision
650 // is 'real' (like linksets don't collide with themselves) and the individual objects 695 // is 'real' (like linksets don't collide with themselves) and the individual objects
651 // know if the simulator has subscribed to collisions. 696 // know if the simulator has subscribed to collisions.
652 lock (CollisionLock) 697 lock (CollisionLock)
653 {
654 if (collidersCount > 0)
655 { 698 {
656 lock (PhysObjects) 699 if (collidersCount > 0)
657 { 700 {
658 for (int ii = 0; ii < collidersCount; ii++) 701 lock (PhysObjects)
659 { 702 {
660 uint cA = m_collisionArray[ii].aID; 703 for (int ii = 0; ii < collidersCount; ii++)
661 uint cB = m_collisionArray[ii].bID; 704 {
662 Vector3 point = m_collisionArray[ii].point; 705 uint cA = m_collisionArray[ii].aID;
663 Vector3 normal = m_collisionArray[ii].normal; 706 uint cB = m_collisionArray[ii].bID;
664 float penetration = m_collisionArray[ii].penetration; 707 Vector3 point = m_collisionArray[ii].point;
665 SendCollision(cA, cB, point, normal, penetration); 708 Vector3 normal = m_collisionArray[ii].normal;
666 SendCollision(cB, cA, point, -normal, penetration); 709 float penetration = m_collisionArray[ii].penetration;
710 SendCollision(cA, cB, point, normal, penetration);
711 SendCollision(cB, cA, point, -normal, penetration);
712 }
667 } 713 }
668 } 714 }
669 } 715 }
670 }
671 716
672 // If any of the objects had updated properties, tell the managed objects about the update 717 // If any of the objects had updated properties, tell the managed objects about the update
673 // and remember that there was a change so it will be passed to the simulator. 718 // and remember that there was a change so it will be passed to the simulator.
674 lock (UpdateLock) 719 lock (UpdateLock)
675 {
676 if (updatedEntityCount > 0)
677 { 720 {
678 lock (PhysObjects) 721 if (updatedEntityCount > 0)
679 { 722 {
680 for (int ii = 0; ii < updatedEntityCount; ii++) 723 lock (PhysObjects)
681 { 724 {
682 EntityProperties entprop = m_updateArray[ii]; 725 for (int ii = 0; ii < updatedEntityCount; ii++)
683 BSPhysObject pobj;
684 if (PhysObjects.TryGetValue(entprop.ID, out pobj))
685 { 726 {
686 if (pobj.IsInitialized) 727 EntityProperties entprop = m_updateArray[ii];
687 pobj.UpdateProperties(entprop); 728 BSPhysObject pobj;
729 if (PhysObjects.TryGetValue(entprop.ID, out pobj))
730 {
731 if (pobj.IsInitialized)
732 pobj.UpdateProperties(entprop);
733 }
688 } 734 }
689 } 735 }
690 } 736 }
691 } 737 }
692 }
693
694 // Some actors want to know when the simulation step is complete.
695 TriggerPostStepEvent(timeStep);
696 738
697 simTime = Util.EnvironmentTickCountSubtract(beforeTime); 739 // Some actors want to know when the simulation step is complete.
698 if (PhysicsLogging.Enabled) 740 TriggerPostStepEvent(timeStep);
699 {
700 DetailLog("{0},DoPhysicsStep,complete,frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
701 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
702 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
703 }
704 741
705 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. 742 simTime = Util.EnvironmentTickCountSubtract(beforeTime);
706 // Only enable this in a limited test world with few objects. 743 if (PhysicsLogging.Enabled)
707 if (m_physicsPhysicalDumpEnabled) 744 {
708 PE.DumpAllInfo(World); 745 DetailLog("{0},DoPhysicsStep,complete,frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
746 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
747 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
748 }
709 749
710 // The physics engine returns the number of milliseconds it simulated this call. 750 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
711 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. 751 // Only enable this in a limited test world with few objects.
712 // Multiply by a fixed nominal frame rate to give a rate similar to the simulator (usually 55). 752 if (m_physicsPhysicalDumpEnabled)
713 m_simulatedTime += (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate; 753 PE.DumpAllInfo(World);
714 }
715 754
716 // Called by a BSPhysObject to note that it has changed properties and this information 755 // The physics engine returns the number of milliseconds it simulated this call.
717 // should be passed up to the simulator at the proper time. 756 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
718 // Note: this is called by the BSPhysObject from invocation via DoPhysicsStep() above so 757 // Multiply by a fixed nominal frame rate to give a rate similar to the simulator (usually 55).
719 // this is is under UpdateLock. 758 m_simulatedTime += (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate;
720 public void PostUpdate(BSPhysObject updatee)
721 {
722 lock (UpdateLock)
723 {
724 ObjectsWithUpdates.Add(updatee);
725 } 759 }
726 }
727
728 // The simulator thinks it is physics time so return all the collisions and position
729 // updates that were collected in actual physics simulation.
730 private float SendUpdatesToSimulator(float timeStep)
731 {
732 if (!m_initialized) return 5.0f;
733 760
734 DetailLog("{0},SendUpdatesToSimulator,collisions={1},updates={2},simedTime={3}", 761 // Called by a BSPhysObject to note that it has changed properties and this information
735 BSScene.DetailLogZero, ObjectsWithCollisions.Count, ObjectsWithUpdates.Count, m_simulatedTime); 762 // should be passed up to the simulator at the proper time.
736 // Push the collisions into the simulator. 763 // Note: this is called by the BSPhysObject from invocation via DoPhysicsStep() above so
737 lock (CollisionLock) 764 // this is is under UpdateLock.
765 public void PostUpdate(BSPhysObject updatee)
738 { 766 {
739 if (ObjectsWithCollisions.Count > 0) 767 lock (UpdateLock)
740 { 768 {
741 foreach (BSPhysObject bsp in ObjectsWithCollisions) 769 ObjectsWithUpdates.Add(updatee);
742 if (!bsp.SendCollisions())
743 {
744 // If the object is done colliding, see that it's removed from the colliding list
745 ObjectsWithNoMoreCollisions.Add(bsp);
746 }
747 } 770 }
771 }
748 772
749 // This is a kludge to get avatar movement updates. 773 // The simulator thinks it is physics time so return all the collisions and position
750 // The simulator expects collisions for avatars even if there are have been no collisions. 774 // updates that were collected in actual physics simulation.
751 // The event updates avatar animations and stuff. 775 private float SendUpdatesToSimulator(float timeStep)
752 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen. 776 {
753 // Note that we get a copy of the list to search because SendCollision() can take a while. 777 if (!m_initialized) return 5.0f;
754 HashSet<BSPhysObject> tempAvatarsInScene; 778
755 lock (AvatarsInSceneLock) 779 DetailLog("{0},SendUpdatesToSimulator,collisions={1},updates={2},simedTime={3}",
780 BSScene.DetailLogZero, ObjectsWithCollisions.Count, ObjectsWithUpdates.Count, m_simulatedTime);
781 // Push the collisions into the simulator.
782 lock (CollisionLock)
756 { 783 {
757 tempAvatarsInScene = new HashSet<BSPhysObject>(AvatarsInScene); 784 if (ObjectsWithCollisions.Count > 0)
785 {
786 foreach (BSPhysObject bsp in ObjectsWithCollisions)
787 if (!bsp.SendCollisions())
788 {
789 // If the object is done colliding, see that it's removed from the colliding list
790 ObjectsWithNoMoreCollisions.Add(bsp);
791 }
792 }
793
794 // This is a kludge to get avatar movement updates.
795 // The simulator expects collisions for avatars even if there are have been no collisions.
796 // The event updates avatar animations and stuff.
797 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
798 // Note that we get a copy of the list to search because SendCollision() can take a while.
799 HashSet<BSPhysObject> tempAvatarsInScene;
800 lock (AvatarsInSceneLock)
801 {
802 tempAvatarsInScene = new HashSet<BSPhysObject>(AvatarsInScene);
803 }
804 foreach (BSPhysObject actor in tempAvatarsInScene)
805 {
806 if (!ObjectsWithCollisions.Contains(actor)) // don't call avatars twice
807 actor.SendCollisions();
808 }
809 tempAvatarsInScene = null;
810
811 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
812 // Not done above because it is inside an iteration of ObjectWithCollisions.
813 // This complex collision processing is required to create an empty collision
814 // event call after all real collisions have happened on an object. This allows
815 // the simulator to generate the 'collision end' event.
816 if (ObjectsWithNoMoreCollisions.Count > 0)
817 {
818 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
819 ObjectsWithCollisions.Remove(po);
820 ObjectsWithNoMoreCollisions.Clear();
821 }
758 } 822 }
759 foreach (BSPhysObject actor in tempAvatarsInScene) 823
824 // Call the simulator for each object that has physics property updates.
825 HashSet<BSPhysObject> updatedObjects = null;
826 lock (UpdateLock)
760 { 827 {
761 if (!ObjectsWithCollisions.Contains(actor)) // don't call avatars twice 828 if (ObjectsWithUpdates.Count > 0)
762 actor.SendCollisions(); 829 {
830 updatedObjects = ObjectsWithUpdates;
831 ObjectsWithUpdates = new HashSet<BSPhysObject>();
832 }
763 } 833 }
764 tempAvatarsInScene = null; 834 if (updatedObjects != null)
765
766 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
767 // Not done above because it is inside an iteration of ObjectWithCollisions.
768 // This complex collision processing is required to create an empty collision
769 // event call after all real collisions have happened on an object. This allows
770 // the simulator to generate the 'collision end' event.
771 if (ObjectsWithNoMoreCollisions.Count > 0)
772 { 835 {
773 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) 836 foreach (BSPhysObject obj in updatedObjects)
774 ObjectsWithCollisions.Remove(po); 837 {
775 ObjectsWithNoMoreCollisions.Clear(); 838 obj.RequestPhysicsterseUpdate();
839 }
840 updatedObjects.Clear();
776 } 841 }
842
843 // Return the framerate simulated to give the above returned results.
844 // (Race condition here but this is just bookkeeping so rare mistakes do not merit a lock).
845 float simTime = m_simulatedTime;
846 m_simulatedTime = 0f;
847 return simTime;
777 } 848 }
778 849
779 // Call the simulator for each object that has physics property updates. 850 // Something has collided
780 HashSet<BSPhysObject> updatedObjects = null; 851 private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration)
781 lock (UpdateLock)
782 { 852 {
783 if (ObjectsWithUpdates.Count > 0) 853 if (localID <= TerrainManager.HighestTerrainID)
784 { 854 {
785 updatedObjects = ObjectsWithUpdates; 855 return; // don't send collisions to the terrain
786 ObjectsWithUpdates = new HashSet<BSPhysObject>();
787 } 856 }
788 } 857
789 if (updatedObjects != null) 858 BSPhysObject collider;
790 { 859 // NOTE that PhysObjects was locked before the call to SendCollision().
791 foreach (BSPhysObject obj in updatedObjects) 860 if (!PhysObjects.TryGetValue(localID, out collider))
792 { 861 {
793 obj.RequestPhysicsterseUpdate(); 862 // If the object that is colliding cannot be found, just ignore the collision.
863 DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith);
864 return;
794 } 865 }
795 updatedObjects.Clear();
796 }
797
798 // Return the framerate simulated to give the above returned results.
799 // (Race condition here but this is just bookkeeping so rare mistakes do not merit a lock).
800 float simTime = m_simulatedTime;
801 m_simulatedTime = 0f;
802 return simTime;
803 }
804 866
805 // Something has collided 867 // Note: the terrain is not in the physical object list so 'collidee' can be null when Collide() is called.
806 private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration) 868 BSPhysObject collidee = null;
807 { 869 PhysObjects.TryGetValue(collidingWith, out collidee);
808 if (localID <= TerrainManager.HighestTerrainID)
809 {
810 return; // don't send collisions to the terrain
811 }
812
813 BSPhysObject collider;
814 // NOTE that PhysObjects was locked before the call to SendCollision().
815 if (!PhysObjects.TryGetValue(localID, out collider))
816 {
817 // If the object that is colliding cannot be found, just ignore the collision.
818 DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith);
819 return;
820 }
821
822 // Note: the terrain is not in the physical object list so 'collidee' can be null when Collide() is called.
823 BSPhysObject collidee = null;
824 PhysObjects.TryGetValue(collidingWith, out collidee);
825 870
826 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); 871 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
827 872
828 if (collider.IsInitialized) 873 if (collider.IsInitialized)
829 {
830 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
831 { 874 {
832 // If a collision was 'good', remember to send it to the simulator 875 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
833 lock (CollisionLock)
834 { 876 {
835 ObjectsWithCollisions.Add(collider); 877 // If a collision was 'good', remember to send it to the simulator
878 lock (CollisionLock)
879 {
880 ObjectsWithCollisions.Add(collider);
881 }
836 } 882 }
837 } 883 }
884
885 return;
838 } 886 }
839 887
840 return; 888 public void BulletSPluginPhysicsThread()
841 } 889 {
890 Thread.CurrentThread.Priority = ThreadPriority.Highest;
891 m_updateWaitEvent = new ManualResetEvent(false);
842 892
843 public void BulletSPluginPhysicsThread() 893 while (m_initialized)
844 { 894 {
845 Thread.CurrentThread.Priority = ThreadPriority.Highest; 895 int beginSimulationRealtimeMS = Util.EnvironmentTickCount();
846 m_updateWaitEvent = new ManualResetEvent(false);
847 896
848 while (m_initialized) 897 if (BSParam.Active)
849 { 898 DoPhysicsStep(BSParam.PhysicsTimeStep);
850 int beginSimulationRealtimeMS = Util.EnvironmentTickCount();
851 899
852 if (BSParam.Active) 900 int simulationRealtimeMS = Util.EnvironmentTickCountSubtract(beginSimulationRealtimeMS);
853 DoPhysicsStep(BSParam.PhysicsTimeStep); 901 int simulationTimeVsRealtimeDifferenceMS = ((int)(BSParam.PhysicsTimeStep*1000f)) - simulationRealtimeMS;
854 902
855 int simulationRealtimeMS = Util.EnvironmentTickCountSubtract(beginSimulationRealtimeMS); 903 if (simulationTimeVsRealtimeDifferenceMS > 0)
856 int simulationTimeVsRealtimeDifferenceMS = ((int)(BSParam.PhysicsTimeStep*1000f)) - simulationRealtimeMS; 904 {
905 // The simulation of the time interval took less than realtime.
906 // Do a wait for the rest of realtime.
907 m_updateWaitEvent.WaitOne(simulationTimeVsRealtimeDifferenceMS);
908 //Thread.Sleep(simulationTimeVsRealtimeDifferenceMS);
909 }
910 else
911 {
912 // The simulation took longer than realtime.
913 // Do some scaling of simulation time.
914 // TODO.
915 DetailLog("{0},BulletSPluginPhysicsThread,longerThanRealtime={1}", BSScene.DetailLogZero, simulationTimeVsRealtimeDifferenceMS);
916 }
857 917
858 if (simulationTimeVsRealtimeDifferenceMS > 0) 918 Watchdog.UpdateThread();
859 {
860 // The simulation of the time interval took less than realtime.
861 // Do a wait for the rest of realtime.
862 m_updateWaitEvent.WaitOne(simulationTimeVsRealtimeDifferenceMS);
863 //Thread.Sleep(simulationTimeVsRealtimeDifferenceMS);
864 }
865 else
866 {
867 // The simulation took longer than realtime.
868 // Do some scaling of simulation time.
869 // TODO.
870 DetailLog("{0},BulletSPluginPhysicsThread,longerThanRealtime={1}", BSScene.DetailLogZero, simulationTimeVsRealtimeDifferenceMS);
871 } 919 }
872 920
873 Watchdog.UpdateThread(); 921 Watchdog.RemoveThread();
874 } 922 }
875 923
876 Watchdog.RemoveThread(); 924 #endregion // Simulation
877 }
878
879 #endregion // Simulation
880
881 public override void GetResults() { }
882 925
883 #region Terrain 926 public override void GetResults() { }
884 927
885 public override void SetTerrain(float[] heightMap) { 928 #region Terrain
886 TerrainManager.SetTerrain(heightMap);
887 }
888 929
889 public override void SetWaterLevel(float baseheight) 930 public override void SetTerrain(float[] heightMap) {
890 { 931 TerrainManager.SetTerrain(heightMap);
891 SimpleWaterLevel = baseheight; 932 }
892 }
893 933
894 public override void DeleteTerrain() 934 public override void SetWaterLevel(float baseheight)
895 { 935 {
896 // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader); 936 SimpleWaterLevel = baseheight;
897 } 937 }
898 938
899 // Although no one seems to check this, I do support combining. 939 public override void DeleteTerrain()
900 public override bool SupportsCombining() 940 {
901 { 941 // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
902 return TerrainManager.SupportsCombining(); 942 }
903 }
904 // This call says I am a child to region zero in a mega-region. 'pScene' is that
905 // of region zero, 'offset' is my offset from regions zero's origin, and
906 // 'extents' is the largest XY that is handled in my region.
907 public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
908 {
909 TerrainManager.Combine(pScene, offset, extents);
910 }
911 943
912 // Unhook all the combining that I know about. 944 // Although no one seems to check this, I do support combining.
913 public override void UnCombine(PhysicsScene pScene) 945 public override bool SupportsCombining()
914 { 946 {
915 TerrainManager.UnCombine(pScene); 947 return TerrainManager.SupportsCombining();
916 } 948 }
949 // This call says I am a child to region zero in a mega-region. 'pScene' is that
950 // of region zero, 'offset' is my offset from regions zero's origin, and
951 // 'extents' is the largest XY that is handled in my region.
952 public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
953 {
954 TerrainManager.Combine(pScene, offset, extents);
955 }
917 956
918 #endregion // Terrain 957 // Unhook all the combining that I know about.
958 public override void UnCombine(PhysicsScene pScene)
959 {
960 TerrainManager.UnCombine(pScene);
961 }
919 962
920 public override Dictionary<uint, float> GetTopColliders() 963 #endregion // Terrain
921 {
922 Dictionary<uint, float> topColliders;
923 964
924 lock (PhysObjects) 965 public override Dictionary<uint, float> GetTopColliders()
925 { 966 {
926 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects) 967 Dictionary<uint, float> topColliders;
968
969 lock (PhysObjects)
927 { 970 {
928 kvp.Value.ComputeCollisionScore(); 971 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
972 {
973 kvp.Value.ComputeCollisionScore();
974 }
975
976 List<BSPhysObject> orderedPrims = new List<BSPhysObject>(PhysObjects.Values);
977 orderedPrims.OrderByDescending(p => p.CollisionScore);
978 topColliders = orderedPrims.Take(25).ToDictionary(p => p.LocalID, p => p.CollisionScore);
929 } 979 }
930 980
931 List<BSPhysObject> orderedPrims = new List<BSPhysObject>(PhysObjects.Values); 981 return topColliders;
932 orderedPrims.OrderByDescending(p => p.CollisionScore);
933 topColliders = orderedPrims.Take(25).ToDictionary(p => p.LocalID, p => p.CollisionScore);
934 } 982 }
935 983
936 return topColliders; 984 public override bool IsThreaded { get { return false; } }
937 }
938
939 public override bool IsThreaded { get { return false; } }
940
941 #region Extensions
942 public override object Extension(string pFunct, params object[] pParams)
943 {
944 DetailLog("{0} BSScene.Extension,op={1}", DetailLogZero, pFunct);
945 return base.Extension(pFunct, pParams);
946 }
947 #endregion // Extensions
948 985
949 public static string PrimitiveBaseShapeToString(PrimitiveBaseShape pbs) 986 #region Extensions
950 { 987 public override object Extension(string pFunct, params object[] pParams)
951 float pathShearX = pbs.PathShearX < 128 ? (float)pbs.PathShearX * 0.01f : (float)(pbs.PathShearX - 256) * 0.01f; 988 {
952 float pathShearY = pbs.PathShearY < 128 ? (float)pbs.PathShearY * 0.01f : (float)(pbs.PathShearY - 256) * 0.01f; 989 DetailLog("{0} BSScene.Extension,op={1}", DetailLogZero, pFunct);
953 float pathBegin = (float)pbs.PathBegin * 2.0e-5f; 990 return base.Extension(pFunct, pParams);
954 float pathEnd = 1.0f - (float)pbs.PathEnd * 2.0e-5f; 991 }
955 float pathScaleX = (float)(200 - pbs.PathScaleX) * 0.01f; 992 #endregion // Extensions
956 float pathScaleY = (float)(200 - pbs.PathScaleY) * 0.01f;
957 float pathTaperX = pbs.PathTaperX * 0.01f;
958 float pathTaperY = pbs.PathTaperY * 0.01f;
959
960 float profileBegin = (float)pbs.ProfileBegin * 2.0e-5f;
961 float profileEnd = 1.0f - (float)pbs.ProfileEnd * 2.0e-5f;
962 float profileHollow = (float)pbs.ProfileHollow * 2.0e-5f;
963 if (profileHollow > 0.95f)
964 profileHollow = 0.95f;
965
966 StringBuilder buff = new StringBuilder();
967 buff.Append("shape=");
968 buff.Append(((ProfileShape)pbs.ProfileShape).ToString());
969 buff.Append(",");
970 buff.Append("hollow=");
971 buff.Append(((HollowShape)pbs.HollowShape).ToString());
972 buff.Append(",");
973 buff.Append("pathCurve=");
974 buff.Append(((Extrusion)pbs.PathCurve).ToString());
975 buff.Append(",");
976 buff.Append("profCurve=");
977 buff.Append(((Extrusion)pbs.ProfileCurve).ToString());
978 buff.Append(",");
979 buff.Append("profHollow=");
980 buff.Append(profileHollow.ToString());
981 buff.Append(",");
982 buff.Append("pathBegEnd=");
983 buff.Append(pathBegin.ToString());
984 buff.Append("/");
985 buff.Append(pathEnd.ToString());
986 buff.Append(",");
987 buff.Append("profileBegEnd=");
988 buff.Append(profileBegin.ToString());
989 buff.Append("/");
990 buff.Append(profileEnd.ToString());
991 buff.Append(",");
992 buff.Append("scaleXY=");
993 buff.Append(pathScaleX.ToString());
994 buff.Append("/");
995 buff.Append(pathScaleY.ToString());
996 buff.Append(",");
997 buff.Append("shearXY=");
998 buff.Append(pathShearX.ToString());
999 buff.Append("/");
1000 buff.Append(pathShearY.ToString());
1001 buff.Append(",");
1002 buff.Append("taperXY=");
1003 buff.Append(pbs.PathTaperX.ToString());
1004 buff.Append("/");
1005 buff.Append(pbs.PathTaperY.ToString());
1006 buff.Append(",");
1007 buff.Append("skew=");
1008 buff.Append(pbs.PathSkew.ToString());
1009 buff.Append(",");
1010 buff.Append("twist/Beg=");
1011 buff.Append(pbs.PathTwist.ToString());
1012 buff.Append("/");
1013 buff.Append(pbs.PathTwistBegin.ToString());
1014
1015 return buff.ToString();
1016 }
1017 993
1018 #region Taints 994 public static string PrimitiveBaseShapeToString(PrimitiveBaseShape pbs)
1019 // The simulation execution order is: 995 {
1020 // Simulate() 996 float pathShearX = pbs.PathShearX < 128 ? (float)pbs.PathShearX * 0.01f : (float)(pbs.PathShearX - 256) * 0.01f;
1021 // DoOneTimeTaints 997 float pathShearY = pbs.PathShearY < 128 ? (float)pbs.PathShearY * 0.01f : (float)(pbs.PathShearY - 256) * 0.01f;
1022 // TriggerPreStepEvent 998 float pathBegin = (float)pbs.PathBegin * 2.0e-5f;
1023 // DoOneTimeTaints 999 float pathEnd = 1.0f - (float)pbs.PathEnd * 2.0e-5f;
1024 // Step() 1000 float pathScaleX = (float)(200 - pbs.PathScaleX) * 0.01f;
1025 // ProcessAndSendToSimulatorCollisions 1001 float pathScaleY = (float)(200 - pbs.PathScaleY) * 0.01f;
1026 // ProcessAndSendToSimulatorPropertyUpdates 1002 float pathTaperX = pbs.PathTaperX * 0.01f;
1027 // TriggerPostStepEvent 1003 float pathTaperY = pbs.PathTaperY * 0.01f;
1028 1004
1029 // Calls to the PhysicsActors can't directly call into the physics engine 1005 float profileBegin = (float)pbs.ProfileBegin * 2.0e-5f;
1030 // because it might be busy. We delay changes to a known time. 1006 float profileEnd = 1.0f - (float)pbs.ProfileEnd * 2.0e-5f;
1031 // We rely on C#'s closure to save and restore the context for the delegate. 1007 float profileHollow = (float)pbs.ProfileHollow * 2.0e-5f;
1032 public void TaintedObject(string pOriginator, string pIdent, TaintCallback pCallback) 1008 if (profileHollow > 0.95f)
1033 { 1009 profileHollow = 0.95f;
1034 TaintedObject(false /*inTaintTime*/, pOriginator, pIdent, pCallback); 1010
1035 } 1011 StringBuilder buff = new StringBuilder();
1036 public void TaintedObject(uint pOriginator, String pIdent, TaintCallback pCallback) 1012 buff.Append("shape=");
1037 { 1013 buff.Append(((ProfileShape)pbs.ProfileShape).ToString());
1038 TaintedObject(false /*inTaintTime*/, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback); 1014 buff.Append(",");
1039 } 1015 buff.Append("hollow=");
1040 public void TaintedObject(bool inTaintTime, String pIdent, TaintCallback pCallback) 1016 buff.Append(((HollowShape)pbs.HollowShape).ToString());
1041 { 1017 buff.Append(",");
1042 TaintedObject(inTaintTime, BSScene.DetailLogZero, pIdent, pCallback); 1018 buff.Append("pathCurve=");
1043 } 1019 buff.Append(((Extrusion)pbs.PathCurve).ToString());
1044 public void TaintedObject(bool inTaintTime, uint pOriginator, String pIdent, TaintCallback pCallback) 1020 buff.Append(",");
1045 { 1021 buff.Append("profCurve=");
1046 TaintedObject(inTaintTime, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback); 1022 buff.Append(((Extrusion)pbs.ProfileCurve).ToString());
1047 } 1023 buff.Append(",");
1048 // Sometimes a potentially tainted operation can be used in and out of taint time. 1024 buff.Append("profHollow=");
1049 // This routine executes the command immediately if in taint-time otherwise it is queued. 1025 buff.Append(profileHollow.ToString());
1050 public void TaintedObject(bool inTaintTime, string pOriginator, string pIdent, TaintCallback pCallback) 1026 buff.Append(",");
1051 { 1027 buff.Append("pathBegEnd=");
1052 if (!m_initialized) return; 1028 buff.Append(pathBegin.ToString());
1029 buff.Append("/");
1030 buff.Append(pathEnd.ToString());
1031 buff.Append(",");
1032 buff.Append("profileBegEnd=");
1033 buff.Append(profileBegin.ToString());
1034 buff.Append("/");
1035 buff.Append(profileEnd.ToString());
1036 buff.Append(",");
1037 buff.Append("scaleXY=");
1038 buff.Append(pathScaleX.ToString());
1039 buff.Append("/");
1040 buff.Append(pathScaleY.ToString());
1041 buff.Append(",");
1042 buff.Append("shearXY=");
1043 buff.Append(pathShearX.ToString());
1044 buff.Append("/");
1045 buff.Append(pathShearY.ToString());
1046 buff.Append(",");
1047 buff.Append("taperXY=");
1048 buff.Append(pbs.PathTaperX.ToString());
1049 buff.Append("/");
1050 buff.Append(pbs.PathTaperY.ToString());
1051 buff.Append(",");
1052 buff.Append("skew=");
1053 buff.Append(pbs.PathSkew.ToString());
1054 buff.Append(",");
1055 buff.Append("twist/Beg=");
1056 buff.Append(pbs.PathTwist.ToString());
1057 buff.Append("/");
1058 buff.Append(pbs.PathTwistBegin.ToString());
1059
1060 return buff.ToString();
1061 }
1053 1062
1054 if (inTaintTime) 1063 #region Taints
1055 pCallback(); 1064 // The simulation execution order is:
1056 else 1065 // Simulate()
1066 // DoOneTimeTaints
1067 // TriggerPreStepEvent
1068 // DoOneTimeTaints
1069 // Step()
1070 // ProcessAndSendToSimulatorCollisions
1071 // ProcessAndSendToSimulatorPropertyUpdates
1072 // TriggerPostStepEvent
1073
1074 // Calls to the PhysicsActors can't directly call into the physics engine
1075 // because it might be busy. We delay changes to a known time.
1076 // We rely on C#'s closure to save and restore the context for the delegate.
1077 public void TaintedObject(string pOriginator, string pIdent, TaintCallback pCallback)
1057 { 1078 {
1058 lock (_taintLock) 1079 TaintedObject(false /*inTaintTime*/, pOriginator, pIdent, pCallback);
1080 }
1081 public void TaintedObject(uint pOriginator, String pIdent, TaintCallback pCallback)
1082 {
1083 TaintedObject(false /*inTaintTime*/, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback);
1084 }
1085 public void TaintedObject(bool inTaintTime, String pIdent, TaintCallback pCallback)
1086 {
1087 TaintedObject(inTaintTime, BSScene.DetailLogZero, pIdent, pCallback);
1088 }
1089 public void TaintedObject(bool inTaintTime, uint pOriginator, String pIdent, TaintCallback pCallback)
1090 {
1091 TaintedObject(inTaintTime, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback);
1092 }
1093 // Sometimes a potentially tainted operation can be used in and out of taint time.
1094 // This routine executes the command immediately if in taint-time otherwise it is queued.
1095 public void TaintedObject(bool inTaintTime, string pOriginator, string pIdent, TaintCallback pCallback)
1096 {
1097 if (!m_initialized) return;
1098
1099 if (inTaintTime)
1100 pCallback();
1101 else
1059 { 1102 {
1060 _taintOperations.Add(new TaintCallbackEntry(pOriginator, pIdent, pCallback)); 1103 lock (_taintLock)
1104 {
1105 _taintOperations.Add(new TaintCallbackEntry(pOriginator, pIdent, pCallback));
1106 }
1061 } 1107 }
1062 } 1108 }
1063 }
1064 1109
1065 private void TriggerPreStepEvent(float timeStep) 1110 private void TriggerPreStepEvent(float timeStep)
1066 { 1111 {
1067 PreStepAction actions = BeforeStep; 1112 PreStepAction actions = BeforeStep;
1068 if (actions != null) 1113 if (actions != null)
1069 actions(timeStep); 1114 actions(timeStep);
1070
1071 }
1072 1115
1073 private void TriggerPostStepEvent(float timeStep) 1116 }
1074 {
1075 PostStepAction actions = AfterStep;
1076 if (actions != null)
1077 actions(timeStep);
1078 1117
1079 } 1118 private void TriggerPostStepEvent(float timeStep)
1119 {
1120 PostStepAction actions = AfterStep;
1121 if (actions != null)
1122 actions(timeStep);
1080 1123
1081 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues 1124 }
1082 // a callback into itself to do the actual property change. That callback is called
1083 // here just before the physics engine is called to step the simulation.
1084 public void ProcessTaints()
1085 {
1086 ProcessRegularTaints();
1087 ProcessPostTaintTaints();
1088 }
1089 1125
1090 private void ProcessRegularTaints() 1126 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues
1091 { 1127 // a callback into itself to do the actual property change. That callback is called
1092 if (m_initialized && _taintOperations.Count > 0) // save allocating new list if there is nothing to process 1128 // here just before the physics engine is called to step the simulation.
1129 public void ProcessTaints()
1093 { 1130 {
1094 // swizzle a new list into the list location so we can process what's there 1131 ProcessRegularTaints();
1095 List<TaintCallbackEntry> oldList; 1132 ProcessPostTaintTaints();
1096 lock (_taintLock) 1133 }
1097 {
1098 oldList = _taintOperations;
1099 _taintOperations = new List<TaintCallbackEntry>();
1100 }
1101 1134
1102 foreach (TaintCallbackEntry tcbe in oldList) 1135 private void ProcessRegularTaints()
1136 {
1137 if (m_initialized && _taintOperations.Count > 0) // save allocating new list if there is nothing to process
1103 { 1138 {
1104 try 1139 // swizzle a new list into the list location so we can process what's there
1140 List<TaintCallbackEntry> oldList;
1141 lock (_taintLock)
1105 { 1142 {
1106 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", tcbe.originator, tcbe.ident); // DEBUG DEBUG DEBUG 1143 oldList = _taintOperations;
1107 tcbe.callback(); 1144 _taintOperations = new List<TaintCallbackEntry>();
1108 } 1145 }
1109 catch (Exception e) 1146
1147 foreach (TaintCallbackEntry tcbe in oldList)
1110 { 1148 {
1111 m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e); 1149 try
1150 {
1151 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", tcbe.originator, tcbe.ident); // DEBUG DEBUG DEBUG
1152 tcbe.callback();
1153 }
1154 catch (Exception e)
1155 {
1156 m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e);
1157 }
1112 } 1158 }
1159 oldList.Clear();
1113 } 1160 }
1114 oldList.Clear();
1115 } 1161 }
1116 }
1117
1118 // Schedule an update to happen after all the regular taints are processed.
1119 // Note that new requests for the same operation ("ident") for the same object ("ID")
1120 // will replace any previous operation by the same object.
1121 public void PostTaintObject(String ident, uint ID, TaintCallback callback)
1122 {
1123 string IDAsString = ID.ToString();
1124 string uniqueIdent = ident + "-" + IDAsString;
1125 lock (_taintLock)
1126 {
1127 _postTaintOperations[uniqueIdent] = new TaintCallbackEntry(IDAsString, uniqueIdent, callback);
1128 }
1129
1130 return;
1131 }
1132 1162
1133 // Taints that happen after the normal taint processing but before the simulation step. 1163 // Schedule an update to happen after all the regular taints are processed.
1134 private void ProcessPostTaintTaints() 1164 // Note that new requests for the same operation ("ident") for the same object ("ID")
1135 { 1165 // will replace any previous operation by the same object.
1136 if (m_initialized && _postTaintOperations.Count > 0) 1166 public void PostTaintObject(String ident, uint ID, TaintCallback callback)
1137 { 1167 {
1138 Dictionary<string, TaintCallbackEntry> oldList; 1168 string IDAsString = ID.ToString();
1169 string uniqueIdent = ident + "-" + IDAsString;
1139 lock (_taintLock) 1170 lock (_taintLock)
1140 { 1171 {
1141 oldList = _postTaintOperations; 1172 _postTaintOperations[uniqueIdent] = new TaintCallbackEntry(IDAsString, uniqueIdent, callback);
1142 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
1143 } 1173 }
1144 1174
1145 foreach (KeyValuePair<string,TaintCallbackEntry> kvp in oldList) 1175 return;
1176 }
1177
1178 // Taints that happen after the normal taint processing but before the simulation step.
1179 private void ProcessPostTaintTaints()
1180 {
1181 if (m_initialized && _postTaintOperations.Count > 0)
1146 { 1182 {
1147 try 1183 Dictionary<string, TaintCallbackEntry> oldList;
1184 lock (_taintLock)
1148 { 1185 {
1149 DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG 1186 oldList = _postTaintOperations;
1150 kvp.Value.callback(); 1187 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
1151 } 1188 }
1152 catch (Exception e) 1189
1190 foreach (KeyValuePair<string,TaintCallbackEntry> kvp in oldList)
1153 { 1191 {
1154 m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e); 1192 try
1193 {
1194 DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG
1195 kvp.Value.callback();
1196 }
1197 catch (Exception e)
1198 {
1199 m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e);
1200 }
1155 } 1201 }
1202 oldList.Clear();
1156 } 1203 }
1157 oldList.Clear();
1158 } 1204 }
1159 }
1160 1205
1161 // Only used for debugging. Does not change state of anything so locking is not necessary. 1206 // Only used for debugging. Does not change state of anything so locking is not necessary.
1162 public bool AssertInTaintTime(string whereFrom) 1207 public bool AssertInTaintTime(string whereFrom)
1163 {
1164 if (!InTaintTime)
1165 { 1208 {
1166 DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom); 1209 if (!InTaintTime)
1167 m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom); 1210 {
1168 // Util.PrintCallStack(DetailLog); 1211 DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
1212 m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
1213 // Util.PrintCallStack(DetailLog);
1214 }
1215 return InTaintTime;
1169 } 1216 }
1170 return InTaintTime;
1171 }
1172 1217
1173 #endregion // Taints 1218 #endregion // Taints
1174 1219
1175 #region IPhysicsParameters 1220 #region IPhysicsParameters
1176 // Get the list of parameters this physics engine supports 1221 // Get the list of parameters this physics engine supports
1177 public PhysParameterEntry[] GetParameterList() 1222 public PhysParameterEntry[] GetParameterList()
1178 { 1223 {
1179 BSParam.BuildParameterTable(); 1224 BSParam.BuildParameterTable();
1180 return BSParam.SettableParameters; 1225 return BSParam.SettableParameters;
1181 } 1226 }
1182
1183 // Set parameter on a specific or all instances.
1184 // Return 'false' if not able to set the parameter.
1185 // Setting the value in the m_params block will change the value the physics engine
1186 // will use the next time since it's pinned and shared memory.
1187 // Some of the values require calling into the physics engine to get the new
1188 // value activated ('terrainFriction' for instance).
1189 public bool SetPhysicsParameter(string parm, string val, uint localID)
1190 {
1191 bool ret = false;
1192 1227
1193 BSParam.ParameterDefnBase theParam; 1228 // Set parameter on a specific or all instances.
1194 if (BSParam.TryGetParameter(parm, out theParam)) 1229 // Return 'false' if not able to set the parameter.
1230 // Setting the value in the m_params block will change the value the physics engine
1231 // will use the next time since it's pinned and shared memory.
1232 // Some of the values require calling into the physics engine to get the new
1233 // value activated ('terrainFriction' for instance).
1234 public bool SetPhysicsParameter(string parm, string val, uint localID)
1195 { 1235 {
1196 // Set the value in the C# code 1236 bool ret = false;
1197 theParam.SetValue(this, val);
1198 1237
1199 // Optionally set the parameter in the unmanaged code 1238 BSParam.ParameterDefnBase theParam;
1200 if (theParam.HasSetOnObject) 1239 if (BSParam.TryGetParameter(parm, out theParam))
1201 { 1240 {
1202 // update all the localIDs specified 1241 // Set the value in the C# code
1203 // If the local ID is APPLY_TO_NONE, just change the default value 1242 theParam.SetValue(this, val);
1204 // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs 1243
1205 // If the localID is a specific object, apply the parameter change to only that object 1244 // Optionally set the parameter in the unmanaged code
1206 List<uint> objectIDs = new List<uint>(); 1245 if (theParam.HasSetOnObject)
1207 switch (localID)
1208 { 1246 {
1209 case PhysParameterEntry.APPLY_TO_NONE: 1247 // update all the localIDs specified
1210 // This will cause a call into the physical world if some operation is specified (SetOnObject). 1248 // If the local ID is APPLY_TO_NONE, just change the default value
1211 objectIDs.Add(TERRAIN_ID); 1249 // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
1212 TaintedUpdateParameter(parm, objectIDs, val); 1250 // If the localID is a specific object, apply the parameter change to only that object
1213 break; 1251 List<uint> objectIDs = new List<uint>();
1214 case PhysParameterEntry.APPLY_TO_ALL: 1252 switch (localID)
1215 lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys); 1253 {
1216 TaintedUpdateParameter(parm, objectIDs, val); 1254 case PhysParameterEntry.APPLY_TO_NONE:
1217 break; 1255 // This will cause a call into the physical world if some operation is specified (SetOnObject).
1218 default: 1256 objectIDs.Add(TERRAIN_ID);
1219 // setting only one localID 1257 TaintedUpdateParameter(parm, objectIDs, val);
1220 objectIDs.Add(localID); 1258 break;
1221 TaintedUpdateParameter(parm, objectIDs, val); 1259 case PhysParameterEntry.APPLY_TO_ALL:
1222 break; 1260 lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
1261 TaintedUpdateParameter(parm, objectIDs, val);
1262 break;
1263 default:
1264 // setting only one localID
1265 objectIDs.Add(localID);
1266 TaintedUpdateParameter(parm, objectIDs, val);
1267 break;
1268 }
1223 } 1269 }
1224 }
1225 1270
1226 ret = true; 1271 ret = true;
1272 }
1273 return ret;
1227 } 1274 }
1228 return ret;
1229 }
1230 1275
1231 // schedule the actual updating of the paramter to when the phys engine is not busy 1276 // schedule the actual updating of the paramter to when the phys engine is not busy
1232 private void TaintedUpdateParameter(string parm, List<uint> lIDs, string val) 1277 private void TaintedUpdateParameter(string parm, List<uint> lIDs, string val)
1233 { 1278 {
1234 string xval = val; 1279 string xval = val;
1235 List<uint> xlIDs = lIDs; 1280 List<uint> xlIDs = lIDs;
1236 string xparm = parm; 1281 string xparm = parm;
1237 TaintedObject(DetailLogZero, "BSScene.UpdateParameterSet", delegate() { 1282 TaintedObject(DetailLogZero, "BSScene.UpdateParameterSet", delegate() {
1238 BSParam.ParameterDefnBase thisParam; 1283 BSParam.ParameterDefnBase thisParam;
1239 if (BSParam.TryGetParameter(xparm, out thisParam)) 1284 if (BSParam.TryGetParameter(xparm, out thisParam))
1240 {
1241 if (thisParam.HasSetOnObject)
1242 { 1285 {
1243 foreach (uint lID in xlIDs) 1286 if (thisParam.HasSetOnObject)
1244 { 1287 {
1245 BSPhysObject theObject = null; 1288 foreach (uint lID in xlIDs)
1246 if (PhysObjects.TryGetValue(lID, out theObject)) 1289 {
1247 thisParam.SetOnObject(this, theObject); 1290 BSPhysObject theObject = null;
1291 if (PhysObjects.TryGetValue(lID, out theObject))
1292 thisParam.SetOnObject(this, theObject);
1293 }
1248 } 1294 }
1249 } 1295 }
1250 } 1296 });
1251 }); 1297 }
1252 }
1253 1298
1254 // Get parameter. 1299 // Get parameter.
1255 // Return 'false' if not able to get the parameter. 1300 // Return 'false' if not able to get the parameter.
1256 public bool GetPhysicsParameter(string parm, out string value) 1301 public bool GetPhysicsParameter(string parm, out string value)
1257 {
1258 string val = String.Empty;
1259 bool ret = false;
1260 BSParam.ParameterDefnBase theParam;
1261 if (BSParam.TryGetParameter(parm, out theParam))
1262 { 1302 {
1263 val = theParam.GetValue(this); 1303 string val = String.Empty;
1264 ret = true; 1304 bool ret = false;
1305 BSParam.ParameterDefnBase theParam;
1306 if (BSParam.TryGetParameter(parm, out theParam))
1307 {
1308 val = theParam.GetValue(this);
1309 ret = true;
1310 }
1311 value = val;
1312 return ret;
1265 } 1313 }
1266 value = val;
1267 return ret;
1268 }
1269 1314
1270 #endregion IPhysicsParameters 1315 #endregion IPhysicsParameters
1271 1316
1272 // Invoke the detailed logger and output something if it's enabled. 1317 // Invoke the detailed logger and output something if it's enabled.
1273 public void DetailLog(string msg, params Object[] args) 1318 public void DetailLog(string msg, params Object[] args)
1274 { 1319 {
1275 PhysicsLogging.Write(msg, args); 1320 PhysicsLogging.Write(msg, args);
1276 } 1321 }
1277 // Used to fill in the LocalID when there isn't one. It's the correct number of characters. 1322 // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
1278 public const string DetailLogZero = "0000000000"; 1323 public const string DetailLogZero = "0000000000";
1279 1324
1280} 1325 }
1281} 1326}