diff options
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSScene.cs')
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | 1000 |
1 files changed, 579 insertions, 421 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index a31c578..27a78d1 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | |||
@@ -39,96 +39,88 @@ using log4net; | |||
39 | using OpenMetaverse; | 39 | using OpenMetaverse; |
40 | 40 | ||
41 | // TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) | 41 | // TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) |
42 | // Debug linkset | 42 | // Test sculpties (verified that they don't work) |
43 | // Test with multiple regions in one simulator | ||
44 | // Adjust character capsule size when height is adjusted (ScenePresence.SetHeight) | ||
45 | // Test sculpties | ||
46 | // Compute physics FPS reasonably | 43 | // Compute physics FPS reasonably |
47 | // Based on material, set density and friction | 44 | // Based on material, set density and friction |
48 | // More efficient memory usage when passing hull information from BSPrim to BulletSim | 45 | // Don't use constraints in linksets of non-physical objects. Means having to move children manually. |
49 | // Move all logic out of the C++ code and into the C# code for easier future modifications. | ||
50 | // Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly? | 46 | // Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly? |
51 | // In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground) | 47 | // In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground) |
52 | // At the moment, physical and phantom causes object to drop through the terrain | 48 | // At the moment, physical and phantom causes object to drop through the terrain |
53 | // Physical phantom objects and related typing (collision options ) | 49 | // Physical phantom objects and related typing (collision options ) |
54 | // Use collision masks for collision with terrain and phantom objects | ||
55 | // Check out llVolumeDetect. Must do something for that. | 50 | // Check out llVolumeDetect. Must do something for that. |
51 | // Use collision masks for collision with terrain and phantom objects | ||
52 | // More efficient memory usage when passing hull information from BSPrim to BulletSim | ||
56 | // Should prim.link() and prim.delink() membership checking happen at taint time? | 53 | // Should prim.link() and prim.delink() membership checking happen at taint time? |
57 | // changing the position and orientation of a linked prim must rebuild the constraint with the root. | 54 | // Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once. |
58 | // Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once | ||
59 | // Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect | 55 | // Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect |
60 | // Implement the genCollisions feature in BulletSim::SetObjectProperties (don't pass up unneeded collisions) | ||
61 | // Implement LockAngularMotion | 56 | // Implement LockAngularMotion |
62 | // Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation) | 57 | // Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation) |
63 | // Does NeedsMeshing() really need to exclude all the different shapes? | ||
64 | // Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet. | 58 | // Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet. |
65 | // Add PID movement operations. What does ScenePresence.MoveToTarget do? | 59 | // Add PID movement operations. What does ScenePresence.MoveToTarget do? |
66 | // Check terrain size. 128 or 127? | 60 | // Check terrain size. 128 or 127? |
67 | // Raycast | 61 | // Raycast |
68 | // | 62 | // |
69 | namespace OpenSim.Region.Physics.BulletSPlugin | 63 | namespace OpenSim.Region.Physics.BulletSPlugin |
70 | { | 64 | { |
71 | public class BSScene : PhysicsScene, IPhysicsParameters | 65 | public sealed class BSScene : PhysicsScene, IPhysicsParameters |
72 | { | 66 | { |
73 | private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | 67 | private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); |
74 | private static readonly string LogHeader = "[BULLETS SCENE]"; | 68 | private static readonly string LogHeader = "[BULLETS SCENE]"; |
75 | 69 | ||
76 | public void DebugLog(string mm, params Object[] xx) { if (ShouldDebugLog) m_log.DebugFormat(mm, xx); } | 70 | // The name of the region we're working for. |
71 | public string RegionName { get; private set; } | ||
77 | 72 | ||
78 | public string BulletSimVersion = "?"; | 73 | public string BulletSimVersion = "?"; |
79 | 74 | ||
80 | private Dictionary<uint, BSCharacter> m_avatars = new Dictionary<uint, BSCharacter>(); | 75 | public Dictionary<uint, BSPhysObject> PhysObjects; |
81 | private Dictionary<uint, BSPrim> m_prims = new Dictionary<uint, BSPrim>(); | 76 | public BSShapeCollection Shapes; |
82 | private HashSet<BSCharacter> m_avatarsWithCollisions = new HashSet<BSCharacter>(); | ||
83 | private HashSet<BSPrim> m_primsWithCollisions = new HashSet<BSPrim>(); | ||
84 | private List<BSPrim> m_vehicles = new List<BSPrim>(); | ||
85 | private float[] m_heightMap; | ||
86 | private float m_waterLevel; | ||
87 | private uint m_worldID; | ||
88 | public uint WorldID { get { return m_worldID; } } | ||
89 | 77 | ||
90 | // let my minuions use my logger | 78 | // Keeping track of the objects with collisions so we can report begin and end of a collision |
91 | public ILog Logger { get { return m_log; } } | 79 | public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>(); |
80 | public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>(); | ||
81 | // Keep track of all the avatars so we can send them a collision event | ||
82 | // every tick so OpenSim will update its animation. | ||
83 | private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>(); | ||
92 | 84 | ||
93 | private bool m_initialized = false; | 85 | // List of all the objects that have vehicle properties and should be called |
86 | // to update each physics step. | ||
87 | private List<BSPhysObject> m_vehicles = new List<BSPhysObject>(); | ||
94 | 88 | ||
95 | private int m_detailedStatsStep = 0; | 89 | // let my minuions use my logger |
90 | public ILog Logger { get { return m_log; } } | ||
96 | 91 | ||
97 | public IMesher mesher; | 92 | public IMesher mesher; |
98 | private float m_meshLOD; | 93 | // Level of Detail values kept as float because that's what the Meshmerizer wants |
99 | public float MeshLOD | 94 | public float MeshLOD { get; private set; } |
100 | { | 95 | public float MeshMegaPrimLOD { get; private set; } |
101 | get { return m_meshLOD; } | 96 | public float MeshMegaPrimThreshold { get; private set; } |
102 | } | 97 | public float SculptLOD { get; private set; } |
103 | private float m_sculptLOD; | ||
104 | public float SculptLOD | ||
105 | { | ||
106 | get { return m_sculptLOD; } | ||
107 | } | ||
108 | 98 | ||
109 | private BulletSim m_worldSim; | 99 | public uint WorldID { get; private set; } |
110 | public BulletSim World | 100 | public BulletSim World { get; private set; } |
111 | { | 101 | |
112 | get { return m_worldSim; } | 102 | // All the constraints that have been allocated in this instance. |
113 | } | 103 | public BSConstraintCollection Constraints { get; private set; } |
114 | private BSConstraintCollection m_constraintCollection; | ||
115 | public BSConstraintCollection Constraints | ||
116 | { | ||
117 | get { return m_constraintCollection; } | ||
118 | } | ||
119 | 104 | ||
105 | // Simulation parameters | ||
120 | private int m_maxSubSteps; | 106 | private int m_maxSubSteps; |
121 | private float m_fixedTimeStep; | 107 | private float m_fixedTimeStep; |
122 | private long m_simulationStep = 0; | 108 | private long m_simulationStep = 0; |
123 | public long SimulationStep { get { return m_simulationStep; } } | 109 | public long SimulationStep { get { return m_simulationStep; } } |
124 | 110 | private int m_taintsToProcessPerStep; | |
125 | public float LastSimulatedTimestep { get; private set; } | ||
126 | 111 | ||
127 | // A value of the time now so all the collision and update routines do not have to get their own | 112 | // A value of the time now so all the collision and update routines do not have to get their own |
128 | // Set to 'now' just before all the prims and actors are called for collisions and updates | 113 | // Set to 'now' just before all the prims and actors are called for collisions and updates |
129 | private int m_simulationNowTime; | 114 | public int SimulationNowTime { get; private set; } |
130 | public int SimulationNowTime { get { return m_simulationNowTime; } } | 115 | |
116 | // True if initialized and ready to do simulation steps | ||
117 | private bool m_initialized = false; | ||
118 | |||
119 | // Flag which is true when processing taints. | ||
120 | // Not guaranteed to be correct all the time (don't depend on this) but good for debugging. | ||
121 | public bool InTaintTime { get; private set; } | ||
131 | 122 | ||
123 | // Pinned memory used to pass step information between managed and unmanaged | ||
132 | private int m_maxCollisionsPerFrame; | 124 | private int m_maxCollisionsPerFrame; |
133 | private CollisionDesc[] m_collisionArray; | 125 | private CollisionDesc[] m_collisionArray; |
134 | private GCHandle m_collisionArrayPinnedHandle; | 126 | private GCHandle m_collisionArrayPinnedHandle; |
@@ -137,14 +129,19 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
137 | private EntityProperties[] m_updateArray; | 129 | private EntityProperties[] m_updateArray; |
138 | private GCHandle m_updateArrayPinnedHandle; | 130 | private GCHandle m_updateArrayPinnedHandle; |
139 | 131 | ||
140 | private bool _meshSculptedPrim = true; // cause scuplted prims to get meshed | 132 | public bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed |
141 | private bool _forceSimplePrimMeshing = false; // if a cube or sphere, let Bullet do internal shapes | 133 | public bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes |
134 | public bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects | ||
142 | 135 | ||
143 | public float PID_D { get; private set; } // derivative | 136 | public float PID_D { get; private set; } // derivative |
144 | public float PID_P { get; private set; } // proportional | 137 | public float PID_P { get; private set; } // proportional |
145 | 138 | ||
146 | public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero | 139 | public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero |
147 | public const uint GROUNDPLANE_ID = 1; | 140 | public const uint GROUNDPLANE_ID = 1; |
141 | public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here | ||
142 | |||
143 | private float m_waterLevel; | ||
144 | public BSTerrainManager TerrainManager { get; private set; } | ||
148 | 145 | ||
149 | public ConfigurationParameters Params | 146 | public ConfigurationParameters Params |
150 | { | 147 | { |
@@ -154,13 +151,18 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
154 | { | 151 | { |
155 | get { return new Vector3(0f, 0f, Params.gravity); } | 152 | get { return new Vector3(0f, 0f, Params.gravity); } |
156 | } | 153 | } |
157 | 154 | // Just the Z value of the gravity | |
158 | private float m_maximumObjectMass; | 155 | public float DefaultGravityZ |
159 | public float MaximumObjectMass | ||
160 | { | 156 | { |
161 | get { return m_maximumObjectMass; } | 157 | get { return Params.gravity; } |
162 | } | 158 | } |
163 | 159 | ||
160 | public float MaximumObjectMass { get; private set; } | ||
161 | |||
162 | // When functions in the unmanaged code must be called, it is only | ||
163 | // done at a known time just before the simulation step. The taint | ||
164 | // system saves all these function calls and executes them in | ||
165 | // order before the simulation. | ||
164 | public delegate void TaintCallback(); | 166 | public delegate void TaintCallback(); |
165 | private struct TaintCallbackEntry | 167 | private struct TaintCallbackEntry |
166 | { | 168 | { |
@@ -172,15 +174,19 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
172 | callback = c; | 174 | callback = c; |
173 | } | 175 | } |
174 | } | 176 | } |
175 | private List<TaintCallbackEntry> _taintedObjects; | 177 | private Object _taintLock = new Object(); // lock for using the next object |
176 | private Object _taintLock = new Object(); | 178 | private List<TaintCallbackEntry> _taintOperations; |
179 | private Dictionary<string, TaintCallbackEntry> _postTaintOperations; | ||
180 | private List<TaintCallbackEntry> _postStepOperations; | ||
177 | 181 | ||
178 | // A pointer to an instance if this structure is passed to the C++ code | 182 | // A pointer to an instance if this structure is passed to the C++ code |
183 | // Used to pass basic configuration values to the unmanaged code. | ||
179 | ConfigurationParameters[] m_params; | 184 | ConfigurationParameters[] m_params; |
180 | GCHandle m_paramsHandle; | 185 | GCHandle m_paramsHandle; |
181 | 186 | ||
182 | public bool ShouldDebugLog { get; private set; } | 187 | // Handle to the callback used by the unmanaged code to call into the managed code. |
183 | 188 | // Used for debug logging. | |
189 | // Need to store the handle in a persistant variable so it won't be freed. | ||
184 | private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle; | 190 | private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle; |
185 | 191 | ||
186 | // Sometimes you just have to log everything. | 192 | // Sometimes you just have to log everything. |
@@ -189,17 +195,26 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
189 | private string m_physicsLoggingDir; | 195 | private string m_physicsLoggingDir; |
190 | private string m_physicsLoggingPrefix; | 196 | private string m_physicsLoggingPrefix; |
191 | private int m_physicsLoggingFileMinutes; | 197 | private int m_physicsLoggingFileMinutes; |
198 | // 'true' of the vehicle code is to log lots of details | ||
199 | public bool VehicleLoggingEnabled { get; private set; } | ||
192 | 200 | ||
193 | private bool m_vehicleLoggingEnabled; | 201 | #region Construction and Initialization |
194 | public bool VehicleLoggingEnabled { get { return m_vehicleLoggingEnabled; } } | ||
195 | |||
196 | public BSScene(string identifier) | 202 | public BSScene(string identifier) |
197 | { | 203 | { |
198 | m_initialized = false; | 204 | m_initialized = false; |
205 | // we are passed the name of the region we're working for. | ||
206 | RegionName = identifier; | ||
199 | } | 207 | } |
200 | 208 | ||
201 | public override void Initialise(IMesher meshmerizer, IConfigSource config) | 209 | public override void Initialise(IMesher meshmerizer, IConfigSource config) |
202 | { | 210 | { |
211 | mesher = meshmerizer; | ||
212 | _taintOperations = new List<TaintCallbackEntry>(); | ||
213 | _postTaintOperations = new Dictionary<string, TaintCallbackEntry>(); | ||
214 | _postStepOperations = new List<TaintCallbackEntry>(); | ||
215 | PhysObjects = new Dictionary<uint, BSPhysObject>(); | ||
216 | Shapes = new BSShapeCollection(this); | ||
217 | |||
203 | // Allocate pinned memory to pass parameters. | 218 | // Allocate pinned memory to pass parameters. |
204 | m_params = new ConfigurationParameters[1]; | 219 | m_params = new ConfigurationParameters[1]; |
205 | m_paramsHandle = GCHandle.Alloc(m_params, GCHandleType.Pinned); | 220 | m_paramsHandle = GCHandle.Alloc(m_params, GCHandleType.Pinned); |
@@ -215,7 +230,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
215 | 230 | ||
216 | // Enable very detailed logging. | 231 | // Enable very detailed logging. |
217 | // By creating an empty logger when not logging, the log message invocation code | 232 | // By creating an empty logger when not logging, the log message invocation code |
218 | // can be left in and every call doesn't have to check for null. | 233 | // can be left in and every call doesn't have to check for null. |
219 | if (m_physicsLoggingEnabled) | 234 | if (m_physicsLoggingEnabled) |
220 | { | 235 | { |
221 | PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); | 236 | PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); |
@@ -225,39 +240,43 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
225 | PhysicsLogging = new Logging.LogWriter(); | 240 | PhysicsLogging = new Logging.LogWriter(); |
226 | } | 241 | } |
227 | 242 | ||
228 | // Get the version of the DLL | 243 | // If Debug logging level, enable logging from the unmanaged code |
229 | // TODO: this doesn't work yet. Something wrong with marshaling the returned string. | 244 | m_DebugLogCallbackHandle = null; |
230 | // BulletSimVersion = BulletSimAPI.GetVersion(); | ||
231 | // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion); | ||
232 | |||
233 | // if Debug, enable logging from the unmanaged code | ||
234 | if (m_log.IsDebugEnabled || PhysicsLogging.Enabled) | 245 | if (m_log.IsDebugEnabled || PhysicsLogging.Enabled) |
235 | { | 246 | { |
236 | m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader); | 247 | m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader); |
237 | if (PhysicsLogging.Enabled) | 248 | if (PhysicsLogging.Enabled) |
249 | // The handle is saved in a variable to make sure it doesn't get freed after this call | ||
238 | m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLoggerPhysLog); | 250 | m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLoggerPhysLog); |
239 | else | 251 | else |
240 | m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger); | 252 | m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger); |
241 | // the handle is saved in a variable to make sure it doesn't get freed after this call | ||
242 | BulletSimAPI.SetDebugLogCallback(m_DebugLogCallbackHandle); | ||
243 | } | 253 | } |
244 | 254 | ||
245 | _taintedObjects = new List<TaintCallbackEntry>(); | 255 | // Get the version of the DLL |
256 | // TODO: this doesn't work yet. Something wrong with marshaling the returned string. | ||
257 | // BulletSimVersion = BulletSimAPI.GetVersion(); | ||
258 | // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion); | ||
246 | 259 | ||
247 | mesher = meshmerizer; | 260 | // The bounding box for the simulated world. The origin is 0,0,0 unless we're |
248 | // The bounding box for the simulated world | 261 | // a child in a mega-region. |
249 | Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, 8192f); | 262 | // Bullet actually doesn't care about the extents of the simulated |
263 | // area. It tracks active objects no matter where they are. | ||
264 | Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); | ||
250 | 265 | ||
251 | // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); | 266 | // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); |
252 | m_worldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(), | 267 | World = new BulletSim(0, this, BulletSimAPI.Initialize2(worldExtent, m_paramsHandle.AddrOfPinnedObject(), |
253 | m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(), | 268 | m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(), |
254 | m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject()); | 269 | m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject(), |
270 | m_DebugLogCallbackHandle)); | ||
271 | |||
272 | Constraints = new BSConstraintCollection(World); | ||
255 | 273 | ||
256 | // Initialization to support the transition to a new API which puts most of the logic | 274 | TerrainManager = new BSTerrainManager(this); |
257 | // into the C# code so it is easier to modify and add to. | 275 | TerrainManager.CreateInitialGroundPlaneAndTerrain(); |
258 | m_worldSim = new BulletSim(m_worldID, this, BulletSimAPI.GetSimHandle2(m_worldID)); | ||
259 | m_constraintCollection = new BSConstraintCollection(World); | ||
260 | 276 | ||
277 | m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)Params.linksetImplementation); | ||
278 | |||
279 | InTaintTime = false; | ||
261 | m_initialized = true; | 280 | m_initialized = true; |
262 | } | 281 | } |
263 | 282 | ||
@@ -281,10 +300,13 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
281 | // Very detailed logging for physics debugging | 300 | // Very detailed logging for physics debugging |
282 | m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false); | 301 | m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false); |
283 | m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); | 302 | m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); |
284 | m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-"); | 303 | m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); |
285 | m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); | 304 | m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); |
286 | // Very detailed logging for vehicle debugging | 305 | // Very detailed logging for vehicle debugging |
287 | m_vehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); | 306 | VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); |
307 | |||
308 | // Do any replacements in the parameters | ||
309 | m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); | ||
288 | } | 310 | } |
289 | } | 311 | } |
290 | } | 312 | } |
@@ -309,13 +331,51 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
309 | { | 331 | { |
310 | m_log.Debug("[BULLETS UNMANAGED]:" + msg); | 332 | m_log.Debug("[BULLETS UNMANAGED]:" + msg); |
311 | } | 333 | } |
312 | 334 | ||
313 | // Called directly from unmanaged code so don't do much | 335 | // Called directly from unmanaged code so don't do much |
314 | private void BulletLoggerPhysLog(string msg) | 336 | private void BulletLoggerPhysLog(string msg) |
315 | { | 337 | { |
316 | PhysicsLogging.Write("[BULLETS UNMANAGED]:" + msg); | 338 | DetailLog("[BULLETS UNMANAGED]:" + msg); |
317 | } | 339 | } |
318 | 340 | ||
341 | public override void Dispose() | ||
342 | { | ||
343 | // m_log.DebugFormat("{0}: Dispose()", LogHeader); | ||
344 | |||
345 | // make sure no stepping happens while we're deleting stuff | ||
346 | m_initialized = false; | ||
347 | |||
348 | TerrainManager.ReleaseGroundPlaneAndTerrain(); | ||
349 | |||
350 | foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects) | ||
351 | { | ||
352 | kvp.Value.Destroy(); | ||
353 | } | ||
354 | PhysObjects.Clear(); | ||
355 | |||
356 | // Now that the prims are all cleaned up, there should be no constraints left | ||
357 | if (Constraints != null) | ||
358 | { | ||
359 | Constraints.Dispose(); | ||
360 | Constraints = null; | ||
361 | } | ||
362 | |||
363 | if (Shapes != null) | ||
364 | { | ||
365 | Shapes.Dispose(); | ||
366 | Shapes = null; | ||
367 | } | ||
368 | |||
369 | // Anything left in the unmanaged code should be cleaned out | ||
370 | BulletSimAPI.Shutdown2(World.ptr); | ||
371 | |||
372 | // Not logging any more | ||
373 | PhysicsLogging.Close(); | ||
374 | } | ||
375 | #endregion // Construction and Initialization | ||
376 | |||
377 | #region Prim and Avatar addition and removal | ||
378 | |||
319 | public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying) | 379 | public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying) |
320 | { | 380 | { |
321 | m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader); | 381 | m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader); |
@@ -329,7 +389,13 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
329 | if (!m_initialized) return null; | 389 | if (!m_initialized) return null; |
330 | 390 | ||
331 | BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); | 391 | BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); |
332 | lock (m_avatars) m_avatars.Add(localID, actor); | 392 | lock (PhysObjects) PhysObjects.Add(localID, actor); |
393 | |||
394 | // TODO: Remove kludge someday. | ||
395 | // We must generate a collision for avatars whether they collide or not. | ||
396 | // This is required by OpenSim to update avatar animations, etc. | ||
397 | lock (m_avatars) m_avatars.Add(actor); | ||
398 | |||
333 | return actor; | 399 | return actor; |
334 | } | 400 | } |
335 | 401 | ||
@@ -344,7 +410,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
344 | { | 410 | { |
345 | try | 411 | try |
346 | { | 412 | { |
347 | lock (m_avatars) m_avatars.Remove(actor.LocalID); | 413 | lock (PhysObjects) PhysObjects.Remove(actor.LocalID); |
414 | // Remove kludge someday | ||
415 | lock (m_avatars) m_avatars.Remove(bsactor); | ||
348 | } | 416 | } |
349 | catch (Exception e) | 417 | catch (Exception e) |
350 | { | 418 | { |
@@ -362,11 +430,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
362 | BSPrim bsprim = prim as BSPrim; | 430 | BSPrim bsprim = prim as BSPrim; |
363 | if (bsprim != null) | 431 | if (bsprim != null) |
364 | { | 432 | { |
365 | // DetailLog("{0},RemovePrim,call", bsprim.LocalID); | 433 | DetailLog("{0},RemovePrim,call", bsprim.LocalID); |
366 | // m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID); | 434 | // m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID); |
367 | try | 435 | try |
368 | { | 436 | { |
369 | lock (m_prims) m_prims.Remove(bsprim.LocalID); | 437 | lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID); |
370 | } | 438 | } |
371 | catch (Exception e) | 439 | catch (Exception e) |
372 | { | 440 | { |
@@ -388,18 +456,21 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
388 | 456 | ||
389 | if (!m_initialized) return null; | 457 | if (!m_initialized) return null; |
390 | 458 | ||
391 | // DetailLog("{0},AddPrimShape,call", localID); | 459 | DetailLog("{0},AddPrimShape,call", localID); |
392 | 460 | ||
393 | BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical); | 461 | BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical); |
394 | lock (m_prims) m_prims.Add(localID, prim); | 462 | lock (PhysObjects) PhysObjects.Add(localID, prim); |
395 | return prim; | 463 | return prim; |
396 | } | 464 | } |
397 | 465 | ||
398 | // This is a call from the simulator saying that some physical property has been updated. | 466 | // This is a call from the simulator saying that some physical property has been updated. |
399 | // The BulletSim driver senses the changing of relevant properties so this taint | 467 | // The BulletSim driver senses the changing of relevant properties so this taint |
400 | // information call is not needed. | 468 | // information call is not needed. |
401 | public override void AddPhysicsActorTaint(PhysicsActor prim) { } | 469 | public override void AddPhysicsActorTaint(PhysicsActor prim) { } |
402 | 470 | ||
471 | #endregion // Prim and Avatar addition and removal | ||
472 | |||
473 | #region Simulation | ||
403 | // Simulate one timestep | 474 | // Simulate one timestep |
404 | public override float Simulate(float timeStep) | 475 | public override float Simulate(float timeStep) |
405 | { | 476 | { |
@@ -408,14 +479,14 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
408 | int collidersCount = 0; | 479 | int collidersCount = 0; |
409 | IntPtr collidersPtr; | 480 | IntPtr collidersPtr; |
410 | 481 | ||
411 | LastSimulatedTimestep = timeStep; | 482 | int beforeTime = 0; |
483 | int simTime = 0; | ||
412 | 484 | ||
413 | // prevent simulation until we've been initialized | 485 | // prevent simulation until we've been initialized |
414 | if (!m_initialized) return 10.0f; | 486 | if (!m_initialized) return 5.0f; |
415 | |||
416 | int simulateStartTime = Util.EnvironmentTickCount(); | ||
417 | 487 | ||
418 | // update the prim states while we know the physics engine is not busy | 488 | // update the prim states while we know the physics engine is not busy |
489 | int numTaints = _taintOperations.Count; | ||
419 | ProcessTaints(); | 490 | ProcessTaints(); |
420 | 491 | ||
421 | // Some of the prims operate with special vehicle properties | 492 | // Some of the prims operate with special vehicle properties |
@@ -425,25 +496,34 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
425 | // step the physical world one interval | 496 | // step the physical world one interval |
426 | m_simulationStep++; | 497 | m_simulationStep++; |
427 | int numSubSteps = 0; | 498 | int numSubSteps = 0; |
499 | |||
428 | try | 500 | try |
429 | { | 501 | { |
430 | numSubSteps = BulletSimAPI.PhysicsStep(m_worldID, timeStep, m_maxSubSteps, m_fixedTimeStep, | 502 | if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG |
503 | if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); | ||
504 | |||
505 | numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep, | ||
431 | out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); | 506 | out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); |
432 | // DetailLog("{0},Simulate,call, substeps={1}, updates={2}, colliders={3}", DetailLogZero, numSubSteps, updatedEntityCount, collidersCount); | 507 | |
508 | if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime); | ||
509 | DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}", | ||
510 | DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount); | ||
511 | if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG | ||
433 | } | 512 | } |
434 | catch (Exception e) | 513 | catch (Exception e) |
435 | { | 514 | { |
436 | m_log.WarnFormat("{0},PhysicsStep Exception: substeps={1}, updates={2}, colliders={3}, e={4}", LogHeader, numSubSteps, updatedEntityCount, collidersCount, e); | 515 | m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}", |
437 | // DetailLog("{0},PhysicsStepException,call, substeps={1}, updates={2}, colliders={3}", DetailLogZero, numSubSteps, updatedEntityCount, collidersCount); | 516 | LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e); |
438 | // updatedEntityCount = 0; | 517 | DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}", |
518 | DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount); | ||
519 | updatedEntityCount = 0; | ||
439 | collidersCount = 0; | 520 | collidersCount = 0; |
440 | } | 521 | } |
441 | 522 | ||
442 | |||
443 | // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in | 523 | // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in |
444 | 524 | ||
445 | // Get a value for 'now' so all the collision and update routines don't have to get their own | 525 | // Get a value for 'now' so all the collision and update routines don't have to get their own |
446 | m_simulationNowTime = Util.EnvironmentTickCount(); | 526 | SimulationNowTime = Util.EnvironmentTickCount(); |
447 | 527 | ||
448 | // If there were collisions, process them by sending the event to the prim. | 528 | // If there were collisions, process them by sending the event to the prim. |
449 | // Collisions must be processed before updates. | 529 | // Collisions must be processed before updates. |
@@ -462,19 +542,32 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
462 | 542 | ||
463 | // The above SendCollision's batch up the collisions on the objects. | 543 | // The above SendCollision's batch up the collisions on the objects. |
464 | // Now push the collisions into the simulator. | 544 | // Now push the collisions into the simulator. |
465 | foreach (BSPrim bsp in m_primsWithCollisions) | 545 | if (ObjectsWithCollisions.Count > 0) |
466 | bsp.SendCollisions(); | 546 | { |
467 | m_primsWithCollisions.Clear(); | 547 | foreach (BSPhysObject bsp in ObjectsWithCollisions) |
468 | 548 | if (!bsp.SendCollisions()) | |
469 | // This is a kludge to get avatar movement updated. | 549 | { |
470 | // Don't send collisions only if there were collisions -- send everytime. | 550 | // If the object is done colliding, see that it's removed from the colliding list |
471 | // ODE sends collisions even if there are none and this is used to update | 551 | ObjectsWithNoMoreCollisions.Add(bsp); |
472 | // avatar animations and stuff. | 552 | } |
473 | // foreach (BSCharacter bsc in m_avatarsWithCollisions) | 553 | } |
474 | // bsc.SendCollisions(); | 554 | |
475 | foreach (KeyValuePair<uint, BSCharacter> kvp in m_avatars) | 555 | // This is a kludge to get avatar movement updates. |
476 | kvp.Value.SendCollisions(); | 556 | // The simulator expects collisions for avatars even if there are have been no collisions. |
477 | m_avatarsWithCollisions.Clear(); | 557 | // The event updates avatar animations and stuff. |
558 | // If you fix avatar animation updates, remove this overhead and let normal collision processing happen. | ||
559 | foreach (BSPhysObject bsp in m_avatars) | ||
560 | if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice | ||
561 | bsp.SendCollisions(); | ||
562 | |||
563 | // Objects that are done colliding are removed from the ObjectsWithCollisions list. | ||
564 | // Not done above because it is inside an iteration of ObjectWithCollisions. | ||
565 | if (ObjectsWithNoMoreCollisions.Count > 0) | ||
566 | { | ||
567 | foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) | ||
568 | ObjectsWithCollisions.Remove(po); | ||
569 | ObjectsWithNoMoreCollisions.Clear(); | ||
570 | } | ||
478 | 571 | ||
479 | // If any of the objects had updated properties, tell the object it has been changed by the physics engine | 572 | // If any of the objects had updated properties, tell the object it has been changed by the physics engine |
480 | if (updatedEntityCount > 0) | 573 | if (updatedEntityCount > 0) |
@@ -482,320 +575,316 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
482 | for (int ii = 0; ii < updatedEntityCount; ii++) | 575 | for (int ii = 0; ii < updatedEntityCount; ii++) |
483 | { | 576 | { |
484 | EntityProperties entprop = m_updateArray[ii]; | 577 | EntityProperties entprop = m_updateArray[ii]; |
485 | BSPrim prim; | 578 | BSPhysObject pobj; |
486 | if (m_prims.TryGetValue(entprop.ID, out prim)) | 579 | if (PhysObjects.TryGetValue(entprop.ID, out pobj)) |
487 | { | ||
488 | prim.UpdateProperties(entprop); | ||
489 | continue; | ||
490 | } | ||
491 | BSCharacter actor; | ||
492 | if (m_avatars.TryGetValue(entprop.ID, out actor)) | ||
493 | { | 580 | { |
494 | actor.UpdateProperties(entprop); | 581 | pobj.UpdateProperties(entprop); |
495 | continue; | ||
496 | } | 582 | } |
497 | } | 583 | } |
498 | } | 584 | } |
499 | 585 | ||
500 | // If enabled, call into the physics engine to dump statistics | 586 | ProcessPostStepTaints(); |
501 | if (m_detailedStatsStep > 0) | ||
502 | { | ||
503 | if ((m_simulationStep % m_detailedStatsStep) == 0) | ||
504 | { | ||
505 | BulletSimAPI.DumpBulletStatistics(); | ||
506 | } | ||
507 | } | ||
508 | 587 | ||
509 | // this is a waste since the outside routine also calcuates the physics simulation | 588 | // This causes the unmanaged code to output ALL the values found in ALL the objects in the world. |
510 | // period. TODO: There should be a way of computing physics frames from simulator computation. | 589 | // Only enable this in a limited test world with few objects. |
511 | // long simulateTotalTime = Util.EnvironmentTickCountSubtract(simulateStartTime); | 590 | // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG |
512 | // return (timeStep * (float)simulateTotalTime); | 591 | |
513 | 592 | // The physics engine returns the number of milliseconds it simulated this call. | |
514 | // TODO: FIX THIS: fps calculation possibly wrong. | 593 | // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. |
515 | // This calculation says 1/timeStep is the ideal frame rate. Any time added to | 594 | // We multiply by 55 to give a recognizable running rate (55 or less). |
516 | // that by the physics simulation gives a slower frame rate. | 595 | return numSubSteps * m_fixedTimeStep * 1000 * 55; |
517 | long totalSimulationTime = Util.EnvironmentTickCountSubtract(simulateStartTime); | 596 | // return timeStep * 1000 * 55; |
518 | if (totalSimulationTime >= timeStep) | ||
519 | return 0; | ||
520 | return 1f / (timeStep + totalSimulationTime); | ||
521 | } | 597 | } |
522 | 598 | ||
523 | // Something has collided | 599 | // Something has collided |
524 | private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penitration) | 600 | private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration) |
525 | { | 601 | { |
526 | if (localID == TERRAIN_ID || localID == GROUNDPLANE_ID) | 602 | if (localID <= TerrainManager.HighestTerrainID) |
527 | { | 603 | { |
528 | return; // don't send collisions to the terrain | 604 | return; // don't send collisions to the terrain |
529 | } | 605 | } |
530 | 606 | ||
531 | ActorTypes type = ActorTypes.Prim; | 607 | BSPhysObject collider; |
532 | if (collidingWith == TERRAIN_ID || collidingWith == GROUNDPLANE_ID) | 608 | if (!PhysObjects.TryGetValue(localID, out collider)) |
533 | type = ActorTypes.Ground; | 609 | { |
534 | else if (m_avatars.ContainsKey(collidingWith)) | 610 | // If the object that is colliding cannot be found, just ignore the collision. |
535 | type = ActorTypes.Agent; | 611 | DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith); |
536 | |||
537 | BSPrim prim; | ||
538 | if (m_prims.TryGetValue(localID, out prim)) { | ||
539 | prim.Collide(collidingWith, type, collidePoint, collideNormal, penitration); | ||
540 | m_primsWithCollisions.Add(prim); | ||
541 | return; | 612 | return; |
542 | } | 613 | } |
543 | BSCharacter actor; | 614 | |
544 | if (m_avatars.TryGetValue(localID, out actor)) { | 615 | // The terrain is not in the physical object list so 'collidee' can be null when Collide() is called. |
545 | actor.Collide(collidingWith, type, collidePoint, collideNormal, penitration); | 616 | BSPhysObject collidee = null; |
546 | m_avatarsWithCollisions.Add(actor); | 617 | PhysObjects.TryGetValue(collidingWith, out collidee); |
547 | return; | 618 | |
619 | // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); | ||
620 | |||
621 | if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration)) | ||
622 | { | ||
623 | // If a collision was posted, remember to send it to the simulator | ||
624 | ObjectsWithCollisions.Add(collider); | ||
548 | } | 625 | } |
626 | |||
549 | return; | 627 | return; |
550 | } | 628 | } |
551 | 629 | ||
630 | #endregion // Simulation | ||
631 | |||
552 | public override void GetResults() { } | 632 | public override void GetResults() { } |
553 | 633 | ||
634 | #region Terrain | ||
635 | |||
554 | public override void SetTerrain(float[] heightMap) { | 636 | public override void SetTerrain(float[] heightMap) { |
555 | m_heightMap = heightMap; | 637 | TerrainManager.SetTerrain(heightMap); |
556 | this.TaintedObject("BSScene.SetTerrain", delegate() | ||
557 | { | ||
558 | BulletSimAPI.SetHeightmap(m_worldID, m_heightMap); | ||
559 | }); | ||
560 | } | 638 | } |
561 | 639 | ||
562 | // Someday we will have complex terrain with caves and tunnels | 640 | public override void SetWaterLevel(float baseheight) |
563 | // For the moment, it's flat and convex | ||
564 | public float GetTerrainHeightAtXYZ(Vector3 loc) | ||
565 | { | 641 | { |
566 | return GetTerrainHeightAtXY(loc.X, loc.Y); | 642 | m_waterLevel = baseheight; |
643 | } | ||
644 | // Someday.... | ||
645 | public float GetWaterLevelAtXYZ(Vector3 loc) | ||
646 | { | ||
647 | return m_waterLevel; | ||
567 | } | 648 | } |
568 | 649 | ||
569 | public float GetTerrainHeightAtXY(float tX, float tY) | 650 | public override void DeleteTerrain() |
570 | { | 651 | { |
571 | if (tX < 0 || tX >= Constants.RegionSize || tY < 0 || tY >= Constants.RegionSize) | 652 | // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader); |
572 | return 30; | ||
573 | return m_heightMap[((int)tX) * Constants.RegionSize + ((int)tY)]; | ||
574 | } | 653 | } |
575 | 654 | ||
576 | public override void SetWaterLevel(float baseheight) | 655 | // Although no one seems to check this, I do support combining. |
656 | public override bool SupportsCombining() | ||
577 | { | 657 | { |
578 | m_waterLevel = baseheight; | 658 | return TerrainManager.SupportsCombining(); |
579 | // TODO: pass to physics engine so things will float? | ||
580 | } | 659 | } |
581 | public float GetWaterLevel() | 660 | // This call says I am a child to region zero in a mega-region. 'pScene' is that |
661 | // of region zero, 'offset' is my offset from regions zero's origin, and | ||
662 | // 'extents' is the largest XY that is handled in my region. | ||
663 | public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents) | ||
582 | { | 664 | { |
583 | return m_waterLevel; | 665 | TerrainManager.Combine(pScene, offset, extents); |
584 | } | 666 | } |
585 | 667 | ||
586 | public override void DeleteTerrain() | 668 | // Unhook all the combining that I know about. |
669 | public override void UnCombine(PhysicsScene pScene) | ||
587 | { | 670 | { |
588 | // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader); | 671 | TerrainManager.UnCombine(pScene); |
589 | } | 672 | } |
590 | 673 | ||
591 | public override void Dispose() | 674 | #endregion // Terrain |
675 | |||
676 | public override Dictionary<uint, float> GetTopColliders() | ||
592 | { | 677 | { |
593 | // m_log.DebugFormat("{0}: Dispose()", LogHeader); | 678 | return new Dictionary<uint, float>(); |
679 | } | ||
594 | 680 | ||
595 | // make sure no stepping happens while we're deleting stuff | 681 | public override bool IsThreaded { get { return false; } } |
596 | m_initialized = false; | ||
597 | 682 | ||
598 | foreach (KeyValuePair<uint, BSCharacter> kvp in m_avatars) | 683 | #region Taints |
599 | { | ||
600 | kvp.Value.Destroy(); | ||
601 | } | ||
602 | m_avatars.Clear(); | ||
603 | 684 | ||
604 | foreach (KeyValuePair<uint, BSPrim> kvp in m_prims) | 685 | // Calls to the PhysicsActors can't directly call into the physics engine |
605 | { | 686 | // because it might be busy. We delay changes to a known time. |
606 | kvp.Value.Destroy(); | 687 | // We rely on C#'s closure to save and restore the context for the delegate. |
607 | } | 688 | public void TaintedObject(String ident, TaintCallback callback) |
608 | m_prims.Clear(); | 689 | { |
690 | if (!m_initialized) return; | ||
609 | 691 | ||
610 | // Now that the prims are all cleaned up, there should be no constraints left | 692 | lock (_taintLock) |
611 | if (m_constraintCollection != null) | ||
612 | { | 693 | { |
613 | m_constraintCollection.Dispose(); | 694 | _taintOperations.Add(new TaintCallbackEntry(ident, callback)); |
614 | m_constraintCollection = null; | ||
615 | } | 695 | } |
616 | 696 | ||
617 | // Anything left in the unmanaged code should be cleaned out | 697 | return; |
618 | BulletSimAPI.Shutdown(WorldID); | ||
619 | |||
620 | // Not logging any more | ||
621 | PhysicsLogging.Close(); | ||
622 | } | 698 | } |
623 | 699 | ||
624 | public override Dictionary<uint, float> GetTopColliders() | 700 | // Sometimes a potentially tainted operation can be used in and out of taint time. |
701 | // This routine executes the command immediately if in taint-time otherwise it is queued. | ||
702 | public void TaintedObject(bool inTaintTime, string ident, TaintCallback callback) | ||
625 | { | 703 | { |
626 | return new Dictionary<uint, float>(); | 704 | if (inTaintTime) |
705 | callback(); | ||
706 | else | ||
707 | TaintedObject(ident, callback); | ||
627 | } | 708 | } |
628 | 709 | ||
629 | public override bool IsThreaded { get { return false; } } | 710 | // When someone tries to change a property on a BSPrim or BSCharacter, the object queues |
630 | 711 | // a callback into itself to do the actual property change. That callback is called | |
631 | /// <summary> | 712 | // here just before the physics engine is called to step the simulation. |
632 | /// Routine to figure out if we need to mesh this prim with our mesher | 713 | public void ProcessTaints() |
633 | /// </summary> | ||
634 | /// <param name="pbs"></param> | ||
635 | /// <returns>true if the prim needs meshing</returns> | ||
636 | public bool NeedsMeshing(PrimitiveBaseShape pbs) | ||
637 | { | 714 | { |
638 | // most of this is redundant now as the mesher will return null if it cant mesh a prim | 715 | InTaintTime = true; // Only used for debugging so locking is not necessary. |
639 | // but we still need to check for sculptie meshing being enabled so this is the most | 716 | ProcessRegularTaints(); |
640 | // convenient place to do it for now... | 717 | ProcessPostTaintTaints(); |
641 | 718 | InTaintTime = false; | |
642 | // int iPropertiesNotSupportedDefault = 0; | 719 | } |
643 | |||
644 | if (pbs.SculptEntry && !_meshSculptedPrim) | ||
645 | { | ||
646 | // Render sculpties as boxes | ||
647 | return false; | ||
648 | } | ||
649 | 720 | ||
650 | // if it's a standard box or sphere with no cuts, hollows, twist or top shear, return false since Bullet | 721 | private void ProcessRegularTaints() |
651 | // can use an internal representation for the prim | 722 | { |
652 | if (!_forceSimplePrimMeshing) | 723 | if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process |
653 | { | 724 | { |
654 | if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) | 725 | /* |
655 | || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 | 726 | // Code to limit the number of taints processed per step. Meant to limit step time. |
656 | && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)) | 727 | // Unsure if a good idea as code assumes that taints are done before the step. |
728 | int taintCount = m_taintsToProcessPerStep; | ||
729 | TaintCallbackEntry oneCallback = new TaintCallbackEntry(); | ||
730 | while (_taintOperations.Count > 0 && taintCount-- > 0) | ||
657 | { | 731 | { |
658 | 732 | bool gotOne = false; | |
659 | if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 | 733 | lock (_taintLock) |
660 | && pbs.ProfileHollow == 0 | ||
661 | && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0 | ||
662 | && pbs.PathBegin == 0 && pbs.PathEnd == 0 | ||
663 | && pbs.PathTaperX == 0 && pbs.PathTaperY == 0 | ||
664 | && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 | ||
665 | && pbs.PathShearX == 0 && pbs.PathShearY == 0) | ||
666 | { | 734 | { |
667 | return false; | 735 | if (_taintOperations.Count > 0) |
736 | { | ||
737 | oneCallback = _taintOperations[0]; | ||
738 | _taintOperations.RemoveAt(0); | ||
739 | gotOne = true; | ||
740 | } | ||
741 | } | ||
742 | if (gotOne) | ||
743 | { | ||
744 | try | ||
745 | { | ||
746 | DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, oneCallback.ident); | ||
747 | oneCallback.callback(); | ||
748 | } | ||
749 | catch (Exception e) | ||
750 | { | ||
751 | DetailLog("{0},BSScene.ProcessTaints,doTaintException,id={1}", DetailLogZero, oneCallback.ident); // DEBUG DEBUG DEBUG | ||
752 | m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, oneCallback.ident, e); | ||
753 | } | ||
668 | } | 754 | } |
669 | } | 755 | } |
670 | } | 756 | if (_taintOperations.Count > 0) |
671 | |||
672 | /* TODO: verify that the mesher will now do all these shapes | ||
673 | if (pbs.ProfileHollow != 0) | ||
674 | iPropertiesNotSupportedDefault++; | ||
675 | |||
676 | if ((pbs.PathBegin != 0) || pbs.PathEnd != 0) | ||
677 | iPropertiesNotSupportedDefault++; | ||
678 | |||
679 | if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0)) | ||
680 | iPropertiesNotSupportedDefault++; | ||
681 | |||
682 | if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0) | ||
683 | iPropertiesNotSupportedDefault++; | ||
684 | |||
685 | if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100)) | ||
686 | iPropertiesNotSupportedDefault++; | ||
687 | |||
688 | if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0)) | ||
689 | iPropertiesNotSupportedDefault++; | ||
690 | |||
691 | if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight) | ||
692 | iPropertiesNotSupportedDefault++; | ||
693 | |||
694 | if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 && (pbs.Scale.X != pbs.Scale.Y || pbs.Scale.Y != pbs.Scale.Z || pbs.Scale.Z != pbs.Scale.X)) | ||
695 | iPropertiesNotSupportedDefault++; | ||
696 | |||
697 | if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1) | ||
698 | iPropertiesNotSupportedDefault++; | ||
699 | |||
700 | // test for torus | ||
701 | if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square) | ||
702 | { | ||
703 | if (pbs.PathCurve == (byte)Extrusion.Curve1) | ||
704 | { | 757 | { |
705 | iPropertiesNotSupportedDefault++; | 758 | DetailLog("{0},BSScene.ProcessTaints,leftTaintsOnList,numNotProcessed={1}", DetailLogZero, _taintOperations.Count); |
706 | } | 759 | } |
707 | } | 760 | */ |
708 | else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle) | 761 | |
709 | { | 762 | // swizzle a new list into the list location so we can process what's there |
710 | if (pbs.PathCurve == (byte)Extrusion.Straight) | 763 | List<TaintCallbackEntry> oldList; |
764 | lock (_taintLock) | ||
711 | { | 765 | { |
712 | iPropertiesNotSupportedDefault++; | 766 | oldList = _taintOperations; |
767 | _taintOperations = new List<TaintCallbackEntry>(); | ||
713 | } | 768 | } |
714 | // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits | 769 | |
715 | else if (pbs.PathCurve == (byte)Extrusion.Curve1) | 770 | foreach (TaintCallbackEntry tcbe in oldList) |
716 | { | 771 | { |
717 | iPropertiesNotSupportedDefault++; | 772 | try |
773 | { | ||
774 | DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG | ||
775 | tcbe.callback(); | ||
776 | } | ||
777 | catch (Exception e) | ||
778 | { | ||
779 | m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e); | ||
780 | } | ||
718 | } | 781 | } |
782 | oldList.Clear(); | ||
719 | } | 783 | } |
720 | else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) | 784 | } |
785 | |||
786 | // Schedule an update to happen after all the regular taints are processed. | ||
787 | // Note that new requests for the same operation ("ident") for the same object ("ID") | ||
788 | // will replace any previous operation by the same object. | ||
789 | public void PostTaintObject(String ident, uint ID, TaintCallback callback) | ||
790 | { | ||
791 | string uniqueIdent = ident + "-" + ID.ToString(); | ||
792 | lock (_taintLock) | ||
721 | { | 793 | { |
722 | if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2) | 794 | _postTaintOperations[uniqueIdent] = new TaintCallbackEntry(uniqueIdent, callback); |
723 | { | ||
724 | iPropertiesNotSupportedDefault++; | ||
725 | } | ||
726 | } | 795 | } |
727 | else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle) | 796 | |
797 | return; | ||
798 | } | ||
799 | |||
800 | private void ProcessPostTaintTaints() | ||
801 | { | ||
802 | if (_postTaintOperations.Count > 0) | ||
728 | { | 803 | { |
729 | if (pbs.PathCurve == (byte)Extrusion.Straight) | 804 | Dictionary<string, TaintCallbackEntry> oldList; |
805 | lock (_taintLock) | ||
730 | { | 806 | { |
731 | iPropertiesNotSupportedDefault++; | 807 | oldList = _postTaintOperations; |
808 | _postTaintOperations = new Dictionary<string, TaintCallbackEntry>(); | ||
732 | } | 809 | } |
733 | else if (pbs.PathCurve == (byte)Extrusion.Curve1) | 810 | |
811 | foreach (KeyValuePair<string,TaintCallbackEntry> kvp in oldList) | ||
734 | { | 812 | { |
735 | iPropertiesNotSupportedDefault++; | 813 | try |
814 | { | ||
815 | DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG | ||
816 | kvp.Value.callback(); | ||
817 | } | ||
818 | catch (Exception e) | ||
819 | { | ||
820 | m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e); | ||
821 | } | ||
736 | } | 822 | } |
823 | oldList.Clear(); | ||
737 | } | 824 | } |
738 | if (iPropertiesNotSupportedDefault == 0) | ||
739 | { | ||
740 | return false; | ||
741 | } | ||
742 | */ | ||
743 | return true; | ||
744 | } | 825 | } |
745 | 826 | ||
746 | // Calls to the PhysicsActors can't directly call into the physics engine | 827 | public void PostStepTaintObject(String ident, TaintCallback callback) |
747 | // because it might be busy. We delay changes to a known time. | ||
748 | // We rely on C#'s closure to save and restore the context for the delegate. | ||
749 | public void TaintedObject(String ident, TaintCallback callback) | ||
750 | { | 828 | { |
751 | if (!m_initialized) return; | 829 | if (!m_initialized) return; |
752 | 830 | ||
753 | lock (_taintLock) | 831 | lock (_taintLock) |
754 | _taintedObjects.Add(new TaintCallbackEntry(ident, callback)); | 832 | { |
833 | _postStepOperations.Add(new TaintCallbackEntry(ident, callback)); | ||
834 | } | ||
835 | |||
755 | return; | 836 | return; |
756 | } | 837 | } |
757 | 838 | ||
758 | // When someone tries to change a property on a BSPrim or BSCharacter, the object queues | 839 | private void ProcessPostStepTaints() |
759 | // a callback into itself to do the actual property change. That callback is called | ||
760 | // here just before the physics engine is called to step the simulation. | ||
761 | public void ProcessTaints() | ||
762 | { | 840 | { |
763 | if (_taintedObjects.Count > 0) // save allocating new list if there is nothing to process | 841 | if (_postStepOperations.Count > 0) |
764 | { | 842 | { |
765 | // swizzle a new list into the list location so we can process what's there | ||
766 | List<TaintCallbackEntry> oldList; | 843 | List<TaintCallbackEntry> oldList; |
767 | lock (_taintLock) | 844 | lock (_taintLock) |
768 | { | 845 | { |
769 | oldList = _taintedObjects; | 846 | oldList = _postStepOperations; |
770 | _taintedObjects = new List<TaintCallbackEntry>(); | 847 | _postStepOperations = new List<TaintCallbackEntry>(); |
771 | } | 848 | } |
772 | 849 | ||
773 | foreach (TaintCallbackEntry tcbe in oldList) | 850 | foreach (TaintCallbackEntry tcbe in oldList) |
774 | { | 851 | { |
775 | try | 852 | try |
776 | { | 853 | { |
854 | DetailLog("{0},BSScene.ProcessPostStepTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG | ||
777 | tcbe.callback(); | 855 | tcbe.callback(); |
778 | } | 856 | } |
779 | catch (Exception e) | 857 | catch (Exception e) |
780 | { | 858 | { |
781 | m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e); | 859 | m_log.ErrorFormat("{0}: ProcessPostStepTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e); |
782 | } | 860 | } |
783 | } | 861 | } |
784 | oldList.Clear(); | 862 | oldList.Clear(); |
785 | } | 863 | } |
786 | } | 864 | } |
787 | 865 | ||
866 | // Only used for debugging. Does not change state of anything so locking is not necessary. | ||
867 | public bool AssertInTaintTime(string whereFrom) | ||
868 | { | ||
869 | if (!InTaintTime) | ||
870 | { | ||
871 | DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom); | ||
872 | m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom); | ||
873 | Util.PrintCallStack(); // Prints the stack into the DEBUG log file. | ||
874 | } | ||
875 | return InTaintTime; | ||
876 | } | ||
877 | |||
878 | #endregion // Taints | ||
879 | |||
788 | #region Vehicles | 880 | #region Vehicles |
789 | 881 | ||
790 | public void VehicleInSceneTypeChanged(BSPrim vehic, Vehicle newType) | 882 | public void VehicleInSceneTypeChanged(BSPrim vehic, Vehicle newType) |
791 | { | 883 | { |
792 | if (newType == Vehicle.TYPE_NONE) | 884 | RemoveVehiclePrim(vehic); |
885 | if (newType != Vehicle.TYPE_NONE) | ||
793 | { | 886 | { |
794 | RemoveVehiclePrim(vehic); | 887 | // make it so the scene will call us each tick to do vehicle things |
795 | } | ||
796 | else | ||
797 | { | ||
798 | // make it so the scene will call us each tick to do vehicle things | ||
799 | AddVehiclePrim(vehic); | 888 | AddVehiclePrim(vehic); |
800 | } | 889 | } |
801 | } | 890 | } |
@@ -827,21 +916,22 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
827 | } | 916 | } |
828 | 917 | ||
829 | // Some prims have extra vehicle actions | 918 | // Some prims have extra vehicle actions |
830 | // no locking because only called when physics engine is not busy | 919 | // Called at taint time! |
831 | private void ProcessVehicles(float timeStep) | 920 | private void ProcessVehicles(float timeStep) |
832 | { | 921 | { |
833 | foreach (BSPrim prim in m_vehicles) | 922 | foreach (BSPhysObject pobj in m_vehicles) |
834 | { | 923 | { |
835 | prim.StepVehicle(timeStep); | 924 | pobj.StepVehicle(timeStep); |
836 | } | 925 | } |
837 | } | 926 | } |
838 | #endregion Vehicles | 927 | #endregion Vehicles |
839 | 928 | ||
840 | #region Parameters | 929 | #region INI and command line parameter processing |
841 | 930 | ||
842 | delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val); | 931 | delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val); |
843 | delegate float ParamGet(BSScene scene); | 932 | delegate float ParamGet(BSScene scene); |
844 | delegate void ParamSet(BSScene scene, string paramName, uint localID, float val); | 933 | delegate void ParamSet(BSScene scene, string paramName, uint localID, float val); |
934 | delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val); | ||
845 | 935 | ||
846 | private struct ParameterDefn | 936 | private struct ParameterDefn |
847 | { | 937 | { |
@@ -851,6 +941,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
851 | public ParamUser userParam; // get the value from the configuration file | 941 | public ParamUser userParam; // get the value from the configuration file |
852 | public ParamGet getter; // return the current value stored for this parameter | 942 | public ParamGet getter; // return the current value stored for this parameter |
853 | public ParamSet setter; // set the current value for this parameter | 943 | public ParamSet setter; // set the current value for this parameter |
944 | public SetOnObject onObject; // set the value on an object in the physical domain | ||
854 | public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s) | 945 | public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s) |
855 | { | 946 | { |
856 | name = n; | 947 | name = n; |
@@ -859,6 +950,17 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
859 | userParam = u; | 950 | userParam = u; |
860 | getter = g; | 951 | getter = g; |
861 | setter = s; | 952 | setter = s; |
953 | onObject = null; | ||
954 | } | ||
955 | public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o) | ||
956 | { | ||
957 | name = n; | ||
958 | desc = d; | ||
959 | defaultValue = v; | ||
960 | userParam = u; | ||
961 | getter = g; | ||
962 | setter = s; | ||
963 | onObject = o; | ||
862 | } | 964 | } |
863 | } | 965 | } |
864 | 966 | ||
@@ -869,7 +971,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
869 | // getters and setters. | 971 | // getters and setters. |
870 | // It is easiest to find an existing definition and copy it. | 972 | // It is easiest to find an existing definition and copy it. |
871 | // Parameter values are floats. Booleans are converted to a floating value. | 973 | // Parameter values are floats. Booleans are converted to a floating value. |
872 | // | 974 | // |
873 | // A ParameterDefn() takes the following parameters: | 975 | // A ParameterDefn() takes the following parameters: |
874 | // -- the text name of the parameter. This is used for console input and ini file. | 976 | // -- the text name of the parameter. This is used for console input and ini file. |
875 | // -- a short text description of the parameter. This shows up in the console listing. | 977 | // -- a short text description of the parameter. This shows up in the console listing. |
@@ -880,6 +982,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
880 | // | 982 | // |
881 | // The single letter parameters for the delegates are: | 983 | // The single letter parameters for the delegates are: |
882 | // s = BSScene | 984 | // s = BSScene |
985 | // o = BSPhysObject | ||
883 | // p = string parameter name | 986 | // p = string parameter name |
884 | // l = localID of referenced object | 987 | // l = localID of referenced object |
885 | // v = float value | 988 | // v = float value |
@@ -888,25 +991,40 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
888 | { | 991 | { |
889 | new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties", | 992 | new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties", |
890 | ConfigurationParameters.numericTrue, | 993 | ConfigurationParameters.numericTrue, |
891 | (s,cf,p,v) => { s._meshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); }, | 994 | (s,cf,p,v) => { s.ShouldMeshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); }, |
892 | (s) => { return s.NumericBool(s._meshSculptedPrim); }, | 995 | (s) => { return s.NumericBool(s.ShouldMeshSculptedPrim); }, |
893 | (s,p,l,v) => { s._meshSculptedPrim = s.BoolNumeric(v); } ), | 996 | (s,p,l,v) => { s.ShouldMeshSculptedPrim = s.BoolNumeric(v); } ), |
894 | new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects", | 997 | new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects", |
895 | ConfigurationParameters.numericFalse, | 998 | ConfigurationParameters.numericFalse, |
896 | (s,cf,p,v) => { s._forceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); }, | 999 | (s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); }, |
897 | (s) => { return s.NumericBool(s._forceSimplePrimMeshing); }, | 1000 | (s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); }, |
898 | (s,p,l,v) => { s._forceSimplePrimMeshing = s.BoolNumeric(v); } ), | 1001 | (s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ), |
1002 | new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects", | ||
1003 | ConfigurationParameters.numericTrue, | ||
1004 | (s,cf,p,v) => { s.ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, s.BoolNumeric(v)); }, | ||
1005 | (s) => { return s.NumericBool(s.ShouldUseHullsForPhysicalObjects); }, | ||
1006 | (s,p,l,v) => { s.ShouldUseHullsForPhysicalObjects = s.BoolNumeric(v); } ), | ||
899 | 1007 | ||
900 | new ParameterDefn("MeshLOD", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", | 1008 | new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", |
901 | 8f, | 1009 | 8f, |
902 | (s,cf,p,v) => { s.m_meshLOD = cf.GetInt(p, (int)v); }, | 1010 | (s,cf,p,v) => { s.MeshLOD = (float)cf.GetInt(p, (int)v); }, |
903 | (s) => { return (float)s.m_meshLOD; }, | 1011 | (s) => { return s.MeshLOD; }, |
904 | (s,p,l,v) => { s.m_meshLOD = (int)v; } ), | 1012 | (s,p,l,v) => { s.MeshLOD = v; } ), |
905 | new ParameterDefn("SculptLOD", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)", | 1013 | new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters", |
1014 | 16f, | ||
1015 | (s,cf,p,v) => { s.MeshMegaPrimLOD = (float)cf.GetInt(p, (int)v); }, | ||
1016 | (s) => { return s.MeshMegaPrimLOD; }, | ||
1017 | (s,p,l,v) => { s.MeshMegaPrimLOD = v; } ), | ||
1018 | new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD", | ||
1019 | 10f, | ||
1020 | (s,cf,p,v) => { s.MeshMegaPrimThreshold = (float)cf.GetInt(p, (int)v); }, | ||
1021 | (s) => { return s.MeshMegaPrimThreshold; }, | ||
1022 | (s,p,l,v) => { s.MeshMegaPrimThreshold = v; } ), | ||
1023 | new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)", | ||
906 | 32f, | 1024 | 32f, |
907 | (s,cf,p,v) => { s.m_sculptLOD = cf.GetInt(p, (int)v); }, | 1025 | (s,cf,p,v) => { s.SculptLOD = (float)cf.GetInt(p, (int)v); }, |
908 | (s) => { return (float)s.m_sculptLOD; }, | 1026 | (s) => { return s.SculptLOD; }, |
909 | (s,p,l,v) => { s.m_sculptLOD = (int)v; } ), | 1027 | (s,p,l,v) => { s.SculptLOD = v; } ), |
910 | 1028 | ||
911 | new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps", | 1029 | new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps", |
912 | 10f, | 1030 | 10f, |
@@ -928,11 +1046,16 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
928 | (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); }, | 1046 | (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); }, |
929 | (s) => { return (float)s.m_maxUpdatesPerFrame; }, | 1047 | (s) => { return (float)s.m_maxUpdatesPerFrame; }, |
930 | (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ), | 1048 | (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ), |
1049 | new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step", | ||
1050 | 500f, | ||
1051 | (s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); }, | ||
1052 | (s) => { return (float)s.m_taintsToProcessPerStep; }, | ||
1053 | (s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ), | ||
931 | new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", | 1054 | new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", |
932 | 10000.01f, | 1055 | 10000.01f, |
933 | (s,cf,p,v) => { s.m_maximumObjectMass = cf.GetFloat(p, v); }, | 1056 | (s,cf,p,v) => { s.MaximumObjectMass = cf.GetFloat(p, v); }, |
934 | (s) => { return (float)s.m_maximumObjectMass; }, | 1057 | (s) => { return (float)s.MaximumObjectMass; }, |
935 | (s,p,l,v) => { s.m_maximumObjectMass = v; } ), | 1058 | (s,p,l,v) => { s.MaximumObjectMass = v; } ), |
936 | 1059 | ||
937 | new ParameterDefn("PID_D", "Derivitive factor for motion smoothing", | 1060 | new ParameterDefn("PID_D", "Derivitive factor for motion smoothing", |
938 | 2200f, | 1061 | 2200f, |
@@ -969,104 +1092,128 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
969 | -9.80665f, | 1092 | -9.80665f, |
970 | (s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); }, | 1093 | (s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); }, |
971 | (s) => { return s.m_params[0].gravity; }, | 1094 | (s) => { return s.m_params[0].gravity; }, |
972 | (s,p,l,v) => { s.m_params[0].gravity = v; s.TaintedUpdateParameter(p,l,v); } ), | 1095 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].gravity, p, PhysParameterEntry.APPLY_TO_NONE, v); }, |
1096 | (s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ), | ||
973 | 1097 | ||
974 | 1098 | ||
975 | new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)", | 1099 | new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)", |
976 | 0f, | 1100 | 0f, |
977 | (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); }, | 1101 | (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); }, |
978 | (s) => { return s.m_params[0].linearDamping; }, | 1102 | (s) => { return s.m_params[0].linearDamping; }, |
979 | (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].linearDamping, p, l, v); } ), | 1103 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); }, |
1104 | (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, v, s.m_params[0].angularDamping); } ), | ||
980 | new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)", | 1105 | new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)", |
981 | 0f, | 1106 | 0f, |
982 | (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); }, | 1107 | (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); }, |
983 | (s) => { return s.m_params[0].angularDamping; }, | 1108 | (s) => { return s.m_params[0].angularDamping; }, |
984 | (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].angularDamping, p, l, v); } ), | 1109 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); }, |
1110 | (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, s.m_params[0].linearDamping, v); } ), | ||
985 | new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static", | 1111 | new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static", |
986 | 0.2f, | 1112 | 0.2f, |
987 | (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); }, | 1113 | (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); }, |
988 | (s) => { return s.m_params[0].deactivationTime; }, | 1114 | (s) => { return s.m_params[0].deactivationTime; }, |
989 | (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].deactivationTime, p, l, v); } ), | 1115 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); }, |
1116 | (s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.PhysBody.ptr, v); } ), | ||
990 | new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static", | 1117 | new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static", |
991 | 0.8f, | 1118 | 0.8f, |
992 | (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); }, | 1119 | (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); }, |
993 | (s) => { return s.m_params[0].linearSleepingThreshold; }, | 1120 | (s) => { return s.m_params[0].linearSleepingThreshold; }, |
994 | (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].linearSleepingThreshold, p, l, v); } ), | 1121 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); }, |
1122 | (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ), | ||
995 | new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static", | 1123 | new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static", |
996 | 1.0f, | 1124 | 1.0f, |
997 | (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); }, | 1125 | (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); }, |
998 | (s) => { return s.m_params[0].angularSleepingThreshold; }, | 1126 | (s) => { return s.m_params[0].angularSleepingThreshold; }, |
999 | (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].angularSleepingThreshold, p, l, v); } ), | 1127 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); }, |
1128 | (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ), | ||
1000 | new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" , | 1129 | new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" , |
1001 | 0f, // set to zero to disable | 1130 | 0f, // set to zero to disable |
1002 | (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); }, | 1131 | (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); }, |
1003 | (s) => { return s.m_params[0].ccdMotionThreshold; }, | 1132 | (s) => { return s.m_params[0].ccdMotionThreshold; }, |
1004 | (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].ccdMotionThreshold, p, l, v); } ), | 1133 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); }, |
1134 | (s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.PhysBody.ptr, v); } ), | ||
1005 | new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , | 1135 | new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , |
1006 | 0f, | 1136 | 0f, |
1007 | (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); }, | 1137 | (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); }, |
1008 | (s) => { return s.m_params[0].ccdSweptSphereRadius; }, | 1138 | (s) => { return s.m_params[0].ccdSweptSphereRadius; }, |
1009 | (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); } ), | 1139 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); }, |
1140 | (s,o,v) => { BulletSimAPI.SetCcdSweptSphereRadius2(o.PhysBody.ptr, v); } ), | ||
1010 | new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" , | 1141 | new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" , |
1011 | 0.1f, | 1142 | 0.1f, |
1012 | (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); }, | 1143 | (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); }, |
1013 | (s) => { return s.m_params[0].contactProcessingThreshold; }, | 1144 | (s) => { return s.m_params[0].contactProcessingThreshold; }, |
1014 | (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].contactProcessingThreshold, p, l, v); } ), | 1145 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); }, |
1015 | 1146 | (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ), | |
1147 | |||
1148 | new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)", | ||
1149 | (float)BSTerrainPhys.TerrainImplementation.Mesh, | ||
1150 | (s,cf,p,v) => { s.m_params[0].terrainImplementation = cf.GetFloat(p,v); }, | ||
1151 | (s) => { return s.m_params[0].terrainImplementation; }, | ||
1152 | (s,p,l,v) => { s.m_params[0].terrainImplementation = v; } ), | ||
1016 | new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , | 1153 | new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , |
1017 | 0.5f, | 1154 | 0.5f, |
1018 | (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, | 1155 | (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, |
1019 | (s) => { return s.m_params[0].terrainFriction; }, | 1156 | (s) => { return s.m_params[0].terrainFriction; }, |
1020 | (s,p,l,v) => { s.m_params[0].terrainFriction = v; s.TaintedUpdateParameter(p,l,v); } ), | 1157 | (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ), |
1021 | new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" , | 1158 | new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" , |
1022 | 0.8f, | 1159 | 0.8f, |
1023 | (s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); }, | 1160 | (s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); }, |
1024 | (s) => { return s.m_params[0].terrainHitFraction; }, | 1161 | (s) => { return s.m_params[0].terrainHitFraction; }, |
1025 | (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; s.TaintedUpdateParameter(p,l,v); } ), | 1162 | (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; /* TODO: set on real terrain */ } ), |
1026 | new ParameterDefn("TerrainRestitution", "Bouncyness" , | 1163 | new ParameterDefn("TerrainRestitution", "Bouncyness" , |
1027 | 0f, | 1164 | 0f, |
1028 | (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); }, | 1165 | (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); }, |
1029 | (s) => { return s.m_params[0].terrainRestitution; }, | 1166 | (s) => { return s.m_params[0].terrainRestitution; }, |
1030 | (s,p,l,v) => { s.m_params[0].terrainRestitution = v; s.TaintedUpdateParameter(p,l,v); } ), | 1167 | (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ), |
1031 | new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", | 1168 | new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", |
1032 | 0.5f, | 1169 | 0.2f, |
1033 | (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, | 1170 | (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, |
1034 | (s) => { return s.m_params[0].avatarFriction; }, | 1171 | (s) => { return s.m_params[0].avatarFriction; }, |
1035 | (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarFriction, p, l, v); } ), | 1172 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ), |
1173 | new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.", | ||
1174 | 10f, | ||
1175 | (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); }, | ||
1176 | (s) => { return s.m_params[0].avatarStandingFriction; }, | ||
1177 | (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ), | ||
1036 | new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.", | 1178 | new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.", |
1037 | 60f, | 1179 | 60f, |
1038 | (s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); }, | 1180 | (s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); }, |
1039 | (s) => { return s.m_params[0].avatarDensity; }, | 1181 | (s) => { return s.m_params[0].avatarDensity; }, |
1040 | (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarDensity, p, l, v); } ), | 1182 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarDensity, p, l, v); } ), |
1041 | new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.", | 1183 | new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.", |
1042 | 0f, | 1184 | 0f, |
1043 | (s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); }, | 1185 | (s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); }, |
1044 | (s) => { return s.m_params[0].avatarRestitution; }, | 1186 | (s) => { return s.m_params[0].avatarRestitution; }, |
1045 | (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarRestitution, p, l, v); } ), | 1187 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarRestitution, p, l, v); } ), |
1046 | new ParameterDefn("AvatarCapsuleRadius", "Radius of space around an avatar", | 1188 | new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule", |
1047 | 0.37f, | 1189 | 0.6f, |
1048 | (s,cf,p,v) => { s.m_params[0].avatarCapsuleRadius = cf.GetFloat(p, v); }, | 1190 | (s,cf,p,v) => { s.m_params[0].avatarCapsuleWidth = cf.GetFloat(p, v); }, |
1049 | (s) => { return s.m_params[0].avatarCapsuleRadius; }, | 1191 | (s) => { return s.m_params[0].avatarCapsuleWidth; }, |
1050 | (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarCapsuleRadius, p, l, v); } ), | 1192 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleWidth, p, l, v); } ), |
1193 | new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule", | ||
1194 | 0.45f, | ||
1195 | (s,cf,p,v) => { s.m_params[0].avatarCapsuleDepth = cf.GetFloat(p, v); }, | ||
1196 | (s) => { return s.m_params[0].avatarCapsuleDepth; }, | ||
1197 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleDepth, p, l, v); } ), | ||
1051 | new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar", | 1198 | new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar", |
1052 | 1.5f, | 1199 | 1.5f, |
1053 | (s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); }, | 1200 | (s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); }, |
1054 | (s) => { return s.m_params[0].avatarCapsuleHeight; }, | 1201 | (s) => { return s.m_params[0].avatarCapsuleHeight; }, |
1055 | (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarCapsuleHeight, p, l, v); } ), | 1202 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleHeight, p, l, v); } ), |
1056 | new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions", | 1203 | new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions", |
1057 | 0.1f, | 1204 | 0.1f, |
1058 | (s,cf,p,v) => { s.m_params[0].avatarContactProcessingThreshold = cf.GetFloat(p, v); }, | 1205 | (s,cf,p,v) => { s.m_params[0].avatarContactProcessingThreshold = cf.GetFloat(p, v); }, |
1059 | (s) => { return s.m_params[0].avatarContactProcessingThreshold; }, | 1206 | (s) => { return s.m_params[0].avatarContactProcessingThreshold; }, |
1060 | (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ), | 1207 | (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ), |
1061 | 1208 | ||
1062 | 1209 | ||
1063 | new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", | 1210 | new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", |
1064 | 0f, // zero to disable | 1211 | 0f, |
1065 | (s,cf,p,v) => { s.m_params[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); }, | 1212 | (s,cf,p,v) => { s.m_params[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); }, |
1066 | (s) => { return s.m_params[0].maxPersistantManifoldPoolSize; }, | 1213 | (s) => { return s.m_params[0].maxPersistantManifoldPoolSize; }, |
1067 | (s,p,l,v) => { s.m_params[0].maxPersistantManifoldPoolSize = v; } ), | 1214 | (s,p,l,v) => { s.m_params[0].maxPersistantManifoldPoolSize = v; } ), |
1068 | new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)", | 1215 | new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)", |
1069 | 0f, // zero to disable | 1216 | 0f, |
1070 | (s,cf,p,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); }, | 1217 | (s,cf,p,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); }, |
1071 | (s) => { return s.m_params[0].maxCollisionAlgorithmPoolSize; }, | 1218 | (s) => { return s.m_params[0].maxCollisionAlgorithmPoolSize; }, |
1072 | (s,p,l,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = v; } ), | 1219 | (s,p,l,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = v; } ), |
@@ -1081,12 +1228,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
1081 | (s) => { return s.m_params[0].shouldForceUpdateAllAabbs; }, | 1228 | (s) => { return s.m_params[0].shouldForceUpdateAllAabbs; }, |
1082 | (s,p,l,v) => { s.m_params[0].shouldForceUpdateAllAabbs = v; } ), | 1229 | (s,p,l,v) => { s.m_params[0].shouldForceUpdateAllAabbs = v; } ), |
1083 | new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction", | 1230 | new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction", |
1084 | ConfigurationParameters.numericFalse, | 1231 | ConfigurationParameters.numericTrue, |
1085 | (s,cf,p,v) => { s.m_params[0].shouldRandomizeSolverOrder = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, | 1232 | (s,cf,p,v) => { s.m_params[0].shouldRandomizeSolverOrder = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, |
1086 | (s) => { return s.m_params[0].shouldRandomizeSolverOrder; }, | 1233 | (s) => { return s.m_params[0].shouldRandomizeSolverOrder; }, |
1087 | (s,p,l,v) => { s.m_params[0].shouldRandomizeSolverOrder = v; } ), | 1234 | (s,p,l,v) => { s.m_params[0].shouldRandomizeSolverOrder = v; } ), |
1088 | new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands", | 1235 | new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands", |
1089 | ConfigurationParameters.numericFalse, | 1236 | ConfigurationParameters.numericTrue, |
1090 | (s,cf,p,v) => { s.m_params[0].shouldSplitSimulationIslands = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, | 1237 | (s,cf,p,v) => { s.m_params[0].shouldSplitSimulationIslands = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, |
1091 | (s) => { return s.m_params[0].shouldSplitSimulationIslands; }, | 1238 | (s) => { return s.m_params[0].shouldSplitSimulationIslands; }, |
1092 | (s,p,l,v) => { s.m_params[0].shouldSplitSimulationIslands = v; } ), | 1239 | (s,p,l,v) => { s.m_params[0].shouldSplitSimulationIslands = v; } ), |
@@ -1101,6 +1248,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
1101 | (s) => { return s.m_params[0].numberOfSolverIterations; }, | 1248 | (s) => { return s.m_params[0].numberOfSolverIterations; }, |
1102 | (s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ), | 1249 | (s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ), |
1103 | 1250 | ||
1251 | new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)", | ||
1252 | (float)BSLinkset.LinksetImplementation.Compound, | ||
1253 | (s,cf,p,v) => { s.m_params[0].linksetImplementation = cf.GetFloat(p,v); }, | ||
1254 | (s) => { return s.m_params[0].linksetImplementation; }, | ||
1255 | (s,p,l,v) => { s.m_params[0].linksetImplementation = v; } ), | ||
1104 | new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.", | 1256 | new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.", |
1105 | ConfigurationParameters.numericFalse, | 1257 | ConfigurationParameters.numericFalse, |
1106 | (s,cf,p,v) => { s.m_params[0].linkConstraintUseFrameOffset = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, | 1258 | (s,cf,p,v) => { s.m_params[0].linkConstraintUseFrameOffset = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, |
@@ -1121,28 +1273,27 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
1121 | (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); }, | 1273 | (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); }, |
1122 | (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; }, | 1274 | (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; }, |
1123 | (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ), | 1275 | (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ), |
1124 | new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=none, 1=all. Default=0", | 1276 | new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1", |
1125 | 0.0f, | 1277 | 0.1f, |
1126 | (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); }, | 1278 | (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); }, |
1127 | (s) => { return s.m_params[0].linkConstraintCFM; }, | 1279 | (s) => { return s.m_params[0].linkConstraintCFM; }, |
1128 | (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ), | 1280 | (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ), |
1129 | new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2", | 1281 | new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2", |
1130 | 0.2f, | 1282 | 0.1f, |
1131 | (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); }, | 1283 | (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); }, |
1132 | (s) => { return s.m_params[0].linkConstraintERP; }, | 1284 | (s) => { return s.m_params[0].linkConstraintERP; }, |
1133 | (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ), | 1285 | (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ), |
1286 | new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)", | ||
1287 | 40, | ||
1288 | (s,cf,p,v) => { s.m_params[0].linkConstraintSolverIterations = cf.GetFloat(p, v); }, | ||
1289 | (s) => { return s.m_params[0].linkConstraintSolverIterations; }, | ||
1290 | (s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ), | ||
1134 | 1291 | ||
1135 | new ParameterDefn("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)", | 1292 | new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)", |
1136 | 0f, | 1293 | 0f, |
1137 | (s,cf,p,v) => { s.m_detailedStatsStep = cf.GetInt(p, (int)v); }, | 1294 | (s,cf,p,v) => { s.m_params[0].physicsLoggingFrames = cf.GetInt(p, (int)v); }, |
1138 | (s) => { return (float)s.m_detailedStatsStep; }, | 1295 | (s) => { return (float)s.m_params[0].physicsLoggingFrames; }, |
1139 | (s,p,l,v) => { s.m_detailedStatsStep = (int)v; } ), | 1296 | (s,p,l,v) => { s.m_params[0].physicsLoggingFrames = (int)v; } ), |
1140 | new ParameterDefn("ShouldDebugLog", "Enables detailed DEBUG log statements", | ||
1141 | ConfigurationParameters.numericFalse, | ||
1142 | (s,cf,p,v) => { s.ShouldDebugLog = cf.GetBoolean(p, s.BoolNumeric(v)); }, | ||
1143 | (s) => { return s.NumericBool(s.ShouldDebugLog); }, | ||
1144 | (s,p,l,v) => { s.ShouldDebugLog = s.BoolNumeric(v); } ), | ||
1145 | |||
1146 | }; | 1297 | }; |
1147 | 1298 | ||
1148 | // Convert a boolean to our numeric true and false values | 1299 | // Convert a boolean to our numeric true and false values |
@@ -1200,11 +1351,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
1200 | 1351 | ||
1201 | private PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1]; | 1352 | private PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1]; |
1202 | 1353 | ||
1354 | // This creates an array in the correct format for returning the list of | ||
1355 | // parameters. This is used by the 'list' option of the 'physics' command. | ||
1203 | private void BuildParameterTable() | 1356 | private void BuildParameterTable() |
1204 | { | 1357 | { |
1205 | if (SettableParameters.Length < ParameterDefinitions.Length) | 1358 | if (SettableParameters.Length < ParameterDefinitions.Length) |
1206 | { | 1359 | { |
1207 | |||
1208 | List<PhysParameterEntry> entries = new List<PhysParameterEntry>(); | 1360 | List<PhysParameterEntry> entries = new List<PhysParameterEntry>(); |
1209 | for (int ii = 0; ii < ParameterDefinitions.Length; ii++) | 1361 | for (int ii = 0; ii < ParameterDefinitions.Length; ii++) |
1210 | { | 1362 | { |
@@ -1249,60 +1401,54 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
1249 | return ret; | 1401 | return ret; |
1250 | } | 1402 | } |
1251 | 1403 | ||
1252 | // check to see if we are updating a parameter for a particular or all of the prims | ||
1253 | protected void UpdateParameterPrims(ref float loc, string parm, uint localID, float val) | ||
1254 | { | ||
1255 | List<uint> operateOn; | ||
1256 | lock (m_prims) operateOn = new List<uint>(m_prims.Keys); | ||
1257 | UpdateParameterSet(operateOn, ref loc, parm, localID, val); | ||
1258 | } | ||
1259 | |||
1260 | // check to see if we are updating a parameter for a particular or all of the avatars | ||
1261 | protected void UpdateParameterAvatars(ref float loc, string parm, uint localID, float val) | ||
1262 | { | ||
1263 | List<uint> operateOn; | ||
1264 | lock (m_avatars) operateOn = new List<uint>(m_avatars.Keys); | ||
1265 | UpdateParameterSet(operateOn, ref loc, parm, localID, val); | ||
1266 | } | ||
1267 | |||
1268 | // update all the localIDs specified | 1404 | // update all the localIDs specified |
1269 | // If the local ID is APPLY_TO_NONE, just change the default value | 1405 | // If the local ID is APPLY_TO_NONE, just change the default value |
1270 | // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs | 1406 | // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs |
1271 | // If the localID is a specific object, apply the parameter change to only that object | 1407 | // If the localID is a specific object, apply the parameter change to only that object |
1272 | protected void UpdateParameterSet(List<uint> lIDs, ref float defaultLoc, string parm, uint localID, float val) | 1408 | private void UpdateParameterObject(ref float defaultLoc, string parm, uint localID, float val) |
1273 | { | 1409 | { |
1410 | List<uint> objectIDs = new List<uint>(); | ||
1274 | switch (localID) | 1411 | switch (localID) |
1275 | { | 1412 | { |
1276 | case PhysParameterEntry.APPLY_TO_NONE: | 1413 | case PhysParameterEntry.APPLY_TO_NONE: |
1277 | defaultLoc = val; // setting only the default value | 1414 | defaultLoc = val; // setting only the default value |
1415 | // This will cause a call into the physical world if some operation is specified (SetOnObject). | ||
1416 | objectIDs.Add(TERRAIN_ID); | ||
1417 | TaintedUpdateParameter(parm, objectIDs, val); | ||
1278 | break; | 1418 | break; |
1279 | case PhysParameterEntry.APPLY_TO_ALL: | 1419 | case PhysParameterEntry.APPLY_TO_ALL: |
1280 | defaultLoc = val; // setting ALL also sets the default value | 1420 | defaultLoc = val; // setting ALL also sets the default value |
1281 | List<uint> objectIDs = lIDs; | 1421 | lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys); |
1282 | string xparm = parm.ToLower(); | 1422 | TaintedUpdateParameter(parm, objectIDs, val); |
1283 | float xval = val; | ||
1284 | TaintedObject("BSScene.UpdateParameterSet", delegate() { | ||
1285 | foreach (uint lID in objectIDs) | ||
1286 | { | ||
1287 | BulletSimAPI.UpdateParameter(m_worldID, lID, xparm, xval); | ||
1288 | } | ||
1289 | }); | ||
1290 | break; | 1423 | break; |
1291 | default: | 1424 | default: |
1292 | // setting only one localID | 1425 | // setting only one localID |
1293 | TaintedUpdateParameter(parm, localID, val); | 1426 | objectIDs.Add(localID); |
1427 | TaintedUpdateParameter(parm, objectIDs, val); | ||
1294 | break; | 1428 | break; |
1295 | } | 1429 | } |
1296 | } | 1430 | } |
1297 | 1431 | ||
1298 | // schedule the actual updating of the paramter to when the phys engine is not busy | 1432 | // schedule the actual updating of the paramter to when the phys engine is not busy |
1299 | protected void TaintedUpdateParameter(string parm, uint localID, float val) | 1433 | private void TaintedUpdateParameter(string parm, List<uint> lIDs, float val) |
1300 | { | 1434 | { |
1301 | uint xlocalID = localID; | ||
1302 | string xparm = parm.ToLower(); | ||
1303 | float xval = val; | 1435 | float xval = val; |
1304 | TaintedObject("BSScene.TaintedUpdateParameter", delegate() { | 1436 | List<uint> xlIDs = lIDs; |
1305 | BulletSimAPI.UpdateParameter(m_worldID, xlocalID, xparm, xval); | 1437 | string xparm = parm; |
1438 | TaintedObject("BSScene.UpdateParameterSet", delegate() { | ||
1439 | ParameterDefn thisParam; | ||
1440 | if (TryGetParameter(xparm, out thisParam)) | ||
1441 | { | ||
1442 | if (thisParam.onObject != null) | ||
1443 | { | ||
1444 | foreach (uint lID in xlIDs) | ||
1445 | { | ||
1446 | BSPhysObject theObject = null; | ||
1447 | PhysObjects.TryGetValue(lID, out theObject); | ||
1448 | thisParam.onObject(this, theObject, xval); | ||
1449 | } | ||
1450 | } | ||
1451 | } | ||
1306 | }); | 1452 | }); |
1307 | } | 1453 | } |
1308 | 1454 | ||
@@ -1326,12 +1472,24 @@ public class BSScene : PhysicsScene, IPhysicsParameters | |||
1326 | 1472 | ||
1327 | #endregion Runtime settable parameters | 1473 | #endregion Runtime settable parameters |
1328 | 1474 | ||
1475 | // Debugging routine for dumping detailed physical information for vehicle prims | ||
1476 | private void DumpVehicles() | ||
1477 | { | ||
1478 | foreach (BSPrim prim in m_vehicles) | ||
1479 | { | ||
1480 | BulletSimAPI.DumpRigidBody2(World.ptr, prim.PhysBody.ptr); | ||
1481 | BulletSimAPI.DumpCollisionShape2(World.ptr, prim.PhysShape.ptr); | ||
1482 | } | ||
1483 | } | ||
1484 | |||
1329 | // Invoke the detailed logger and output something if it's enabled. | 1485 | // Invoke the detailed logger and output something if it's enabled. |
1330 | public void DetailLog(string msg, params Object[] args) | 1486 | public void DetailLog(string msg, params Object[] args) |
1331 | { | 1487 | { |
1332 | PhysicsLogging.Write(msg, args); | 1488 | PhysicsLogging.Write(msg, args); |
1489 | // Add the Flush() if debugging crashes. Gets all the messages written out. | ||
1490 | // PhysicsLogging.Flush(); | ||
1333 | } | 1491 | } |
1334 | // used to fill in the LocalID when there isn't one | 1492 | // Used to fill in the LocalID when there isn't one. It's the correct number of characters. |
1335 | public const string DetailLogZero = "0000000000"; | 1493 | public const string DetailLogZero = "0000000000"; |
1336 | 1494 | ||
1337 | } | 1495 | } |