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.cs553
1 files changed, 553 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..beffd21
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -0,0 +1,553 @@
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// Fix folding up feet
41// Fix terrain. Only flat terrain works. Terrain with shape is oriented wrong? Origined wrong?
42// Parameterize BulletSim. Pass a structure of parameters to the C++ code. Capsule size, friction, ...
43// Shift drag duplication of objects does not work
44// Adjust character capsule size when height is adjusted (ScenePresence.SetHeight)
45// Test sculpties
46// Compute physics FPS reasonably
47// Based on material, set density and friction
48// More efficient memory usage in passing hull information from BSPrim to BulletSim
49// Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly?
50// In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground)
51// At the moment, physical and phantom causes object to drop through the terrain
52// Should prim.link() and prim.delink() membership checking happen at taint time?
53// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once
54// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
55// Implement the genCollisions feature in BulletSim::SetObjectProperties (don't pass up unneeded collisions)
56// Implement LockAngularMotion
57// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
58// Built Galton board (lots of MoveTo's) and some slats were not positioned correctly (mistakes scattered)
59// No mistakes with ODE. Shape creation race condition?
60// Does NeedsMeshing() really need to exclude all the different shapes?
61//
62namespace OpenSim.Region.Physics.BulletSPlugin
63{
64public class BSScene : PhysicsScene
65{
66 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
67 private static readonly string LogHeader = "[BULLETS SCENE]";
68
69 private Dictionary<uint, BSCharacter> m_avatars = new Dictionary<uint, BSCharacter>();
70 private Dictionary<uint, BSPrim> m_prims = new Dictionary<uint, BSPrim>();
71 private List<BSPrim> m_vehicles = new List<BSPrim>();
72 private float[] m_heightMap;
73 private float m_waterLevel;
74 private uint m_worldID;
75 public uint WorldID { get { return m_worldID; } }
76
77 public IMesher mesher;
78 public int meshLOD = 32;
79
80 private int m_maxSubSteps = 10;
81 private float m_fixedTimeStep = 1f / 60f;
82 private long m_simulationStep = 0;
83 public long SimulationStep { get { return m_simulationStep; } }
84
85 private bool _meshSculptedPrim = true; // cause scuplted prims to get meshed
86 private bool _forceSimplePrimMeshing = false; // if a cube or sphere, let Bullet do internal shapes
87 public float maximumMassObject = 10000.01f;
88
89 public const uint TERRAIN_ID = 0;
90 public const uint GROUNDPLANE_ID = 1;
91
92 public float DefaultFriction = 0.70f;
93 public float DefaultDensity = 10.000006836f; // Aluminum g/cm3; TODO: compute based on object material
94 public Vector3 DefaultGravity = new Vector3(0, 0, -9.80665f);
95
96 public delegate void TaintCallback();
97 private List<TaintCallback> _taintedObjects;
98 private Object _taintLock = new Object();
99
100 private BulletSimAPI.DebugLogCallback debugLogCallbackHandle;
101
102 public BSScene(string identifier)
103 {
104 }
105
106 public override void Initialise(IMesher meshmerizer, IConfigSource config)
107 {
108 if (config != null)
109 {
110 IConfig pConfig = config.Configs["BulletSim"];
111 if (pConfig != null)
112 {
113 DefaultFriction = pConfig.GetFloat("Friction", DefaultFriction);
114 DefaultDensity = pConfig.GetFloat("Density", DefaultDensity);
115 // TODO: a lot more parameters that are passed to BulletSim
116 }
117 }
118 // if Debug, enable logging from the unmanaged code
119 if (m_log.IsDebugEnabled)
120 {
121 m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader);
122 debugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger);
123 BulletSimAPI.SetDebugLogCallback(debugLogCallbackHandle);
124 }
125
126 _meshSculptedPrim = true; // mesh sculpted prims
127 _forceSimplePrimMeshing = false; // use complex meshing if called for
128
129 _taintedObjects = new List<TaintCallback>();
130
131 mesher = meshmerizer;
132 // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
133 m_worldID = BulletSimAPI.Initialize(new Vector3(Constants.RegionSize, Constants.RegionSize, 4096f));
134 }
135
136 // Called directly from unmanaged code so don't do much
137 private void BulletLogger(string msg)
138 {
139 m_log.Debug("[BULLETS UNMANAGED]:" + msg);
140 }
141
142 public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
143 {
144 m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
145 return null;
146 }
147
148 public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying)
149 {
150 // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName);
151 BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying);
152 lock (m_avatars) m_avatars.Add(localID, actor);
153 return actor;
154 }
155
156 public override void RemoveAvatar(PhysicsActor actor)
157 {
158 // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader);
159 if (actor is BSCharacter)
160 {
161 ((BSCharacter)actor).Destroy();
162 }
163 try
164 {
165 lock (m_avatars) m_avatars.Remove(actor.LocalID);
166 }
167 catch (Exception e)
168 {
169 m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e);
170 }
171 }
172
173 public override void RemovePrim(PhysicsActor prim)
174 {
175 // m_log.DebugFormat("{0}: RemovePrim", LogHeader);
176 if (prim is BSPrim)
177 {
178 ((BSPrim)prim).Destroy();
179 }
180 try
181 {
182 lock (m_prims) m_prims.Remove(prim.LocalID);
183 }
184 catch (Exception e)
185 {
186 m_log.WarnFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e);
187 }
188 }
189
190 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
191 Vector3 size, Quaternion rotation) // deprecated
192 {
193 return null;
194 }
195 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
196 Vector3 size, Quaternion rotation, bool isPhysical)
197 {
198 m_log.ErrorFormat("{0}: CALL TO AddPrimShape in BSScene. NOT IMPLEMENTED", LogHeader);
199 return null;
200 }
201
202 public override PhysicsActor AddPrimShape(uint localID, string primName, PrimitiveBaseShape pbs, Vector3 position,
203 Vector3 size, Quaternion rotation, bool isPhysical)
204 {
205 // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName);
206 IMesh mesh = null;
207 if (NeedsMeshing(pbs))
208 {
209 // if the prim is complex, create the mesh for it.
210 // If simple (box or sphere) leave 'mesh' null and physics will do a native shape.
211 mesh = mesher.CreateMesh(primName, pbs, size, this.meshLOD, isPhysical);
212 }
213 BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, mesh, pbs, isPhysical);
214 lock (m_prims) m_prims.Add(localID, prim);
215 return prim;
216 }
217
218 // This is a call from the simulator saying that some physical property has been updated.
219 // The BulletS driver senses the changing of relevant properties so this taint
220 // information call is not needed.
221 public override void AddPhysicsActorTaint(PhysicsActor prim) { }
222
223 // Simulate one timestep
224 public override float Simulate(float timeStep)
225 {
226 int updatedEntityCount;
227 IntPtr updatedEntitiesPtr;
228 IntPtr[] updatedEntities;
229 int collidersCount;
230 IntPtr collidersPtr;
231 int[] colliders; // should be uint but Marshal.Copy does not have that overload
232
233 // update the prim states while we know the physics engine is not busy
234 ProcessTaints();
235
236 // Some of the prims operate with special vehicle properties
237 ProcessVehicles(timeStep);
238 ProcessTaints(); // the vehicles might have added taints
239
240 // step the physical world one interval
241 m_simulationStep++;
242 int numSubSteps = BulletSimAPI.PhysicsStep(m_worldID, timeStep, m_maxSubSteps, m_fixedTimeStep,
243 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
244
245 // if there were collisions, they show up here
246 if (collidersCount > 0)
247 {
248 colliders = new int[collidersCount];
249 Marshal.Copy(collidersPtr, colliders, 0, collidersCount);
250 for (int ii = 0; ii < collidersCount; ii+=2)
251 {
252 uint cA = (uint)colliders[ii];
253 uint cB = (uint)colliders[ii+1];
254 SendCollision(cA, cB);
255 SendCollision(cB, cA);
256 }
257 }
258
259 // if any of the objects had updated properties, they are returned in the updatedEntity structure
260 // TODO: figure out how to pass all of the EntityProperties structures in one marshal call.
261 if (updatedEntityCount > 0)
262 {
263 updatedEntities = new IntPtr[updatedEntityCount];
264 // fetch all the pointers to all the EntityProperties structures for these updates
265 Marshal.Copy(updatedEntitiesPtr, updatedEntities, 0, updatedEntityCount);
266 for (int ii = 0; ii < updatedEntityCount; ii++)
267 {
268 IntPtr updatePointer = updatedEntities[ii];
269 EntityProperties entprop = (EntityProperties)Marshal.PtrToStructure(updatePointer, typeof(EntityProperties));
270 // m_log.DebugFormat("{0}: entprop: id={1}, pos={2}", LogHeader, entprop.ID, entprop.Position);
271 BSCharacter actor;
272 if (m_avatars.TryGetValue(entprop.ID, out actor))
273 {
274 actor.UpdateProperties(entprop);
275 continue;
276 }
277 BSPrim prim;
278 if (m_prims.TryGetValue(entprop.ID, out prim))
279 {
280 prim.UpdateProperties(entprop);
281 }
282 }
283 }
284
285 // fps calculation wrong. This calculation returns about 1 in normal operation.
286 return timeStep / (numSubSteps * m_fixedTimeStep) * 1000f;
287 }
288
289 // Something has collided
290 private void SendCollision(uint localID, uint collidingWith)
291 {
292 if (localID == TERRAIN_ID || localID == GROUNDPLANE_ID)
293 {
294 // we never send collisions to the terrain
295 return;
296 }
297
298 ActorTypes type = ActorTypes.Prim;
299 if (collidingWith == TERRAIN_ID || collidingWith == GROUNDPLANE_ID)
300 type = ActorTypes.Ground;
301 else if (m_avatars.ContainsKey(collidingWith))
302 type = ActorTypes.Agent;
303
304 BSPrim prim;
305 if (m_prims.TryGetValue(localID, out prim)) {
306 prim.Collide(collidingWith, type, Vector3.Zero, Vector3.UnitZ, 0.01f);
307 return;
308 }
309 BSCharacter actor;
310 if (m_avatars.TryGetValue(localID, out actor)) {
311 actor.Collide(collidingWith, type, Vector3.Zero, Vector3.UnitZ, 0.01f);
312 return;
313 }
314 return;
315 }
316
317 public override void GetResults() { }
318
319 public override void SetTerrain(float[] heightMap) {
320 m_log.DebugFormat("{0}: SetTerrain", LogHeader);
321 m_heightMap = heightMap;
322 this.TaintedObject(delegate()
323 {
324 BulletSimAPI.SetHeightmap(m_worldID, m_heightMap);
325 });
326 }
327
328 public float GetTerrainHeightAtXY(float tX, float tY)
329 {
330 return m_heightMap[((int)tX) * Constants.RegionSize + ((int)tY)];
331 }
332
333 public override void SetWaterLevel(float baseheight)
334 {
335 m_waterLevel = baseheight;
336 }
337 public float GetWaterLevel()
338 {
339 return m_waterLevel;
340 }
341
342 public override void DeleteTerrain()
343 {
344 m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
345 }
346
347 public override void Dispose()
348 {
349 m_log.DebugFormat("{0}: Dispose()", LogHeader);
350 }
351
352 public override Dictionary<uint, float> GetTopColliders()
353 {
354 return new Dictionary<uint, float>();
355 }
356
357 public override bool IsThreaded { get { return false; } }
358
359 /// <summary>
360 /// Routine to figure out if we need to mesh this prim with our mesher
361 /// </summary>
362 /// <param name="pbs"></param>
363 /// <returns>true if the prim needs meshing</returns>
364 public bool NeedsMeshing(PrimitiveBaseShape pbs)
365 {
366 // most of this is redundant now as the mesher will return null if it cant mesh a prim
367 // but we still need to check for sculptie meshing being enabled so this is the most
368 // convenient place to do it for now...
369
370 // int iPropertiesNotSupportedDefault = 0;
371
372 if (pbs.SculptEntry && !_meshSculptedPrim)
373 {
374 // m_log.DebugFormat("{0}: NeedsMeshing: scultpy mesh", LogHeader);
375 return false;
376 }
377
378 // if it's a standard box or sphere with no cuts, hollows, twist or top shear, return false since Bullet
379 // can use an internal representation for the prim
380 if (!_forceSimplePrimMeshing)
381 {
382 // m_log.DebugFormat("{0}: NeedsMeshing: simple mesh: profshape={1}, curve={2}", LogHeader, pbs.ProfileShape, pbs.PathCurve);
383 if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
384 || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1
385 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z))
386 {
387
388 if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
389 && pbs.ProfileHollow == 0
390 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
391 && pbs.PathBegin == 0 && pbs.PathEnd == 0
392 && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
393 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
394 && pbs.PathShearX == 0 && pbs.PathShearY == 0)
395 {
396 return false;
397 }
398 }
399 }
400
401 /* TODO: verify that the mesher will now do all these shapes
402 if (pbs.ProfileHollow != 0)
403 iPropertiesNotSupportedDefault++;
404
405 if ((pbs.PathBegin != 0) || pbs.PathEnd != 0)
406 iPropertiesNotSupportedDefault++;
407
408 if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0))
409 iPropertiesNotSupportedDefault++;
410
411 if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0)
412 iPropertiesNotSupportedDefault++;
413
414 if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100))
415 iPropertiesNotSupportedDefault++;
416
417 if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0))
418 iPropertiesNotSupportedDefault++;
419
420 if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight)
421 iPropertiesNotSupportedDefault++;
422
423 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))
424 iPropertiesNotSupportedDefault++;
425
426 if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1)
427 iPropertiesNotSupportedDefault++;
428
429 // test for torus
430 if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square)
431 {
432 if (pbs.PathCurve == (byte)Extrusion.Curve1)
433 {
434 iPropertiesNotSupportedDefault++;
435 }
436 }
437 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
438 {
439 if (pbs.PathCurve == (byte)Extrusion.Straight)
440 {
441 iPropertiesNotSupportedDefault++;
442 }
443 // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits
444 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
445 {
446 iPropertiesNotSupportedDefault++;
447 }
448 }
449 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
450 {
451 if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2)
452 {
453 iPropertiesNotSupportedDefault++;
454 }
455 }
456 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
457 {
458 if (pbs.PathCurve == (byte)Extrusion.Straight)
459 {
460 iPropertiesNotSupportedDefault++;
461 }
462 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
463 {
464 iPropertiesNotSupportedDefault++;
465 }
466 }
467 if (iPropertiesNotSupportedDefault == 0)
468 {
469 return false;
470 }
471 */
472 return true;
473 }
474
475 // The calls to the PhysicsActors can't directly call into the physics engine
476 // because it might be busy. We we delay changes to a known time.
477 // We rely on C#'s closure to save and restore the context for the delegate.
478 public void TaintedObject(TaintCallback callback)
479 {
480 lock (_taintLock)
481 _taintedObjects.Add(callback);
482 return;
483 }
484
485 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues
486 // a callback into itself to do the actual property change. That callback is called
487 // here just before the physics engine is called to step the simulation.
488 public void ProcessTaints()
489 {
490 if (_taintedObjects.Count > 0) // save allocating new list if there is nothing to process
491 {
492 // swizzle a new list into the list location so we can process what's there
493 List<TaintCallback> oldList;
494 lock (_taintLock)
495 {
496 oldList = _taintedObjects;
497 _taintedObjects = new List<TaintCallback>();
498 }
499
500 foreach (TaintCallback callback in oldList)
501 {
502 try
503 {
504 callback();
505 }
506 catch (Exception e)
507 {
508 m_log.ErrorFormat("{0}: ProcessTaints: Exception: {1}", LogHeader, e);
509 }
510 }
511 oldList.Clear();
512 }
513 }
514
515 #region Vehicles
516 // Make so the scene will call this prim for vehicle actions each tick.
517 // Safe to call if prim is already in the vehicle list.
518 public void AddVehiclePrim(BSPrim vehicle)
519 {
520 lock (m_vehicles)
521 {
522 if (!m_vehicles.Contains(vehicle))
523 {
524 m_vehicles.Add(vehicle);
525 }
526 }
527 }
528
529 // Remove a prim from our list of vehicles.
530 // Safe to call if the prim is not in the vehicle list.
531 public void RemoveVehiclePrim(BSPrim vehicle)
532 {
533 lock (m_vehicles)
534 {
535 if (m_vehicles.Contains(vehicle))
536 {
537 m_vehicles.Remove(vehicle);
538 }
539 }
540 }
541
542 // Some prims have extra vehicle actions
543 // no locking because only called when physics engine is not busy
544 private void ProcessVehicles(float timeStep)
545 {
546 foreach (BSPrim prim in m_vehicles)
547 {
548 prim.StepVehicle(timeStep);
549 }
550 }
551 #endregion Vehicles
552}
553}