aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSScene.cs')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSScene.cs854
1 files changed, 854 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
new file mode 100644
index 0000000..518be09
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -0,0 +1,854 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Runtime.InteropServices;
30using System.Text;
31using System.Threading;
32using Nini.Config;
33using log4net;
34using OpenSim.Framework;
35using OpenSim.Region.Physics.Manager;
36using OpenMetaverse;
37using OpenSim.Region.Framework;
38
39// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
40// Parameterize BulletSim. Pass a structure of parameters to the C++ code. Capsule size, friction, ...
41// Adjust character capsule size when height is adjusted (ScenePresence.SetHeight)
42// Test sculpties
43// Compute physics FPS reasonably
44// Based on material, set density and friction
45// More efficient memory usage in passing hull information from BSPrim to BulletSim
46// Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly?
47// In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground)
48// At the moment, physical and phantom causes object to drop through the terrain
49// Should prim.link() and prim.delink() membership checking happen at taint time?
50// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once
51// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
52// Implement the genCollisions feature in BulletSim::SetObjectProperties (don't pass up unneeded collisions)
53// Implement LockAngularMotion
54// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
55// Built Galton board (lots of MoveTo's) and some slats were not positioned correctly (mistakes scattered)
56// No mistakes with ODE. Shape creation race condition?
57// Does NeedsMeshing() really need to exclude all the different shapes?
58//
59namespace OpenSim.Region.Physics.BulletSPlugin
60{
61public class BSScene : PhysicsScene, IPhysicsParameters
62{
63 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
64 private static readonly string LogHeader = "[BULLETS SCENE]";
65
66 public string BulletSimVersion = "?";
67
68 private Dictionary<uint, BSCharacter> m_avatars = new Dictionary<uint, BSCharacter>();
69 private Dictionary<uint, BSPrim> m_prims = new Dictionary<uint, BSPrim>();
70 private List<BSPrim> m_vehicles = new List<BSPrim>();
71 private float[] m_heightMap;
72 private float m_waterLevel;
73 private uint m_worldID;
74 public uint WorldID { get { return m_worldID; } }
75
76 private bool m_initialized = false;
77
78 public IMesher mesher;
79 private int m_meshLOD;
80 public int MeshLOD
81 {
82 get { return m_meshLOD; }
83 }
84
85 private int m_maxSubSteps;
86 private float m_fixedTimeStep;
87 private long m_simulationStep = 0;
88 public long SimulationStep { get { return m_simulationStep; } }
89
90 // A value of the time now so all the collision and update routines do not have to get their own
91 // Set to 'now' just before all the prims and actors are called for collisions and updates
92 private int m_simulationNowTime;
93 public int SimulationNowTime { get { return m_simulationNowTime; } }
94
95 private int m_maxCollisionsPerFrame;
96 private CollisionDesc[] m_collisionArray;
97 private GCHandle m_collisionArrayPinnedHandle;
98
99 private int m_maxUpdatesPerFrame;
100 private EntityProperties[] m_updateArray;
101 private GCHandle m_updateArrayPinnedHandle;
102
103 private bool _meshSculptedPrim = true; // cause scuplted prims to get meshed
104 private bool _forceSimplePrimMeshing = false; // if a cube or sphere, let Bullet do internal shapes
105
106 public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero
107 public const uint GROUNDPLANE_ID = 1;
108
109 public ConfigurationParameters Params
110 {
111 get { return m_params[0]; }
112 }
113 public Vector3 DefaultGravity
114 {
115 get { return new Vector3(0f, 0f, Params.gravity); }
116 }
117
118 private float m_maximumObjectMass;
119 public float MaximumObjectMass
120 {
121 get { return m_maximumObjectMass; }
122 }
123
124 public delegate void TaintCallback();
125 private List<TaintCallback> _taintedObjects;
126 private Object _taintLock = new Object();
127
128 // A pointer to an instance if this structure is passed to the C++ code
129 ConfigurationParameters[] m_params;
130 GCHandle m_paramsHandle;
131
132 private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle;
133
134 public BSScene(string identifier)
135 {
136 m_initialized = false;
137 }
138
139 public override void Initialise(IMesher meshmerizer, IConfigSource config)
140 {
141 // Allocate pinned memory to pass parameters.
142 m_params = new ConfigurationParameters[1];
143 m_paramsHandle = GCHandle.Alloc(m_params, GCHandleType.Pinned);
144
145 // Set default values for physics parameters plus any overrides from the ini file
146 GetInitialParameterValues(config);
147
148 // allocate more pinned memory close to the above in an attempt to get the memory all together
149 m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame];
150 m_collisionArrayPinnedHandle = GCHandle.Alloc(m_collisionArray, GCHandleType.Pinned);
151 m_updateArray = new EntityProperties[m_maxUpdatesPerFrame];
152 m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned);
153
154 // Get the version of the DLL
155 // TODO: this doesn't work yet. Something wrong with marshaling the returned string.
156 // BulletSimVersion = BulletSimAPI.GetVersion();
157 // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion);
158
159 // if Debug, enable logging from the unmanaged code
160 if (m_log.IsDebugEnabled)
161 {
162 m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader);
163 m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger);
164 BulletSimAPI.SetDebugLogCallback(m_DebugLogCallbackHandle);
165 }
166
167 _taintedObjects = new List<TaintCallback>();
168
169 mesher = meshmerizer;
170 // The bounding box for the simulated world
171 Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, 4096f);
172
173 // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
174 m_worldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(),
175 m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(),
176 m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject());
177
178 m_initialized = true;
179 }
180
181 // All default parameter values are set here. There should be no values set in the
182 // variable definitions.
183 private void GetInitialParameterValues(IConfigSource config)
184 {
185 ConfigurationParameters parms = new ConfigurationParameters();
186
187 _meshSculptedPrim = true; // mesh sculpted prims
188 _forceSimplePrimMeshing = false; // use complex meshing if called for
189
190 m_meshLOD = 32;
191
192 m_maxSubSteps = 10;
193 m_fixedTimeStep = 1f / 60f;
194 m_maxCollisionsPerFrame = 2048;
195 m_maxUpdatesPerFrame = 2048;
196 m_maximumObjectMass = 10000.01f;
197
198 parms.defaultFriction = 0.5f;
199 parms.defaultDensity = 10.000006836f; // Aluminum g/cm3
200 parms.defaultRestitution = 0f;
201 parms.collisionMargin = 0.0f;
202 parms.gravity = -9.80665f;
203
204 parms.linearDamping = 0.0f;
205 parms.angularDamping = 0.0f;
206 parms.deactivationTime = 0.2f;
207 parms.linearSleepingThreshold = 0.8f;
208 parms.angularSleepingThreshold = 1.0f;
209 parms.ccdMotionThreshold = 0.5f; // set to zero to disable
210 parms.ccdSweptSphereRadius = 0.2f;
211
212 parms.terrainFriction = 0.5f;
213 parms.terrainHitFraction = 0.8f;
214 parms.terrainRestitution = 0f;
215 parms.avatarFriction = 0.0f;
216 parms.avatarDensity = 60f;
217 parms.avatarCapsuleRadius = 0.37f;
218 parms.avatarCapsuleHeight = 1.5f; // 2.140599f
219
220 if (config != null)
221 {
222 // If there are specifications in the ini file, use those values
223 // WHEN ADDING OR UPDATING THIS SECTION, BE SURE TO UPDATE OpenSimDefaults.ini
224 // ALSO REMEMBER TO UPDATE THE RUNTIME SETTING OF THE PARAMETERS.
225 IConfig pConfig = config.Configs["BulletSim"];
226 if (pConfig != null)
227 {
228 _meshSculptedPrim = pConfig.GetBoolean("MeshSculptedPrim", _meshSculptedPrim);
229 _forceSimplePrimMeshing = pConfig.GetBoolean("ForceSimplePrimMeshing", _forceSimplePrimMeshing);
230
231 m_meshLOD = pConfig.GetInt("MeshLevelOfDetail", m_meshLOD);
232
233 m_maxSubSteps = pConfig.GetInt("MaxSubSteps", m_maxSubSteps);
234 m_fixedTimeStep = pConfig.GetFloat("FixedTimeStep", m_fixedTimeStep);
235 m_maxCollisionsPerFrame = pConfig.GetInt("MaxCollisionsPerFrame", m_maxCollisionsPerFrame);
236 m_maxUpdatesPerFrame = pConfig.GetInt("MaxUpdatesPerFrame", m_maxUpdatesPerFrame);
237 m_maximumObjectMass = pConfig.GetFloat("MaxObjectMass", m_maximumObjectMass);
238
239 parms.defaultFriction = pConfig.GetFloat("DefaultFriction", parms.defaultFriction);
240 parms.defaultDensity = pConfig.GetFloat("DefaultDensity", parms.defaultDensity);
241 parms.defaultRestitution = pConfig.GetFloat("DefaultRestitution", parms.defaultRestitution);
242 parms.collisionMargin = pConfig.GetFloat("CollisionMargin", parms.collisionMargin);
243 parms.gravity = pConfig.GetFloat("Gravity", parms.gravity);
244
245 parms.linearDamping = pConfig.GetFloat("LinearDamping", parms.linearDamping);
246 parms.angularDamping = pConfig.GetFloat("AngularDamping", parms.angularDamping);
247 parms.deactivationTime = pConfig.GetFloat("DeactivationTime", parms.deactivationTime);
248 parms.linearSleepingThreshold = pConfig.GetFloat("LinearSleepingThreshold", parms.linearSleepingThreshold);
249 parms.angularSleepingThreshold = pConfig.GetFloat("AngularSleepingThreshold", parms.angularSleepingThreshold);
250 parms.ccdMotionThreshold = pConfig.GetFloat("CcdMotionThreshold", parms.ccdMotionThreshold);
251 parms.ccdSweptSphereRadius = pConfig.GetFloat("CcdSweptSphereRadius", parms.ccdSweptSphereRadius);
252
253 parms.terrainFriction = pConfig.GetFloat("TerrainFriction", parms.terrainFriction);
254 parms.terrainHitFraction = pConfig.GetFloat("TerrainHitFraction", parms.terrainHitFraction);
255 parms.terrainRestitution = pConfig.GetFloat("TerrainRestitution", parms.terrainRestitution);
256 parms.avatarFriction = pConfig.GetFloat("AvatarFriction", parms.avatarFriction);
257 parms.avatarDensity = pConfig.GetFloat("AvatarDensity", parms.avatarDensity);
258 parms.avatarCapsuleRadius = pConfig.GetFloat("AvatarCapsuleRadius", parms.avatarCapsuleRadius);
259 parms.avatarCapsuleHeight = pConfig.GetFloat("AvatarCapsuleHeight", parms.avatarCapsuleHeight);
260 }
261 }
262 m_params[0] = parms;
263 }
264
265 // Called directly from unmanaged code so don't do much
266 private void BulletLogger(string msg)
267 {
268 m_log.Debug("[BULLETS UNMANAGED]:" + msg);
269 }
270
271 public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
272 {
273 m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
274 return null;
275 }
276
277 public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying)
278 {
279 // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName);
280 BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying);
281 lock (m_avatars) m_avatars.Add(localID, actor);
282 return actor;
283 }
284
285 public override void RemoveAvatar(PhysicsActor actor)
286 {
287 // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader);
288 if (actor is BSCharacter)
289 {
290 ((BSCharacter)actor).Destroy();
291 }
292 try
293 {
294 lock (m_avatars) m_avatars.Remove(actor.LocalID);
295 }
296 catch (Exception e)
297 {
298 m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e);
299 }
300 }
301
302 public override void RemovePrim(PhysicsActor prim)
303 {
304 // m_log.DebugFormat("{0}: RemovePrim", LogHeader);
305 if (prim is BSPrim)
306 {
307 ((BSPrim)prim).Destroy();
308 }
309 try
310 {
311 lock (m_prims) m_prims.Remove(prim.LocalID);
312 }
313 catch (Exception e)
314 {
315 m_log.WarnFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e);
316 }
317 }
318
319 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
320 Vector3 size, Quaternion rotation, bool isPhysical, uint localID)
321 {
322 // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName);
323 BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical);
324 lock (m_prims) m_prims.Add(localID, prim);
325 return prim;
326 }
327
328 // This is a call from the simulator saying that some physical property has been updated.
329 // The BulletSim driver senses the changing of relevant properties so this taint
330 // information call is not needed.
331 public override void AddPhysicsActorTaint(PhysicsActor prim) { }
332
333 // Simulate one timestep
334 public override float Simulate(float timeStep)
335 {
336 int updatedEntityCount;
337 IntPtr updatedEntitiesPtr;
338 int collidersCount;
339 IntPtr collidersPtr;
340
341 // prevent simulation until we've been initialized
342 if (!m_initialized) return 10.0f;
343
344 // update the prim states while we know the physics engine is not busy
345 ProcessTaints();
346
347 // Some of the prims operate with special vehicle properties
348 ProcessVehicles(timeStep);
349 ProcessTaints(); // the vehicles might have added taints
350
351 // step the physical world one interval
352 m_simulationStep++;
353 int numSubSteps = BulletSimAPI.PhysicsStep(m_worldID, timeStep, m_maxSubSteps, m_fixedTimeStep,
354 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
355
356 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in
357
358 // Get a value for 'now' so all the collision and update routines don't have to get their own
359 m_simulationNowTime = Util.EnvironmentTickCount();
360
361 // If there were collisions, process them by sending the event to the prim.
362 // Collisions must be processed before updates.
363 if (collidersCount > 0)
364 {
365 for (int ii = 0; ii < collidersCount; ii++)
366 {
367 uint cA = m_collisionArray[ii].aID;
368 uint cB = m_collisionArray[ii].bID;
369 Vector3 point = m_collisionArray[ii].point;
370 Vector3 normal = m_collisionArray[ii].normal;
371 SendCollision(cA, cB, point, normal, 0.01f);
372 SendCollision(cB, cA, point, -normal, 0.01f);
373 }
374 }
375
376 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
377 if (updatedEntityCount > 0)
378 {
379 for (int ii = 0; ii < updatedEntityCount; ii++)
380 {
381 EntityProperties entprop = m_updateArray[ii];
382 // m_log.DebugFormat("{0}: entprop[{1}]: id={2}, pos={3}", LogHeader, ii, entprop.ID, entprop.Position);
383 BSCharacter actor;
384 if (m_avatars.TryGetValue(entprop.ID, out actor))
385 {
386 actor.UpdateProperties(entprop);
387 continue;
388 }
389 BSPrim prim;
390 if (m_prims.TryGetValue(entprop.ID, out prim))
391 {
392 prim.UpdateProperties(entprop);
393 }
394 }
395 }
396
397 // TODO: FIX THIS: fps calculation wrong. This calculation always returns about 1 in normal operation.
398 return timeStep / (numSubSteps * m_fixedTimeStep) * 1000f;
399 }
400
401 // Something has collided
402 private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penitration)
403 {
404 if (localID == TERRAIN_ID || localID == GROUNDPLANE_ID)
405 {
406 return; // don't send collisions to the terrain
407 }
408
409 ActorTypes type = ActorTypes.Prim;
410 if (collidingWith == TERRAIN_ID || collidingWith == GROUNDPLANE_ID)
411 type = ActorTypes.Ground;
412 else if (m_avatars.ContainsKey(collidingWith))
413 type = ActorTypes.Agent;
414
415 BSPrim prim;
416 if (m_prims.TryGetValue(localID, out prim)) {
417 prim.Collide(collidingWith, type, collidePoint, collideNormal, penitration);
418 return;
419 }
420 BSCharacter actor;
421 if (m_avatars.TryGetValue(localID, out actor)) {
422 actor.Collide(collidingWith, type, collidePoint, collideNormal, penitration);
423 return;
424 }
425 return;
426 }
427
428 public override void GetResults() { }
429
430 public override void SetTerrain(float[] heightMap) {
431 m_heightMap = heightMap;
432 this.TaintedObject(delegate()
433 {
434 BulletSimAPI.SetHeightmap(m_worldID, m_heightMap);
435 });
436 }
437
438 public float GetTerrainHeightAtXY(float tX, float tY)
439 {
440 return m_heightMap[((int)tX) * Constants.RegionSize + ((int)tY)];
441 }
442
443 public override void SetWaterLevel(float baseheight)
444 {
445 m_waterLevel = baseheight;
446 }
447 public float GetWaterLevel()
448 {
449 return m_waterLevel;
450 }
451
452 public override void DeleteTerrain()
453 {
454 m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
455 }
456
457 public override void Dispose()
458 {
459 m_log.DebugFormat("{0}: Dispose()", LogHeader);
460 }
461
462 public override Dictionary<uint, float> GetTopColliders()
463 {
464 return new Dictionary<uint, float>();
465 }
466
467 public override bool IsThreaded { get { return false; } }
468
469 /// <summary>
470 /// Routine to figure out if we need to mesh this prim with our mesher
471 /// </summary>
472 /// <param name="pbs"></param>
473 /// <returns>true if the prim needs meshing</returns>
474 public bool NeedsMeshing(PrimitiveBaseShape pbs)
475 {
476 // most of this is redundant now as the mesher will return null if it cant mesh a prim
477 // but we still need to check for sculptie meshing being enabled so this is the most
478 // convenient place to do it for now...
479
480 // int iPropertiesNotSupportedDefault = 0;
481
482 if (pbs.SculptEntry && !_meshSculptedPrim)
483 {
484 // Render sculpties as boxes
485 return false;
486 }
487
488 // if it's a standard box or sphere with no cuts, hollows, twist or top shear, return false since Bullet
489 // can use an internal representation for the prim
490 if (!_forceSimplePrimMeshing)
491 {
492 // m_log.DebugFormat("{0}: NeedsMeshing: simple mesh: profshape={1}, curve={2}", LogHeader, pbs.ProfileShape, pbs.PathCurve);
493 if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
494 || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1
495 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z))
496 {
497
498 if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
499 && pbs.ProfileHollow == 0
500 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
501 && pbs.PathBegin == 0 && pbs.PathEnd == 0
502 && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
503 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
504 && pbs.PathShearX == 0 && pbs.PathShearY == 0)
505 {
506 return false;
507 }
508 }
509 }
510
511 /* TODO: verify that the mesher will now do all these shapes
512 if (pbs.ProfileHollow != 0)
513 iPropertiesNotSupportedDefault++;
514
515 if ((pbs.PathBegin != 0) || pbs.PathEnd != 0)
516 iPropertiesNotSupportedDefault++;
517
518 if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0))
519 iPropertiesNotSupportedDefault++;
520
521 if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0)
522 iPropertiesNotSupportedDefault++;
523
524 if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100))
525 iPropertiesNotSupportedDefault++;
526
527 if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0))
528 iPropertiesNotSupportedDefault++;
529
530 if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight)
531 iPropertiesNotSupportedDefault++;
532
533 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))
534 iPropertiesNotSupportedDefault++;
535
536 if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1)
537 iPropertiesNotSupportedDefault++;
538
539 // test for torus
540 if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square)
541 {
542 if (pbs.PathCurve == (byte)Extrusion.Curve1)
543 {
544 iPropertiesNotSupportedDefault++;
545 }
546 }
547 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
548 {
549 if (pbs.PathCurve == (byte)Extrusion.Straight)
550 {
551 iPropertiesNotSupportedDefault++;
552 }
553 // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits
554 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
555 {
556 iPropertiesNotSupportedDefault++;
557 }
558 }
559 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
560 {
561 if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2)
562 {
563 iPropertiesNotSupportedDefault++;
564 }
565 }
566 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
567 {
568 if (pbs.PathCurve == (byte)Extrusion.Straight)
569 {
570 iPropertiesNotSupportedDefault++;
571 }
572 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
573 {
574 iPropertiesNotSupportedDefault++;
575 }
576 }
577 if (iPropertiesNotSupportedDefault == 0)
578 {
579 return false;
580 }
581 */
582 return true;
583 }
584
585 // The calls to the PhysicsActors can't directly call into the physics engine
586 // because it might be busy. We we delay changes to a known time.
587 // We rely on C#'s closure to save and restore the context for the delegate.
588 public void TaintedObject(TaintCallback callback)
589 {
590 lock (_taintLock)
591 _taintedObjects.Add(callback);
592 return;
593 }
594
595 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues
596 // a callback into itself to do the actual property change. That callback is called
597 // here just before the physics engine is called to step the simulation.
598 public void ProcessTaints()
599 {
600 if (_taintedObjects.Count > 0) // save allocating new list if there is nothing to process
601 {
602 // swizzle a new list into the list location so we can process what's there
603 List<TaintCallback> oldList;
604 lock (_taintLock)
605 {
606 oldList = _taintedObjects;
607 _taintedObjects = new List<TaintCallback>();
608 }
609
610 foreach (TaintCallback callback in oldList)
611 {
612 try
613 {
614 callback();
615 }
616 catch (Exception e)
617 {
618 m_log.ErrorFormat("{0}: ProcessTaints: Exception: {1}", LogHeader, e);
619 }
620 }
621 oldList.Clear();
622 }
623 }
624
625 #region Vehicles
626 // Make so the scene will call this prim for vehicle actions each tick.
627 // Safe to call if prim is already in the vehicle list.
628 public void AddVehiclePrim(BSPrim vehicle)
629 {
630 lock (m_vehicles)
631 {
632 if (!m_vehicles.Contains(vehicle))
633 {
634 m_vehicles.Add(vehicle);
635 }
636 }
637 }
638
639 // Remove a prim from our list of vehicles.
640 // Safe to call if the prim is not in the vehicle list.
641 public void RemoveVehiclePrim(BSPrim vehicle)
642 {
643 lock (m_vehicles)
644 {
645 if (m_vehicles.Contains(vehicle))
646 {
647 m_vehicles.Remove(vehicle);
648 }
649 }
650 }
651
652 // Some prims have extra vehicle actions
653 // no locking because only called when physics engine is not busy
654 private void ProcessVehicles(float timeStep)
655 {
656 foreach (BSPrim prim in m_vehicles)
657 {
658 prim.StepVehicle(timeStep);
659 }
660 }
661 #endregion Vehicles
662
663 #region Runtime settable parameters
664 public static PhysParameterEntry[] SettableParameters = new PhysParameterEntry[]
665 {
666 new PhysParameterEntry("MeshLOD", "Level of detail to render meshes (Power of two. Default 32)"),
667 new PhysParameterEntry("MaxSubStep", "In simulation step, maximum number of substeps"),
668 new PhysParameterEntry("FixedTimeStep", "In simulation step, seconds of one substep (1/60)"),
669 new PhysParameterEntry("MaxObjectMass", "Maximum object mass (10000.01)"),
670
671 new PhysParameterEntry("DefaultFriction", "Friction factor used on new objects"),
672 new PhysParameterEntry("DefaultDensity", "Density for new objects" ),
673 new PhysParameterEntry("DefaultRestitution", "Bouncyness of an object" ),
674 // new PhysParameterEntry("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!!)" ),
675 new PhysParameterEntry("Gravity", "Vertical force of gravity (negative means down)" ),
676
677 new PhysParameterEntry("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)" ),
678 new PhysParameterEntry("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)" ),
679 new PhysParameterEntry("DeactivationTime", "Seconds before considering an object potentially static" ),
680 new PhysParameterEntry("LinearSleepingThreshold", "Seconds to measure linear movement before considering static" ),
681 new PhysParameterEntry("AngularSleepingThreshold", "Seconds to measure angular movement before considering static" ),
682 // new PhysParameterEntry("CcdMotionThreshold", "" ),
683 // new PhysParameterEntry("CcdSweptSphereRadius", "" ),
684
685 new PhysParameterEntry("TerrainFriction", "Factor to reduce movement against terrain surface" ),
686 new PhysParameterEntry("TerrainHitFraction", "Distance to measure hit collisions" ),
687 new PhysParameterEntry("TerrainRestitution", "Bouncyness" ),
688 new PhysParameterEntry("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation." ),
689 new PhysParameterEntry("AvatarDensity", "Density of an avatar. Changed on avatar recreation." ),
690 new PhysParameterEntry("AvatarRestitution", "Bouncyness. Changed on avatar recreation." ),
691 new PhysParameterEntry("AvatarCapsuleRadius", "Radius of space around an avatar" ),
692 new PhysParameterEntry("AvatarCapsuleHeight", "Default height of space around avatar" )
693 };
694
695 #region IPhysicsParameters
696 // Get the list of parameters this physics engine supports
697 public PhysParameterEntry[] GetParameterList()
698 {
699 return SettableParameters;
700 }
701
702 // Set parameter on a specific or all instances.
703 // Return 'false' if not able to set the parameter.
704 // Setting the value in the m_params block will change the value the physics engine
705 // will use the next time since it's pinned and shared memory.
706 // Some of the values require calling into the physics engine to get the new
707 // value activated ('terrainFriction' for instance).
708 public bool SetPhysicsParameter(string parm, float val, uint localID)
709 {
710 bool ret = true;
711 string lparm = parm.ToLower();
712 switch (lparm)
713 {
714 case "meshlod": m_meshLOD = (int)val; break;
715 case "maxsubstep": m_maxSubSteps = (int)val; break;
716 case "fixedtimestep": m_fixedTimeStep = val; break;
717 case "maxobjectmass": m_maximumObjectMass = val; break;
718
719 case "defaultfriction": m_params[0].defaultFriction = val; break;
720 case "defaultdensity": m_params[0].defaultDensity = val; break;
721 case "defaultrestitution": m_params[0].defaultRestitution = val; break;
722 case "collisionmargin": m_params[0].collisionMargin = val; break;
723 case "gravity": m_params[0].gravity = val; TaintedUpdateParameter(lparm, PhysParameterEntry.APPLY_TO_NONE, val); break;
724
725 case "lineardamping": UpdateParameterPrims(ref m_params[0].linearDamping, lparm, localID, val); break;
726 case "angulardamping": UpdateParameterPrims(ref m_params[0].angularDamping, lparm, localID, val); break;
727 case "deactivationtime": UpdateParameterPrims(ref m_params[0].deactivationTime, lparm, localID, val); break;
728 case "linearsleepingthreshold": UpdateParameterPrims(ref m_params[0].linearSleepingThreshold, lparm, localID, val); break;
729 case "angularsleepingthreshold": UpdateParameterPrims(ref m_params[0].angularDamping, lparm, localID, val); break;
730 case "ccdmotionthreshold": UpdateParameterPrims(ref m_params[0].ccdMotionThreshold, lparm, localID, val); break;
731 case "ccdsweptsphereradius": UpdateParameterPrims(ref m_params[0].ccdSweptSphereRadius, lparm, localID, val); break;
732
733 // set a terrain physical feature and cause terrain to be recalculated
734 case "terrainfriction": m_params[0].terrainFriction = val; TaintedUpdateParameter("terrain", 0, val); break;
735 case "terrainhitfraction": m_params[0].terrainHitFraction = val; TaintedUpdateParameter("terrain", 0, val); break;
736 case "terrainrestitution": m_params[0].terrainRestitution = val; TaintedUpdateParameter("terrain", 0, val); break;
737 // set an avatar physical feature and cause avatar(s) to be recalculated
738 case "avatarfriction": UpdateParameterAvatars(ref m_params[0].avatarFriction, "avatar", localID, val); break;
739 case "avatardensity": UpdateParameterAvatars(ref m_params[0].avatarDensity, "avatar", localID, val); break;
740 case "avatarrestitution": UpdateParameterAvatars(ref m_params[0].avatarRestitution, "avatar", localID, val); break;
741 case "avatarcapsuleradius": UpdateParameterAvatars(ref m_params[0].avatarCapsuleRadius, "avatar", localID, val); break;
742 case "avatarcapsuleheight": UpdateParameterAvatars(ref m_params[0].avatarCapsuleHeight, "avatar", localID, val); break;
743
744 default: ret = false; break;
745 }
746 return ret;
747 }
748
749 // check to see if we are updating a parameter for a particular or all of the prims
750 private void UpdateParameterPrims(ref float loc, string parm, uint localID, float val)
751 {
752 List<uint> operateOn;
753 lock (m_prims) operateOn = new List<uint>(m_prims.Keys);
754 UpdateParameterSet(operateOn, ref loc, parm, localID, val);
755 }
756
757 // check to see if we are updating a parameter for a particular or all of the avatars
758 private void UpdateParameterAvatars(ref float loc, string parm, uint localID, float val)
759 {
760 List<uint> operateOn;
761 lock (m_avatars) operateOn = new List<uint>(m_avatars.Keys);
762 UpdateParameterSet(operateOn, ref loc, parm, localID, val);
763 }
764
765 // update all the localIDs specified
766 // If the local ID is APPLY_TO_NONE, just change the default value
767 // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
768 // If the localID is a specific object, apply the parameter change to only that object
769 private void UpdateParameterSet(List<uint> lIDs, ref float defaultLoc, string parm, uint localID, float val)
770 {
771 switch (localID)
772 {
773 case PhysParameterEntry.APPLY_TO_NONE:
774 defaultLoc = val; // setting only the default value
775 break;
776 case PhysParameterEntry.APPLY_TO_ALL:
777 defaultLoc = val; // setting ALL also sets the default value
778 List<uint> objectIDs = lIDs;
779 string xparm = parm.ToLower();
780 float xval = val;
781 TaintedObject(delegate() {
782 foreach (uint lID in objectIDs)
783 {
784 BulletSimAPI.UpdateParameter(m_worldID, lID, xparm, xval);
785 }
786 });
787 break;
788 default:
789 // setting only one localID
790 TaintedUpdateParameter(parm, localID, val);
791 break;
792 }
793 }
794
795 // schedule the actual updating of the paramter to when the phys engine is not busy
796 private void TaintedUpdateParameter(string parm, uint localID, float val)
797 {
798 uint xlocalID = localID;
799 string xparm = parm.ToLower();
800 float xval = val;
801 TaintedObject(delegate() {
802 BulletSimAPI.UpdateParameter(m_worldID, xlocalID, xparm, xval);
803 });
804 }
805
806 // Get parameter.
807 // Return 'false' if not able to get the parameter.
808 public bool GetPhysicsParameter(string parm, out float value)
809 {
810 float val = 0f;
811 bool ret = true;
812 switch (parm.ToLower())
813 {
814 case "meshlod": val = (float)m_meshLOD; break;
815 case "maxsubstep": val = (float)m_maxSubSteps; break;
816 case "fixedtimestep": val = m_fixedTimeStep; break;
817 case "maxobjectmass": val = m_maximumObjectMass; break;
818
819 case "defaultfriction": val = m_params[0].defaultFriction; break;
820 case "defaultdensity": val = m_params[0].defaultDensity; break;
821 case "defaultrestitution": val = m_params[0].defaultRestitution; break;
822 case "collisionmargin": val = m_params[0].collisionMargin; break;
823 case "gravity": val = m_params[0].gravity; break;
824
825 case "lineardamping": val = m_params[0].linearDamping; break;
826 case "angulardamping": val = m_params[0].angularDamping; break;
827 case "deactivationtime": val = m_params[0].deactivationTime; break;
828 case "linearsleepingthreshold": val = m_params[0].linearSleepingThreshold; break;
829 case "angularsleepingthreshold": val = m_params[0].angularDamping; break;
830 case "ccdmotionthreshold": val = m_params[0].ccdMotionThreshold; break;
831 case "ccdsweptsphereradius": val = m_params[0].ccdSweptSphereRadius; break;
832
833 case "terrainfriction": val = m_params[0].terrainFriction; break;
834 case "terrainhitfraction": val = m_params[0].terrainHitFraction; break;
835 case "terrainrestitution": val = m_params[0].terrainRestitution; break;
836
837 case "avatarfriction": val = m_params[0].avatarFriction; break;
838 case "avatardensity": val = m_params[0].avatarDensity; break;
839 case "avatarrestitution": val = m_params[0].avatarRestitution; break;
840 case "avatarcapsuleradius": val = m_params[0].avatarCapsuleRadius; break;
841 case "avatarcapsuleheight": val = m_params[0].avatarCapsuleHeight; break;
842 default: ret = false; break;
843
844 }
845 value = val;
846 return ret;
847 }
848
849 #endregion IPhysicsParameters
850
851 #endregion Runtime settable parameters
852
853}
854}