diff options
Diffstat (limited to 'OpenSim/Region/PhysicsModules/BulletS/BSScene.cs')
-rw-r--r-- | OpenSim/Region/PhysicsModules/BulletS/BSScene.cs | 1995 |
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; | |||
33 | using System.Threading; | 33 | using System.Threading; |
34 | using OpenSim.Framework; | 34 | using OpenSim.Framework; |
35 | using OpenSim.Framework.Monitoring; | 35 | using OpenSim.Framework.Monitoring; |
36 | using OpenSim.Region.Framework; | 36 | using OpenSim.Region.Framework.Scenes; |
37 | using OpenSim.Region.CoreModules; | 37 | using OpenSim.Region.Framework.Interfaces; |
38 | using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging; | ||
39 | using OpenSim.Region.PhysicsModules.SharedBase; | 38 | using OpenSim.Region.PhysicsModules.SharedBase; |
40 | using Nini.Config; | 39 | using Nini.Config; |
41 | using log4net; | 40 | using log4net; |
42 | using OpenMetaverse; | 41 | using OpenMetaverse; |
42 | using Mono.Addins; | ||
43 | 43 | ||
44 | namespace OpenSim.Region.PhysicsModule.BulletS | 44 | namespace OpenSim.Region.PhysicsModule.BulletS |
45 | { | 45 | { |
46 | public 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 | } |