aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/PhysicsModules/Ode/OdeScene.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/PhysicsModules/Ode/OdeScene.cs')
-rw-r--r--OpenSim/Region/PhysicsModules/Ode/OdeScene.cs4117
1 files changed, 4117 insertions, 0 deletions
diff --git a/OpenSim/Region/PhysicsModules/Ode/OdeScene.cs b/OpenSim/Region/PhysicsModules/Ode/OdeScene.cs
new file mode 100644
index 0000000..8cc7f28
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/Ode/OdeScene.cs
@@ -0,0 +1,4117 @@
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 copyright
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 */
27
28// changes for varsize regions
29// note that raycasts need to have limited range
30// (even in normal regions)
31// or application thread stack may just blowup
32// see RayCast(ODERayCastRequest req)
33
34//#define USE_DRAWSTUFF
35//#define SPAM
36
37using System;
38using System.Collections.Generic;
39using System.Diagnostics;
40using System.IO;
41using System.Linq;
42using System.Reflection;
43using System.Runtime.InteropServices;
44using System.Threading;
45using log4net;
46using Nini.Config;
47using Mono.Addins;
48using Ode.NET;
49using OpenMetaverse;
50#if USE_DRAWSTUFF
51using Drawstuff.NET;
52#endif
53using OpenSim.Framework;
54using OpenSim.Region.PhysicsModules.SharedBase;
55using OpenSim.Region.Framework.Scenes;
56using OpenSim.Region.Framework.Interfaces;
57
58
59namespace OpenSim.Region.PhysicsModule.ODE
60{
61 public enum StatusIndicators : int
62 {
63 Generic = 0,
64 Start = 1,
65 End = 2
66 }
67
68// public struct sCollisionData
69// {
70// public uint ColliderLocalId;
71// public uint CollidedWithLocalId;
72// public int NumberOfCollisions;
73// public int CollisionType;
74// public int StatusIndicator;
75// public int lastframe;
76// }
77
78 [Flags]
79 public enum CollisionCategories : int
80 {
81 Disabled = 0,
82 Geom = 0x00000001,
83 Body = 0x00000002,
84 Space = 0x00000004,
85 Character = 0x00000008,
86 Land = 0x00000010,
87 Water = 0x00000020,
88 Wind = 0x00000040,
89 Sensor = 0x00000080,
90 Selected = 0x00000100
91 }
92
93 /// <summary>
94 /// Material type for a primitive
95 /// </summary>
96 public enum Material : int
97 {
98 /// <summary></summary>
99 Stone = 0,
100 /// <summary></summary>
101 Metal = 1,
102 /// <summary></summary>
103 Glass = 2,
104 /// <summary></summary>
105 Wood = 3,
106 /// <summary></summary>
107 Flesh = 4,
108 /// <summary></summary>
109 Plastic = 5,
110 /// <summary></summary>
111 Rubber = 6
112 }
113
114 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "ODEPhysicsScene")]
115 public class OdeScene : PhysicsScene, INonSharedRegionModule
116 {
117 private readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType.ToString());
118 private bool m_Enabled = false;
119
120 // private Dictionary<string, sCollisionData> m_storedCollisions = new Dictionary<string, sCollisionData>();
121
122 /// <summary>
123 /// Provide a sync object so that only one thread calls d.Collide() at a time across all OdeScene instances.
124 /// </summary>
125 /// <remarks>
126 /// With ODE as of r1755 (though also tested on r1860), only one thread can call d.Collide() at a
127 /// time, even where physics objects are in entirely different ODE worlds. This is because generating contacts
128 /// uses a static cache at the ODE level.
129 ///
130 /// Without locking, simulators running multiple regions will eventually crash with a native stack trace similar
131 /// to
132 ///
133 /// mono() [0x489171]
134 /// mono() [0x4d154f]
135 /// /lib/x86_64-linux-gnu/libpthread.so.0(+0xfc60) [0x7f6ded592c60]
136 /// .../opensim/bin/libode-x86_64.so(_ZN6Opcode11OBBCollider8_CollideEPKNS_14AABBNoLeafNodeE+0xd7a) [0x7f6dd822628a]
137 ///
138 /// ODE provides an experimental option to cache in thread local storage but compiling ODE with this option
139 /// causes OpenSimulator to immediately crash with a native stack trace similar to
140 ///
141 /// mono() [0x489171]
142 /// mono() [0x4d154f]
143 /// /lib/x86_64-linux-gnu/libpthread.so.0(+0xfc60) [0x7f03c9849c60]
144 /// .../opensim/bin/libode-x86_64.so(_Z12dCollideCCTLP6dxGeomS0_iP12dContactGeomi+0x92) [0x7f03b44bcf82]
145 /// </remarks>
146 internal static Object UniversalColliderSyncObject = new Object();
147
148 /// <summary>
149 /// Is stats collecting enabled for this ODE scene?
150 /// </summary>
151 public bool CollectStats { get; set; }
152
153 /// <summary>
154 /// Statistics for this scene.
155 /// </summary>
156 private Dictionary<string, float> m_stats = new Dictionary<string, float>();
157
158 /// <summary>
159 /// Stat name for total number of avatars in this ODE scene.
160 /// </summary>
161 public const string ODETotalAvatarsStatName = "ODETotalAvatars";
162
163 /// <summary>
164 /// Stat name for total number of prims in this ODE scene.
165 /// </summary>
166 public const string ODETotalPrimsStatName = "ODETotalPrims";
167
168 /// <summary>
169 /// Stat name for total number of prims with active physics in this ODE scene.
170 /// </summary>
171 public const string ODEActivePrimsStatName = "ODEActivePrims";
172
173 /// <summary>
174 /// Stat name for the total time spent in ODE frame processing.
175 /// </summary>
176 /// <remarks>
177 /// A sanity check for the main scene loop physics time.
178 /// </remarks>
179 public const string ODETotalFrameMsStatName = "ODETotalFrameMS";
180
181 /// <summary>
182 /// Stat name for time spent processing avatar taints per frame
183 /// </summary>
184 public const string ODEAvatarTaintMsStatName = "ODEAvatarTaintFrameMS";
185
186 /// <summary>
187 /// Stat name for time spent processing prim taints per frame
188 /// </summary>
189 public const string ODEPrimTaintMsStatName = "ODEPrimTaintFrameMS";
190
191 /// <summary>
192 /// Stat name for time spent calculating avatar forces per frame.
193 /// </summary>
194 public const string ODEAvatarForcesFrameMsStatName = "ODEAvatarForcesFrameMS";
195
196 /// <summary>
197 /// Stat name for time spent calculating prim forces per frame
198 /// </summary>
199 public const string ODEPrimForcesFrameMsStatName = "ODEPrimForcesFrameMS";
200
201 /// <summary>
202 /// Stat name for time spent fulfilling raycasting requests per frame
203 /// </summary>
204 public const string ODERaycastingFrameMsStatName = "ODERaycastingFrameMS";
205
206 /// <summary>
207 /// Stat name for time spent in native code that actually steps through the simulation.
208 /// </summary>
209 public const string ODENativeStepFrameMsStatName = "ODENativeStepFrameMS";
210
211 /// <summary>
212 /// Stat name for the number of milliseconds that ODE spends in native space collision code.
213 /// </summary>
214 public const string ODENativeSpaceCollisionFrameMsStatName = "ODENativeSpaceCollisionFrameMS";
215
216 /// <summary>
217 /// Stat name for milliseconds that ODE spends in native geom collision code.
218 /// </summary>
219 public const string ODENativeGeomCollisionFrameMsStatName = "ODENativeGeomCollisionFrameMS";
220
221 /// <summary>
222 /// Time spent in collision processing that is not spent in native space or geom collision code.
223 /// </summary>
224 public const string ODEOtherCollisionFrameMsStatName = "ODEOtherCollisionFrameMS";
225
226 /// <summary>
227 /// Stat name for time spent notifying listeners of collisions
228 /// </summary>
229 public const string ODECollisionNotificationFrameMsStatName = "ODECollisionNotificationFrameMS";
230
231 /// <summary>
232 /// Stat name for milliseconds spent updating avatar position and velocity
233 /// </summary>
234 public const string ODEAvatarUpdateFrameMsStatName = "ODEAvatarUpdateFrameMS";
235
236 /// <summary>
237 /// Stat name for the milliseconds spent updating prim position and velocity
238 /// </summary>
239 public const string ODEPrimUpdateFrameMsStatName = "ODEPrimUpdateFrameMS";
240
241 /// <summary>
242 /// Stat name for avatar collisions with another entity.
243 /// </summary>
244 public const string ODEAvatarContactsStatsName = "ODEAvatarContacts";
245
246 /// <summary>
247 /// Stat name for prim collisions with another entity.
248 /// </summary>
249 public const string ODEPrimContactsStatName = "ODEPrimContacts";
250
251 /// <summary>
252 /// Used to hold tick numbers for stat collection purposes.
253 /// </summary>
254 private int m_nativeCollisionStartTick;
255
256 /// <summary>
257 /// A messy way to tell if we need to avoid adding a collision time because this was already done in the callback.
258 /// </summary>
259 private bool m_inCollisionTiming;
260
261 /// <summary>
262 /// A temporary holder for the number of avatar collisions in a frame, so we can work out how many object
263 /// collisions occured using the _perloopcontact if stats collection is enabled.
264 /// </summary>
265 private int m_tempAvatarCollisionsThisFrame;
266
267 /// <summary>
268 /// Used in calculating physics frame time dilation
269 /// </summary>
270 private int tickCountFrameRun;
271
272 /// <summary>
273 /// Used in calculating physics frame time dilation
274 /// </summary>
275 private int latertickcount;
276
277 private Random fluidRandomizer = new Random(Environment.TickCount);
278
279 public bool m_suportCombine = true;
280
281 private uint m_regionWidth = Constants.RegionSize;
282 private uint m_regionHeight = Constants.RegionSize;
283
284 private float ODE_STEPSIZE = 0.0178f;
285 private float metersInSpace = 29.9f;
286 private float m_timeDilation = 1.0f;
287
288 public float gravityx = 0f;
289 public float gravityy = 0f;
290 public float gravityz = -9.8f;
291
292 public float AvatarTerminalVelocity { get; set; }
293
294 private float contactsurfacelayer = 0.001f;
295
296 private int worldHashspaceLow = -4;
297 private int worldHashspaceHigh = 128;
298
299 private int smallHashspaceLow = -4;
300 private int smallHashspaceHigh = 66;
301
302 private float waterlevel = 0f;
303 private int framecount = 0;
304 //private int m_returncollisions = 10;
305
306 private IntPtr contactgroup;
307
308// internal IntPtr WaterGeom;
309
310 private float nmTerrainContactFriction = 255.0f;
311 private float nmTerrainContactBounce = 0.1f;
312 private float nmTerrainContactERP = 0.1025f;
313
314 private float mTerrainContactFriction = 75f;
315 private float mTerrainContactBounce = 0.1f;
316 private float mTerrainContactERP = 0.05025f;
317
318 private float nmAvatarObjectContactFriction = 250f;
319 private float nmAvatarObjectContactBounce = 0.1f;
320
321 private float mAvatarObjectContactFriction = 75f;
322 private float mAvatarObjectContactBounce = 0.1f;
323
324 private float avPIDD = 3200f;
325 private float avPIDP = 1400f;
326 private float avCapRadius = 0.37f;
327 private float avStandupTensor = 2000000f;
328
329 /// <summary>
330 /// true = old compatibility mode with leaning capsule; false = new corrected mode
331 /// </summary>
332 /// <remarks>
333 /// Even when set to false, the capsule still tilts but this is done in a different way.
334 /// </remarks>
335 public bool IsAvCapsuleTilted { get; private set; }
336
337 private float avDensity = 80f;
338// private float avHeightFudgeFactor = 0.52f;
339 private float avMovementDivisorWalk = 1.3f;
340 private float avMovementDivisorRun = 0.8f;
341 private float minimumGroundFlightOffset = 3f;
342 public float maximumMassObject = 10000.01f;
343
344 public bool meshSculptedPrim = true;
345 public bool forceSimplePrimMeshing = false;
346
347 public float meshSculptLOD = 32;
348 public float MeshSculptphysicalLOD = 16;
349
350 public float geomDefaultDensity = 10.000006836f;
351
352 public int geomContactPointsStartthrottle = 3;
353 public int geomUpdatesPerThrottledUpdate = 15;
354 private const int avatarExpectedContacts = 3;
355
356 public float bodyPIDD = 35f;
357 public float bodyPIDG = 25;
358
359 public int geomCrossingFailuresBeforeOutofbounds = 5;
360
361 public float bodyMotorJointMaxforceTensor = 2;
362
363 public int bodyFramesAutoDisable = 20;
364
365 private float[] _watermap;
366 private bool m_filterCollisions = true;
367
368 private d.NearCallback nearCallback;
369 public d.TriCallback triCallback;
370 public d.TriArrayCallback triArrayCallback;
371
372 /// <summary>
373 /// Avatars in the physics scene.
374 /// </summary>
375 private readonly HashSet<OdeCharacter> _characters = new HashSet<OdeCharacter>();
376
377 /// <summary>
378 /// Prims in the physics scene.
379 /// </summary>
380 private readonly HashSet<OdePrim> _prims = new HashSet<OdePrim>();
381
382 /// <summary>
383 /// Prims in the physics scene that are subject to physics, not just collisions.
384 /// </summary>
385 private readonly HashSet<OdePrim> _activeprims = new HashSet<OdePrim>();
386
387 /// <summary>
388 /// Prims that the simulator has created/deleted/updated and so need updating in ODE.
389 /// </summary>
390 private readonly HashSet<OdePrim> _taintedPrims = new HashSet<OdePrim>();
391
392 /// <summary>
393 /// Record a character that has taints to be processed.
394 /// </summary>
395 private readonly HashSet<OdeCharacter> _taintedActors = new HashSet<OdeCharacter>();
396
397 /// <summary>
398 /// Keep record of contacts in the physics loop so that we can remove duplicates.
399 /// </summary>
400 private readonly List<d.ContactGeom> _perloopContact = new List<d.ContactGeom>();
401
402 /// <summary>
403 /// A dictionary of actors that should receive collision events.
404 /// </summary>
405 private readonly Dictionary<uint, PhysicsActor> m_collisionEventActors = new Dictionary<uint, PhysicsActor>();
406
407 /// <summary>
408 /// A dictionary of collision event changes that are waiting to be processed.
409 /// </summary>
410 private readonly Dictionary<uint, PhysicsActor> m_collisionEventActorsChanges = new Dictionary<uint, PhysicsActor>();
411
412 /// <summary>
413 /// Maps a unique geometry id (a memory location) to a physics actor name.
414 /// </summary>
415 /// <remarks>
416 /// Only actors participating in collisions have geometries. This has to be maintained separately from
417 /// actor_name_map because terrain and water currently don't conceptually have a physics actor of their own
418 /// apart from the singleton PANull
419 /// </remarks>
420 public Dictionary<IntPtr, String> geom_name_map = new Dictionary<IntPtr, String>();
421
422 /// <summary>
423 /// Maps a unique geometry id (a memory location) to a physics actor.
424 /// </summary>
425 /// <remarks>
426 /// Only actors participating in collisions have geometries.
427 /// </remarks>
428 public Dictionary<IntPtr, PhysicsActor> actor_name_map = new Dictionary<IntPtr, PhysicsActor>();
429
430 /// <summary>
431 /// Defects list to remove characters that no longer have finite positions due to some other bug.
432 /// </summary>
433 /// <remarks>
434 /// Used repeatedly in Simulate() but initialized once here.
435 /// </remarks>
436 private readonly List<OdeCharacter> defects = new List<OdeCharacter>();
437
438 private bool m_NINJA_physics_joints_enabled = false;
439 //private Dictionary<String, IntPtr> jointpart_name_map = new Dictionary<String,IntPtr>();
440 private readonly Dictionary<String, List<PhysicsJoint>> joints_connecting_actor = new Dictionary<String, List<PhysicsJoint>>();
441 private d.ContactGeom[] contacts;
442
443 /// <summary>
444 /// Lock only briefly. accessed by external code (to request new joints) and by OdeScene.Simulate() to move those joints into pending/active
445 /// </summary>
446 private readonly List<PhysicsJoint> requestedJointsToBeCreated = new List<PhysicsJoint>();
447
448 /// <summary>
449 /// can lock for longer. accessed only by OdeScene.
450 /// </summary>
451 private readonly List<PhysicsJoint> pendingJoints = new List<PhysicsJoint>();
452
453 /// <summary>
454 /// can lock for longer. accessed only by OdeScene.
455 /// </summary>
456 private readonly List<PhysicsJoint> activeJoints = new List<PhysicsJoint>();
457
458 /// <summary>
459 /// lock only briefly. accessed by external code (to request deletion of joints) and by OdeScene.Simulate() to move those joints out of pending/active
460 /// </summary>
461 private readonly List<string> requestedJointsToBeDeleted = new List<string>();
462
463 private Object externalJointRequestsLock = new Object();
464 private readonly Dictionary<String, PhysicsJoint> SOPName_to_activeJoint = new Dictionary<String, PhysicsJoint>();
465 private readonly Dictionary<String, PhysicsJoint> SOPName_to_pendingJoint = new Dictionary<String, PhysicsJoint>();
466 private readonly DoubleDictionary<Vector3, IntPtr, IntPtr> RegionTerrain = new DoubleDictionary<Vector3, IntPtr, IntPtr>();
467 private readonly Dictionary<IntPtr,float[]> TerrainHeightFieldHeights = new Dictionary<IntPtr, float[]>();
468
469 private d.Contact contact;
470 private d.Contact TerrainContact;
471 private d.Contact AvatarMovementprimContact;
472 private d.Contact AvatarMovementTerrainContact;
473 private d.Contact WaterContact;
474 private d.Contact[,] m_materialContacts;
475
476//Ckrinke: Comment out until used. We declare it, initialize it, but do not use it
477//Ckrinke private int m_randomizeWater = 200;
478 private int m_physicsiterations = 10;
479 private const float m_SkipFramesAtms = 0.40f; // Drop frames gracefully at a 400 ms lag
480 private readonly PhysicsActor PANull = new NullPhysicsActor();
481// private float step_time = 0.0f;
482//Ckrinke: Comment out until used. We declare it, initialize it, but do not use it
483//Ckrinke private int ms = 0;
484 public IntPtr world;
485 //private bool returncollisions = false;
486 // private uint obj1LocalID = 0;
487 private uint obj2LocalID = 0;
488 //private int ctype = 0;
489 private OdeCharacter cc1;
490 private OdePrim cp1;
491 private OdeCharacter cc2;
492 private OdePrim cp2;
493 private int p1ExpectedPoints = 0;
494 private int p2ExpectedPoints = 0;
495 //private int cStartStop = 0;
496 //private string cDictKey = "";
497
498 public IntPtr space;
499
500 //private IntPtr tmpSpace;
501 // split static geometry collision handling into spaces of 30 meters
502 public IntPtr[,] staticPrimspace;
503
504 /// <summary>
505 /// Used to lock the entire physics scene. Locked during the main part of Simulate()
506 /// </summary>
507 internal Object OdeLock = new Object();
508
509 private bool _worldInitialized = false;
510
511 public IMesher mesher;
512
513 private IConfigSource m_config;
514
515 public bool physics_logging = false;
516 public int physics_logging_interval = 0;
517 public bool physics_logging_append_existing_logfile = false;
518
519 private bool avplanted = false;
520 private bool av_av_collisions_off = false;
521
522 public d.Vector3 xyz = new d.Vector3(128.1640f, 128.3079f, 25.7600f);
523 public d.Vector3 hpr = new d.Vector3(125.5000f, -17.0000f, 0.0000f);
524
525 private volatile int m_global_contactcount = 0;
526
527 private Vector3 m_worldOffset = Vector3.Zero;
528 public Vector2 WorldExtents = new Vector2((int)Constants.RegionSize, (int)Constants.RegionSize);
529 private PhysicsScene m_parentScene = null;
530
531 float spacesPerMeterX;
532 float spacesPerMeterY;
533 int spaceGridMaxX;
534 int spaceGridMaxY;
535
536 private ODERayCastRequestManager m_rayCastManager;
537
538
539 #region INonSharedRegionModule
540 public string Name
541 {
542 get { return "OpenDynamicsEngine"; }
543 }
544
545 public Type ReplaceableInterface
546 {
547 get { return null; }
548 }
549
550 public void Initialise(IConfigSource source)
551 {
552 // TODO: Move this out of Startup
553 IConfig config = source.Configs["Startup"];
554 if (config != null)
555 {
556 string physics = config.GetString("physics", string.Empty);
557 if (physics == Name)
558 {
559 m_Enabled = true;
560 m_config = source;
561
562 // We do this so that OpenSimulator on Windows loads the correct native ODE library depending on whether
563 // it's running as a 32-bit process or a 64-bit one. By invoking LoadLibary here, later DLLImports
564 // will find it already loaded later on.
565 //
566 // This isn't necessary for other platforms (e.g. Mac OSX and Linux) since the DLL used can be
567 // controlled in Ode.NET.dll.config
568 if (Util.IsWindows())
569 Util.LoadArchSpecificWindowsDll("ode.dll");
570
571 // Initializing ODE only when a scene is created allows alternative ODE plugins to co-habit (according to
572 // http://opensimulator.org/mantis/view.php?id=2750).
573 d.InitODE();
574
575 }
576 }
577
578 }
579
580 public void Close()
581 {
582 }
583
584 public void AddRegion(Scene scene)
585 {
586 if (!m_Enabled)
587 return;
588
589 EngineType = Name;
590 PhysicsSceneName = EngineType + "/" + scene.RegionInfo.RegionName;
591
592 scene.RegisterModuleInterface<PhysicsScene>(this);
593 Vector3 extent = new Vector3(scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY, scene.RegionInfo.RegionSizeZ);
594 Initialise(extent);
595 InitialiseFromConfig(m_config);
596
597 // This may not be that good since terrain may not be avaiable at this point
598 base.Initialise(scene.PhysicsRequestAsset,
599 (scene.Heightmap != null ? scene.Heightmap.GetFloatsSerialised() : new float[(int)(extent.X * extent.Y)]),
600 (float)scene.RegionInfo.RegionSettings.WaterHeight);
601
602 }
603
604 public void RemoveRegion(Scene scene)
605 {
606 if (!m_Enabled)
607 return;
608 }
609
610 public void RegionLoaded(Scene scene)
611 {
612 if (!m_Enabled)
613 return;
614
615 mesher = scene.RequestModuleInterface<IMesher>();
616 if (mesher == null)
617 m_log.WarnFormat("[ODE SCENE]: No mesher in {0}. Things will not work well.", PhysicsSceneName);
618 }
619 #endregion
620
621 /// <summary>
622 /// Initiailizes the scene
623 /// Sets many properties that ODE requires to be stable
624 /// These settings need to be tweaked 'exactly' right or weird stuff happens.
625 /// </summary>
626 private void Initialise(Vector3 regionExtent)
627 {
628 WorldExtents.X = regionExtent.X;
629 m_regionWidth = (uint)regionExtent.X;
630 WorldExtents.Y = regionExtent.Y;
631 m_regionHeight = (uint)regionExtent.Y;
632
633 m_suportCombine = false;
634
635 nearCallback = near;
636 triCallback = TriCallback;
637 triArrayCallback = TriArrayCallback;
638 m_rayCastManager = new ODERayCastRequestManager(this);
639
640 // Create the world and the first space
641 world = d.WorldCreate();
642 space = d.HashSpaceCreate(IntPtr.Zero);
643
644 contactgroup = d.JointGroupCreate(0);
645
646 d.WorldSetAutoDisableFlag(world, false);
647
648 #if USE_DRAWSTUFF
649 Thread viewthread = new Thread(new ParameterizedThreadStart(startvisualization));
650 viewthread.Start();
651 #endif
652
653 // _watermap = new float[258 * 258];
654
655 // Zero out the prim spaces array (we split our space into smaller spaces so
656 // we can hit test less.
657 }
658
659#if USE_DRAWSTUFF
660 public void startvisualization(object o)
661 {
662 ds.Functions fn;
663 fn.version = ds.VERSION;
664 fn.start = new ds.CallbackFunction(start);
665 fn.step = new ds.CallbackFunction(step);
666 fn.command = new ds.CallbackFunction(command);
667 fn.stop = null;
668 fn.path_to_textures = "./textures";
669 string[] args = new string[0];
670 ds.SimulationLoop(args.Length, args, 352, 288, ref fn);
671 }
672#endif
673
674 // Initialize from configs
675 private void InitialiseFromConfig(IConfigSource config)
676 {
677 InitializeExtraStats();
678
679 m_config = config;
680 // Defaults
681
682 if (Environment.OSVersion.Platform == PlatformID.Unix)
683 {
684 avPIDD = 3200.0f;
685 avPIDP = 1400.0f;
686 avStandupTensor = 2000000f;
687 }
688 else
689 {
690 avPIDD = 2200.0f;
691 avPIDP = 900.0f;
692 avStandupTensor = 550000f;
693 }
694
695 int contactsPerCollision = 80;
696
697 if (m_config != null)
698 {
699 IConfig physicsconfig = m_config.Configs["ODEPhysicsSettings"];
700 if (physicsconfig != null)
701 {
702 CollectStats = physicsconfig.GetBoolean("collect_stats", false);
703
704 gravityx = physicsconfig.GetFloat("world_gravityx", 0f);
705 gravityy = physicsconfig.GetFloat("world_gravityy", 0f);
706 gravityz = physicsconfig.GetFloat("world_gravityz", -9.8f);
707
708 float avatarTerminalVelocity = physicsconfig.GetFloat("avatar_terminal_velocity", 54f);
709 AvatarTerminalVelocity = Util.Clamp<float>(avatarTerminalVelocity, 0, 255f);
710 if (AvatarTerminalVelocity != avatarTerminalVelocity)
711 {
712 m_log.WarnFormat(
713 "[ODE SCENE]: avatar_terminal_velocity of {0} is invalid. Clamping to {1}",
714 avatarTerminalVelocity, AvatarTerminalVelocity);
715 }
716
717 worldHashspaceLow = physicsconfig.GetInt("world_hashspace_size_low", -4);
718 worldHashspaceHigh = physicsconfig.GetInt("world_hashspace_size_high", 128);
719
720 metersInSpace = physicsconfig.GetFloat("meters_in_small_space", 29.9f);
721 smallHashspaceLow = physicsconfig.GetInt("small_hashspace_size_low", -4);
722 smallHashspaceHigh = physicsconfig.GetInt("small_hashspace_size_high", 66);
723
724 contactsurfacelayer = physicsconfig.GetFloat("world_contact_surface_layer", 0.001f);
725
726 nmTerrainContactFriction = physicsconfig.GetFloat("nm_terraincontact_friction", 255.0f);
727 nmTerrainContactBounce = physicsconfig.GetFloat("nm_terraincontact_bounce", 0.1f);
728 nmTerrainContactERP = physicsconfig.GetFloat("nm_terraincontact_erp", 0.1025f);
729
730 mTerrainContactFriction = physicsconfig.GetFloat("m_terraincontact_friction", 75f);
731 mTerrainContactBounce = physicsconfig.GetFloat("m_terraincontact_bounce", 0.05f);
732 mTerrainContactERP = physicsconfig.GetFloat("m_terraincontact_erp", 0.05025f);
733
734 nmAvatarObjectContactFriction = physicsconfig.GetFloat("objectcontact_friction", 250f);
735 nmAvatarObjectContactBounce = physicsconfig.GetFloat("objectcontact_bounce", 0.2f);
736
737 mAvatarObjectContactFriction = physicsconfig.GetFloat("m_avatarobjectcontact_friction", 75f);
738 mAvatarObjectContactBounce = physicsconfig.GetFloat("m_avatarobjectcontact_bounce", 0.1f);
739
740 ODE_STEPSIZE = physicsconfig.GetFloat("world_stepsize", ODE_STEPSIZE);
741 m_physicsiterations = physicsconfig.GetInt("world_internal_steps_without_collisions", 10);
742
743 avDensity = physicsconfig.GetFloat("av_density", 80f);
744// avHeightFudgeFactor = physicsconfig.GetFloat("av_height_fudge_factor", 0.52f);
745 avMovementDivisorWalk = physicsconfig.GetFloat("av_movement_divisor_walk", 1.3f);
746 avMovementDivisorRun = physicsconfig.GetFloat("av_movement_divisor_run", 0.8f);
747 avCapRadius = physicsconfig.GetFloat("av_capsule_radius", 0.37f);
748 avplanted = physicsconfig.GetBoolean("av_planted", false);
749 av_av_collisions_off = physicsconfig.GetBoolean("av_av_collisions_off", false);
750
751 IsAvCapsuleTilted = physicsconfig.GetBoolean("av_capsule_tilted", false);
752
753 contactsPerCollision = physicsconfig.GetInt("contacts_per_collision", 80);
754
755 geomContactPointsStartthrottle = physicsconfig.GetInt("geom_contactpoints_start_throttling", 5);
756 geomUpdatesPerThrottledUpdate = physicsconfig.GetInt("geom_updates_before_throttled_update", 15);
757 geomCrossingFailuresBeforeOutofbounds = physicsconfig.GetInt("geom_crossing_failures_before_outofbounds", 5);
758
759 geomDefaultDensity = physicsconfig.GetFloat("geometry_default_density", 10.000006836f);
760 bodyFramesAutoDisable = physicsconfig.GetInt("body_frames_auto_disable", 20);
761
762 bodyPIDD = physicsconfig.GetFloat("body_pid_derivative", 35f);
763 bodyPIDG = physicsconfig.GetFloat("body_pid_gain", 25f);
764
765 forceSimplePrimMeshing = physicsconfig.GetBoolean("force_simple_prim_meshing", forceSimplePrimMeshing);
766 meshSculptedPrim = physicsconfig.GetBoolean("mesh_sculpted_prim", true);
767 meshSculptLOD = physicsconfig.GetFloat("mesh_lod", 32f);
768 MeshSculptphysicalLOD = physicsconfig.GetFloat("mesh_physical_lod", 16f);
769 m_filterCollisions = physicsconfig.GetBoolean("filter_collisions", false);
770
771
772
773 if (Environment.OSVersion.Platform == PlatformID.Unix)
774 {
775 avPIDD = physicsconfig.GetFloat("av_pid_derivative_linux", 2200.0f);
776 avPIDP = physicsconfig.GetFloat("av_pid_proportional_linux", 900.0f);
777 avStandupTensor = physicsconfig.GetFloat("av_capsule_standup_tensor_linux", 550000f);
778 bodyMotorJointMaxforceTensor = physicsconfig.GetFloat("body_motor_joint_maxforce_tensor_linux", 5f);
779 }
780 else
781 {
782 avPIDD = physicsconfig.GetFloat("av_pid_derivative_win", 2200.0f);
783 avPIDP = physicsconfig.GetFloat("av_pid_proportional_win", 900.0f);
784 avStandupTensor = physicsconfig.GetFloat("av_capsule_standup_tensor_win", 550000f);
785 bodyMotorJointMaxforceTensor = physicsconfig.GetFloat("body_motor_joint_maxforce_tensor_win", 5f);
786 }
787
788 physics_logging = physicsconfig.GetBoolean("physics_logging", false);
789 physics_logging_interval = physicsconfig.GetInt("physics_logging_interval", 0);
790 physics_logging_append_existing_logfile = physicsconfig.GetBoolean("physics_logging_append_existing_logfile", false);
791
792 m_NINJA_physics_joints_enabled = physicsconfig.GetBoolean("use_NINJA_physics_joints", false);
793 minimumGroundFlightOffset = physicsconfig.GetFloat("minimum_ground_flight_offset", 3f);
794 maximumMassObject = physicsconfig.GetFloat("maximum_mass_object", 10000.01f);
795 }
796 }
797
798 contacts = new d.ContactGeom[contactsPerCollision];
799
800 spacesPerMeterX = 1.0f / metersInSpace;
801 spacesPerMeterY = 1.0f / metersInSpace;
802
803 spaceGridMaxX = (int)(WorldExtents.X * spacesPerMeterX);
804 spaceGridMaxY = (int)(WorldExtents.Y * spacesPerMeterY);
805
806 // note: limit number of spaces
807 if (spaceGridMaxX > 24)
808 {
809 spaceGridMaxX = 24;
810 spacesPerMeterX = spaceGridMaxX / WorldExtents.X;
811 }
812 if (spaceGridMaxY > 24)
813 {
814 spaceGridMaxY = 24;
815 spacesPerMeterY = spaceGridMaxY / WorldExtents.Y ;
816 }
817
818 staticPrimspace = new IntPtr[spaceGridMaxX, spaceGridMaxY];
819
820 // make this index limits
821 spaceGridMaxX--;
822 spaceGridMaxY--;
823
824
825
826 // Centeral contact friction and bounce
827 // ckrinke 11/10/08 Enabling soft_erp but not soft_cfm until I figure out why
828 // an avatar falls through in Z but not in X or Y when walking on a prim.
829 contact.surface.mode |= d.ContactFlags.SoftERP;
830 contact.surface.mu = nmAvatarObjectContactFriction;
831 contact.surface.bounce = nmAvatarObjectContactBounce;
832 contact.surface.soft_cfm = 0.010f;
833 contact.surface.soft_erp = 0.010f;
834
835 // Terrain contact friction and Bounce
836 // This is the *non* moving version. Use this when an avatar
837 // isn't moving to keep it in place better
838 TerrainContact.surface.mode |= d.ContactFlags.SoftERP;
839 TerrainContact.surface.mu = nmTerrainContactFriction;
840 TerrainContact.surface.bounce = nmTerrainContactBounce;
841 TerrainContact.surface.soft_erp = nmTerrainContactERP;
842
843 WaterContact.surface.mode |= (d.ContactFlags.SoftERP | d.ContactFlags.SoftCFM);
844 WaterContact.surface.mu = 0f; // No friction
845 WaterContact.surface.bounce = 0.0f; // No bounce
846 WaterContact.surface.soft_cfm = 0.010f;
847 WaterContact.surface.soft_erp = 0.010f;
848
849 // Prim contact friction and bounce
850 // THis is the *non* moving version of friction and bounce
851 // Use this when an avatar comes in contact with a prim
852 // and is moving
853 AvatarMovementprimContact.surface.mu = mAvatarObjectContactFriction;
854 AvatarMovementprimContact.surface.bounce = mAvatarObjectContactBounce;
855
856 // Terrain contact friction bounce and various error correcting calculations
857 // Use this when an avatar is in contact with the terrain and moving.
858 AvatarMovementTerrainContact.surface.mode |= d.ContactFlags.SoftERP;
859 AvatarMovementTerrainContact.surface.mu = mTerrainContactFriction;
860 AvatarMovementTerrainContact.surface.bounce = mTerrainContactBounce;
861 AvatarMovementTerrainContact.surface.soft_erp = mTerrainContactERP;
862
863 /*
864 <summary></summary>
865 Stone = 0,
866 /// <summary></summary>
867 Metal = 1,
868 /// <summary></summary>
869 Glass = 2,
870 /// <summary></summary>
871 Wood = 3,
872 /// <summary></summary>
873 Flesh = 4,
874 /// <summary></summary>
875 Plastic = 5,
876 /// <summary></summary>
877 Rubber = 6
878 */
879
880 m_materialContacts = new d.Contact[7,2];
881
882 m_materialContacts[(int)Material.Stone, 0] = new d.Contact();
883 m_materialContacts[(int)Material.Stone, 0].surface.mode |= d.ContactFlags.SoftERP;
884 m_materialContacts[(int)Material.Stone, 0].surface.mu = nmAvatarObjectContactFriction;
885 m_materialContacts[(int)Material.Stone, 0].surface.bounce = nmAvatarObjectContactBounce;
886 m_materialContacts[(int)Material.Stone, 0].surface.soft_cfm = 0.010f;
887 m_materialContacts[(int)Material.Stone, 0].surface.soft_erp = 0.010f;
888
889 m_materialContacts[(int)Material.Stone, 1] = new d.Contact();
890 m_materialContacts[(int)Material.Stone, 1].surface.mode |= d.ContactFlags.SoftERP;
891 m_materialContacts[(int)Material.Stone, 1].surface.mu = mAvatarObjectContactFriction;
892 m_materialContacts[(int)Material.Stone, 1].surface.bounce = mAvatarObjectContactBounce;
893 m_materialContacts[(int)Material.Stone, 1].surface.soft_cfm = 0.010f;
894 m_materialContacts[(int)Material.Stone, 1].surface.soft_erp = 0.010f;
895
896 m_materialContacts[(int)Material.Metal, 0] = new d.Contact();
897 m_materialContacts[(int)Material.Metal, 0].surface.mode |= d.ContactFlags.SoftERP;
898 m_materialContacts[(int)Material.Metal, 0].surface.mu = nmAvatarObjectContactFriction;
899 m_materialContacts[(int)Material.Metal, 0].surface.bounce = nmAvatarObjectContactBounce;
900 m_materialContacts[(int)Material.Metal, 0].surface.soft_cfm = 0.010f;
901 m_materialContacts[(int)Material.Metal, 0].surface.soft_erp = 0.010f;
902
903 m_materialContacts[(int)Material.Metal, 1] = new d.Contact();
904 m_materialContacts[(int)Material.Metal, 1].surface.mode |= d.ContactFlags.SoftERP;
905 m_materialContacts[(int)Material.Metal, 1].surface.mu = mAvatarObjectContactFriction;
906 m_materialContacts[(int)Material.Metal, 1].surface.bounce = mAvatarObjectContactBounce;
907 m_materialContacts[(int)Material.Metal, 1].surface.soft_cfm = 0.010f;
908 m_materialContacts[(int)Material.Metal, 1].surface.soft_erp = 0.010f;
909
910 m_materialContacts[(int)Material.Glass, 0] = new d.Contact();
911 m_materialContacts[(int)Material.Glass, 0].surface.mode |= d.ContactFlags.SoftERP;
912 m_materialContacts[(int)Material.Glass, 0].surface.mu = 1f;
913 m_materialContacts[(int)Material.Glass, 0].surface.bounce = 0.5f;
914 m_materialContacts[(int)Material.Glass, 0].surface.soft_cfm = 0.010f;
915 m_materialContacts[(int)Material.Glass, 0].surface.soft_erp = 0.010f;
916
917 /*
918 private float nmAvatarObjectContactFriction = 250f;
919 private float nmAvatarObjectContactBounce = 0.1f;
920
921 private float mAvatarObjectContactFriction = 75f;
922 private float mAvatarObjectContactBounce = 0.1f;
923 */
924 m_materialContacts[(int)Material.Glass, 1] = new d.Contact();
925 m_materialContacts[(int)Material.Glass, 1].surface.mode |= d.ContactFlags.SoftERP;
926 m_materialContacts[(int)Material.Glass, 1].surface.mu = 1f;
927 m_materialContacts[(int)Material.Glass, 1].surface.bounce = 0.5f;
928 m_materialContacts[(int)Material.Glass, 1].surface.soft_cfm = 0.010f;
929 m_materialContacts[(int)Material.Glass, 1].surface.soft_erp = 0.010f;
930
931 m_materialContacts[(int)Material.Wood, 0] = new d.Contact();
932 m_materialContacts[(int)Material.Wood, 0].surface.mode |= d.ContactFlags.SoftERP;
933 m_materialContacts[(int)Material.Wood, 0].surface.mu = nmAvatarObjectContactFriction;
934 m_materialContacts[(int)Material.Wood, 0].surface.bounce = nmAvatarObjectContactBounce;
935 m_materialContacts[(int)Material.Wood, 0].surface.soft_cfm = 0.010f;
936 m_materialContacts[(int)Material.Wood, 0].surface.soft_erp = 0.010f;
937
938 m_materialContacts[(int)Material.Wood, 1] = new d.Contact();
939 m_materialContacts[(int)Material.Wood, 1].surface.mode |= d.ContactFlags.SoftERP;
940 m_materialContacts[(int)Material.Wood, 1].surface.mu = mAvatarObjectContactFriction;
941 m_materialContacts[(int)Material.Wood, 1].surface.bounce = mAvatarObjectContactBounce;
942 m_materialContacts[(int)Material.Wood, 1].surface.soft_cfm = 0.010f;
943 m_materialContacts[(int)Material.Wood, 1].surface.soft_erp = 0.010f;
944
945 m_materialContacts[(int)Material.Flesh, 0] = new d.Contact();
946 m_materialContacts[(int)Material.Flesh, 0].surface.mode |= d.ContactFlags.SoftERP;
947 m_materialContacts[(int)Material.Flesh, 0].surface.mu = nmAvatarObjectContactFriction;
948 m_materialContacts[(int)Material.Flesh, 0].surface.bounce = nmAvatarObjectContactBounce;
949 m_materialContacts[(int)Material.Flesh, 0].surface.soft_cfm = 0.010f;
950 m_materialContacts[(int)Material.Flesh, 0].surface.soft_erp = 0.010f;
951
952 m_materialContacts[(int)Material.Flesh, 1] = new d.Contact();
953 m_materialContacts[(int)Material.Flesh, 1].surface.mode |= d.ContactFlags.SoftERP;
954 m_materialContacts[(int)Material.Flesh, 1].surface.mu = mAvatarObjectContactFriction;
955 m_materialContacts[(int)Material.Flesh, 1].surface.bounce = mAvatarObjectContactBounce;
956 m_materialContacts[(int)Material.Flesh, 1].surface.soft_cfm = 0.010f;
957 m_materialContacts[(int)Material.Flesh, 1].surface.soft_erp = 0.010f;
958
959 m_materialContacts[(int)Material.Plastic, 0] = new d.Contact();
960 m_materialContacts[(int)Material.Plastic, 0].surface.mode |= d.ContactFlags.SoftERP;
961 m_materialContacts[(int)Material.Plastic, 0].surface.mu = nmAvatarObjectContactFriction;
962 m_materialContacts[(int)Material.Plastic, 0].surface.bounce = nmAvatarObjectContactBounce;
963 m_materialContacts[(int)Material.Plastic, 0].surface.soft_cfm = 0.010f;
964 m_materialContacts[(int)Material.Plastic, 0].surface.soft_erp = 0.010f;
965
966 m_materialContacts[(int)Material.Plastic, 1] = new d.Contact();
967 m_materialContacts[(int)Material.Plastic, 1].surface.mode |= d.ContactFlags.SoftERP;
968 m_materialContacts[(int)Material.Plastic, 1].surface.mu = mAvatarObjectContactFriction;
969 m_materialContacts[(int)Material.Plastic, 1].surface.bounce = mAvatarObjectContactBounce;
970 m_materialContacts[(int)Material.Plastic, 1].surface.soft_cfm = 0.010f;
971 m_materialContacts[(int)Material.Plastic, 1].surface.soft_erp = 0.010f;
972
973 m_materialContacts[(int)Material.Rubber, 0] = new d.Contact();
974 m_materialContacts[(int)Material.Rubber, 0].surface.mode |= d.ContactFlags.SoftERP;
975 m_materialContacts[(int)Material.Rubber, 0].surface.mu = nmAvatarObjectContactFriction;
976 m_materialContacts[(int)Material.Rubber, 0].surface.bounce = nmAvatarObjectContactBounce;
977 m_materialContacts[(int)Material.Rubber, 0].surface.soft_cfm = 0.010f;
978 m_materialContacts[(int)Material.Rubber, 0].surface.soft_erp = 0.010f;
979
980 m_materialContacts[(int)Material.Rubber, 1] = new d.Contact();
981 m_materialContacts[(int)Material.Rubber, 1].surface.mode |= d.ContactFlags.SoftERP;
982 m_materialContacts[(int)Material.Rubber, 1].surface.mu = mAvatarObjectContactFriction;
983 m_materialContacts[(int)Material.Rubber, 1].surface.bounce = mAvatarObjectContactBounce;
984 m_materialContacts[(int)Material.Rubber, 1].surface.soft_cfm = 0.010f;
985 m_materialContacts[(int)Material.Rubber, 1].surface.soft_erp = 0.010f;
986
987 d.HashSpaceSetLevels(space, worldHashspaceLow, worldHashspaceHigh);
988
989 // Set the gravity,, don't disable things automatically (we set it explicitly on some things)
990
991 d.WorldSetGravity(world, gravityx, gravityy, gravityz);
992 d.WorldSetContactSurfaceLayer(world, contactsurfacelayer);
993
994 d.WorldSetLinearDamping(world, 256f);
995 d.WorldSetAngularDamping(world, 256f);
996 d.WorldSetAngularDampingThreshold(world, 256f);
997 d.WorldSetLinearDampingThreshold(world, 256f);
998 d.WorldSetMaxAngularSpeed(world, 256f);
999
1000 // Set how many steps we go without running collision testing
1001 // This is in addition to the step size.
1002 // Essentially Steps * m_physicsiterations
1003 d.WorldSetQuickStepNumIterations(world, m_physicsiterations);
1004 //d.WorldSetContactMaxCorrectingVel(world, 1000.0f);
1005
1006 for (int i = 0; i < staticPrimspace.GetLength(0); i++)
1007 {
1008 for (int j = 0; j < staticPrimspace.GetLength(1); j++)
1009 {
1010 staticPrimspace[i, j] = IntPtr.Zero;
1011 }
1012 }
1013
1014 _worldInitialized = true;
1015 }
1016
1017// internal void waitForSpaceUnlock(IntPtr space)
1018// {
1019// //if (space != IntPtr.Zero)
1020// //while (d.SpaceLockQuery(space)) { } // Wait and do nothing
1021// }
1022
1023// /// <summary>
1024// /// Debug space message for printing the space that a prim/avatar is in.
1025// /// </summary>
1026// /// <param name="pos"></param>
1027// /// <returns>Returns which split up space the given position is in.</returns>
1028// public string whichspaceamIin(Vector3 pos)
1029// {
1030// return calculateSpaceForGeom(pos).ToString();
1031// }
1032
1033 #region Collision Detection
1034
1035 /// <summary>
1036 /// Collides two geometries.
1037 /// </summary>
1038 /// <returns></returns>
1039 /// <param name='geom1'></param>
1040 /// <param name='geom2'>/param>
1041 /// <param name='maxContacts'></param>
1042 /// <param name='contactsArray'></param>
1043 /// <param name='contactGeomSize'></param>
1044 private int CollideGeoms(
1045 IntPtr geom1, IntPtr geom2, int maxContacts, Ode.NET.d.ContactGeom[] contactsArray, int contactGeomSize)
1046 {
1047 int count;
1048
1049 lock (OdeScene.UniversalColliderSyncObject)
1050 {
1051 // We do this inside the lock so that we don't count any delay in acquiring it
1052 if (CollectStats)
1053 m_nativeCollisionStartTick = Util.EnvironmentTickCount();
1054
1055 count = d.Collide(geom1, geom2, maxContacts, contactsArray, contactGeomSize);
1056 }
1057
1058 // We do this outside the lock so that any waiting threads aren't held up, though the effect is probably
1059 // negligable
1060 if (CollectStats)
1061 m_stats[ODENativeGeomCollisionFrameMsStatName]
1062 += Util.EnvironmentTickCountSubtract(m_nativeCollisionStartTick);
1063
1064 return count;
1065 }
1066
1067 /// <summary>
1068 /// Collide two spaces or a space and a geometry.
1069 /// </summary>
1070 /// <param name='space1'></param>
1071 /// <param name='space2'>/param>
1072 /// <param name='data'></param>
1073 private void CollideSpaces(IntPtr space1, IntPtr space2, IntPtr data)
1074 {
1075 if (CollectStats)
1076 {
1077 m_inCollisionTiming = true;
1078 m_nativeCollisionStartTick = Util.EnvironmentTickCount();
1079 }
1080
1081 d.SpaceCollide2(space1, space2, data, nearCallback);
1082
1083 if (CollectStats && m_inCollisionTiming)
1084 {
1085 m_stats[ODENativeSpaceCollisionFrameMsStatName]
1086 += Util.EnvironmentTickCountSubtract(m_nativeCollisionStartTick);
1087 m_inCollisionTiming = false;
1088 }
1089 }
1090
1091 /// <summary>
1092 /// This is our near callback. A geometry is near a body
1093 /// </summary>
1094 /// <param name="space">The space that contains the geoms. Remember, spaces are also geoms</param>
1095 /// <param name="g1">a geometry or space</param>
1096 /// <param name="g2">another geometry or space</param>
1097 private void near(IntPtr space, IntPtr g1, IntPtr g2)
1098 {
1099 if (CollectStats && m_inCollisionTiming)
1100 {
1101 m_stats[ODENativeSpaceCollisionFrameMsStatName]
1102 += Util.EnvironmentTickCountSubtract(m_nativeCollisionStartTick);
1103 m_inCollisionTiming = false;
1104 }
1105
1106// m_log.DebugFormat("[PHYSICS]: Colliding {0} and {1} in {2}", g1, g2, space);
1107 // no lock here! It's invoked from within Simulate(), which is thread-locked
1108
1109 // Test if we're colliding a geom with a space.
1110 // If so we have to drill down into the space recursively
1111
1112 if (d.GeomIsSpace(g1) || d.GeomIsSpace(g2))
1113 {
1114 if (g1 == IntPtr.Zero || g2 == IntPtr.Zero)
1115 return;
1116
1117 // Separating static prim geometry spaces.
1118 // We'll be calling near recursivly if one
1119 // of them is a space to find all of the
1120 // contact points in the space
1121 try
1122 {
1123 CollideSpaces(g1, g2, IntPtr.Zero);
1124 }
1125 catch (AccessViolationException)
1126 {
1127 m_log.Error("[ODE SCENE]: Unable to collide test a space");
1128 return;
1129 }
1130 //Colliding a space or a geom with a space or a geom. so drill down
1131
1132 //Collide all geoms in each space..
1133 //if (d.GeomIsSpace(g1)) d.SpaceCollide(g1, IntPtr.Zero, nearCallback);
1134 //if (d.GeomIsSpace(g2)) d.SpaceCollide(g2, IntPtr.Zero, nearCallback);
1135 return;
1136 }
1137
1138 if (g1 == IntPtr.Zero || g2 == IntPtr.Zero)
1139 return;
1140
1141 IntPtr b1 = d.GeomGetBody(g1);
1142 IntPtr b2 = d.GeomGetBody(g2);
1143
1144 // d.GeomClassID id = d.GeomGetClass(g1);
1145
1146 String name1 = null;
1147 String name2 = null;
1148
1149 if (!geom_name_map.TryGetValue(g1, out name1))
1150 {
1151 name1 = "null";
1152 }
1153 if (!geom_name_map.TryGetValue(g2, out name2))
1154 {
1155 name2 = "null";
1156 }
1157
1158 //if (id == d.GeomClassId.TriMeshClass)
1159 //{
1160 // m_log.InfoFormat("near: A collision was detected between {1} and {2}", 0, name1, name2);
1161 //m_log.Debug("near: A collision was detected between {1} and {2}", 0, name1, name2);
1162 //}
1163
1164 // Figure out how many contact points we have
1165 int count = 0;
1166
1167 try
1168 {
1169 // Colliding Geom To Geom
1170 // This portion of the function 'was' blatantly ripped off from BoxStack.cs
1171
1172 if (g1 == g2)
1173 return; // Can't collide with yourself
1174
1175 if (b1 != IntPtr.Zero && b2 != IntPtr.Zero && d.AreConnectedExcluding(b1, b2, d.JointType.Contact))
1176 return;
1177
1178 count = CollideGeoms(g1, g2, contacts.Length, contacts, d.ContactGeom.SizeOf);
1179
1180 // All code after this is only relevant if we have any collisions
1181 if (count <= 0)
1182 return;
1183
1184 if (count > contacts.Length)
1185 m_log.Error("[ODE SCENE]: Got " + count + " contacts when we asked for a maximum of " + contacts.Length);
1186 }
1187 catch (SEHException)
1188 {
1189 m_log.Error(
1190 "[ODE SCENE]: The Operating system shut down ODE because of corrupt memory. This could be a result of really irregular terrain. If this repeats continuously, restart using Basic Physics and terrain fill your terrain. Restarting the sim.");
1191 base.TriggerPhysicsBasedRestart();
1192 }
1193 catch (Exception e)
1194 {
1195 m_log.ErrorFormat("[ODE SCENE]: Unable to collide test an object: {0}", e.Message);
1196 return;
1197 }
1198
1199 PhysicsActor p1;
1200 PhysicsActor p2;
1201
1202 p1ExpectedPoints = 0;
1203 p2ExpectedPoints = 0;
1204
1205 if (!actor_name_map.TryGetValue(g1, out p1))
1206 {
1207 p1 = PANull;
1208 }
1209
1210 if (!actor_name_map.TryGetValue(g2, out p2))
1211 {
1212 p2 = PANull;
1213 }
1214
1215 ContactPoint maxDepthContact = new ContactPoint();
1216 if (p1.CollisionScore + count >= float.MaxValue)
1217 p1.CollisionScore = 0;
1218 p1.CollisionScore += count;
1219
1220 if (p2.CollisionScore + count >= float.MaxValue)
1221 p2.CollisionScore = 0;
1222 p2.CollisionScore += count;
1223
1224 for (int i = 0; i < count; i++)
1225 {
1226 d.ContactGeom curContact = contacts[i];
1227
1228 if (curContact.depth > maxDepthContact.PenetrationDepth)
1229 {
1230 maxDepthContact = new ContactPoint(
1231 new Vector3(curContact.pos.X, curContact.pos.Y, curContact.pos.Z),
1232 new Vector3(curContact.normal.X, curContact.normal.Y, curContact.normal.Z),
1233 curContact.depth
1234 );
1235 }
1236
1237 //m_log.Warn("[CCOUNT]: " + count);
1238 IntPtr joint;
1239 // If we're colliding with terrain, use 'TerrainContact' instead of contact.
1240 // allows us to have different settings
1241
1242 // We only need to test p2 for 'jump crouch purposes'
1243 if (p2 is OdeCharacter && p1.PhysicsActorType == (int)ActorTypes.Prim)
1244 {
1245 // Testing if the collision is at the feet of the avatar
1246
1247 //m_log.DebugFormat("[PHYSICS]: {0} - {1} - {2} - {3}", curContact.pos.Z, p2.Position.Z, (p2.Position.Z - curContact.pos.Z), (p2.Size.Z * 0.6f));
1248 if ((p2.Position.Z - curContact.pos.Z) > (p2.Size.Z * 0.6f))
1249 p2.IsColliding = true;
1250 }
1251 else
1252 {
1253 p2.IsColliding = true;
1254 }
1255
1256 //if ((framecount % m_returncollisions) == 0)
1257
1258 switch (p1.PhysicsActorType)
1259 {
1260 case (int)ActorTypes.Agent:
1261 p1ExpectedPoints = avatarExpectedContacts;
1262 p2.CollidingObj = true;
1263 break;
1264 case (int)ActorTypes.Prim:
1265 if (p1 != null && p1 is OdePrim)
1266 p1ExpectedPoints = ((OdePrim) p1).ExpectedCollisionContacts;
1267
1268 if (p2.Velocity.LengthSquared() > 0.0f)
1269 p2.CollidingObj = true;
1270 break;
1271 case (int)ActorTypes.Unknown:
1272 p2.CollidingGround = true;
1273 break;
1274 default:
1275 p2.CollidingGround = true;
1276 break;
1277 }
1278
1279 // we don't want prim or avatar to explode
1280
1281 #region InterPenetration Handling - Unintended physics explosions
1282# region disabled code1
1283
1284 if (curContact.depth >= 0.08f)
1285 {
1286 //This is disabled at the moment only because it needs more tweaking
1287 //It will eventually be uncommented
1288 /*
1289 if (contact.depth >= 1.00f)
1290 {
1291 //m_log.Debug("[PHYSICS]: " + contact.depth.ToString());
1292 }
1293
1294 //If you interpenetrate a prim with an agent
1295 if ((p2.PhysicsActorType == (int) ActorTypes.Agent &&
1296 p1.PhysicsActorType == (int) ActorTypes.Prim) ||
1297 (p1.PhysicsActorType == (int) ActorTypes.Agent &&
1298 p2.PhysicsActorType == (int) ActorTypes.Prim))
1299 {
1300
1301 //contact.depth = contact.depth * 4.15f;
1302 /*
1303 if (p2.PhysicsActorType == (int) ActorTypes.Agent)
1304 {
1305 p2.CollidingObj = true;
1306 contact.depth = 0.003f;
1307 p2.Velocity = p2.Velocity + new PhysicsVector(0, 0, 2.5f);
1308 OdeCharacter character = (OdeCharacter) p2;
1309 character.SetPidStatus(true);
1310 contact.pos = new d.Vector3(contact.pos.X + (p1.Size.X / 2), contact.pos.Y + (p1.Size.Y / 2), contact.pos.Z + (p1.Size.Z / 2));
1311
1312 }
1313 else
1314 {
1315
1316 //contact.depth = 0.0000000f;
1317 }
1318 if (p1.PhysicsActorType == (int) ActorTypes.Agent)
1319 {
1320
1321 p1.CollidingObj = true;
1322 contact.depth = 0.003f;
1323 p1.Velocity = p1.Velocity + new PhysicsVector(0, 0, 2.5f);
1324 contact.pos = new d.Vector3(contact.pos.X + (p2.Size.X / 2), contact.pos.Y + (p2.Size.Y / 2), contact.pos.Z + (p2.Size.Z / 2));
1325 OdeCharacter character = (OdeCharacter)p1;
1326 character.SetPidStatus(true);
1327 }
1328 else
1329 {
1330
1331 //contact.depth = 0.0000000f;
1332 }
1333
1334
1335
1336 }
1337*/
1338 // If you interpenetrate a prim with another prim
1339 /*
1340 if (p1.PhysicsActorType == (int) ActorTypes.Prim && p2.PhysicsActorType == (int) ActorTypes.Prim)
1341 {
1342 #region disabledcode2
1343 //OdePrim op1 = (OdePrim)p1;
1344 //OdePrim op2 = (OdePrim)p2;
1345 //op1.m_collisionscore++;
1346 //op2.m_collisionscore++;
1347
1348 //if (op1.m_collisionscore > 8000 || op2.m_collisionscore > 8000)
1349 //{
1350 //op1.m_taintdisable = true;
1351 //AddPhysicsActorTaint(p1);
1352 //op2.m_taintdisable = true;
1353 //AddPhysicsActorTaint(p2);
1354 //}
1355
1356 //if (contact.depth >= 0.25f)
1357 //{
1358 // Don't collide, one or both prim will expld.
1359
1360 //op1.m_interpenetrationcount++;
1361 //op2.m_interpenetrationcount++;
1362 //interpenetrations_before_disable = 200;
1363 //if (op1.m_interpenetrationcount >= interpenetrations_before_disable)
1364 //{
1365 //op1.m_taintdisable = true;
1366 //AddPhysicsActorTaint(p1);
1367 //}
1368 //if (op2.m_interpenetrationcount >= interpenetrations_before_disable)
1369 //{
1370 // op2.m_taintdisable = true;
1371 //AddPhysicsActorTaint(p2);
1372 //}
1373
1374 //contact.depth = contact.depth / 8f;
1375 //contact.normal = new d.Vector3(0, 0, 1);
1376 //}
1377 //if (op1.m_disabled || op2.m_disabled)
1378 //{
1379 //Manually disabled objects stay disabled
1380 //contact.depth = 0f;
1381 //}
1382 #endregion
1383 }
1384 */
1385#endregion
1386 if (curContact.depth >= 1.00f)
1387 {
1388 //m_log.Info("[P]: " + contact.depth.ToString());
1389 if ((p2.PhysicsActorType == (int) ActorTypes.Agent &&
1390 p1.PhysicsActorType == (int) ActorTypes.Unknown) ||
1391 (p1.PhysicsActorType == (int) ActorTypes.Agent &&
1392 p2.PhysicsActorType == (int) ActorTypes.Unknown))
1393 {
1394 if (p2.PhysicsActorType == (int) ActorTypes.Agent)
1395 {
1396 if (p2 is OdeCharacter)
1397 {
1398 OdeCharacter character = (OdeCharacter) p2;
1399
1400 //p2.CollidingObj = true;
1401 curContact.depth = 0.00000003f;
1402 p2.Velocity = p2.Velocity + new Vector3(0f, 0f, 0.5f);
1403 curContact.pos =
1404 new d.Vector3(curContact.pos.X + (p1.Size.X/2),
1405 curContact.pos.Y + (p1.Size.Y/2),
1406 curContact.pos.Z + (p1.Size.Z/2));
1407 character.SetPidStatus(true);
1408 }
1409 }
1410
1411 if (p1.PhysicsActorType == (int) ActorTypes.Agent)
1412 {
1413 if (p1 is OdeCharacter)
1414 {
1415 OdeCharacter character = (OdeCharacter) p1;
1416
1417 //p2.CollidingObj = true;
1418 curContact.depth = 0.00000003f;
1419 p1.Velocity = p1.Velocity + new Vector3(0f, 0f, 0.5f);
1420 curContact.pos =
1421 new d.Vector3(curContact.pos.X + (p1.Size.X/2),
1422 curContact.pos.Y + (p1.Size.Y/2),
1423 curContact.pos.Z + (p1.Size.Z/2));
1424 character.SetPidStatus(true);
1425 }
1426 }
1427 }
1428 }
1429 }
1430
1431 #endregion
1432
1433 // Logic for collision handling
1434 // Note, that if *all* contacts are skipped (VolumeDetect)
1435 // The prim still detects (and forwards) collision events but
1436 // appears to be phantom for the world
1437 Boolean skipThisContact = false;
1438
1439 if ((p1 is OdePrim) && (((OdePrim)p1).m_isVolumeDetect))
1440 skipThisContact = true; // No collision on volume detect prims
1441
1442 if (av_av_collisions_off)
1443 if ((p1 is OdeCharacter) && (p2 is OdeCharacter))
1444 skipThisContact = true;
1445
1446 if (!skipThisContact && (p2 is OdePrim) && (((OdePrim)p2).m_isVolumeDetect))
1447 skipThisContact = true; // No collision on volume detect prims
1448
1449 if (!skipThisContact && curContact.depth < 0f)
1450 skipThisContact = true;
1451
1452 if (!skipThisContact && checkDupe(curContact, p2.PhysicsActorType))
1453 skipThisContact = true;
1454
1455 const int maxContactsbeforedeath = 4000;
1456 joint = IntPtr.Zero;
1457
1458 if (!skipThisContact)
1459 {
1460 _perloopContact.Add(curContact);
1461
1462 if (name1 == "Terrain" || name2 == "Terrain")
1463 {
1464 if ((p2.PhysicsActorType == (int) ActorTypes.Agent) &&
1465 (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f))
1466 {
1467 p2ExpectedPoints = avatarExpectedContacts;
1468 // Avatar is moving on terrain, use the movement terrain contact
1469 AvatarMovementTerrainContact.geom = curContact;
1470
1471 if (m_global_contactcount < maxContactsbeforedeath)
1472 {
1473 joint = d.JointCreateContact(world, contactgroup, ref AvatarMovementTerrainContact);
1474 m_global_contactcount++;
1475 }
1476 }
1477 else
1478 {
1479 if (p2.PhysicsActorType == (int)ActorTypes.Agent)
1480 {
1481 p2ExpectedPoints = avatarExpectedContacts;
1482 // Avatar is standing on terrain, use the non moving terrain contact
1483 TerrainContact.geom = curContact;
1484
1485 if (m_global_contactcount < maxContactsbeforedeath)
1486 {
1487 joint = d.JointCreateContact(world, contactgroup, ref TerrainContact);
1488 m_global_contactcount++;
1489 }
1490 }
1491 else
1492 {
1493 if (p2.PhysicsActorType == (int)ActorTypes.Prim && p1.PhysicsActorType == (int)ActorTypes.Prim)
1494 {
1495 // prim prim contact
1496 // int pj294950 = 0;
1497 int movintYN = 0;
1498 int material = (int) Material.Wood;
1499 // prim terrain contact
1500 if (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f)
1501 {
1502 movintYN = 1;
1503 }
1504
1505 if (p2 is OdePrim)
1506 {
1507 material = ((OdePrim) p2).m_material;
1508 p2ExpectedPoints = ((OdePrim)p2).ExpectedCollisionContacts;
1509 }
1510
1511 // Unnessesary because p1 is defined above
1512 //if (p1 is OdePrim)
1513 // {
1514 // p1ExpectedPoints = ((OdePrim)p1).ExpectedCollisionContacts;
1515 // }
1516 //m_log.DebugFormat("Material: {0}", material);
1517
1518 m_materialContacts[material, movintYN].geom = curContact;
1519
1520 if (m_global_contactcount < maxContactsbeforedeath)
1521 {
1522 joint = d.JointCreateContact(world, contactgroup, ref m_materialContacts[material, movintYN]);
1523 m_global_contactcount++;
1524 }
1525 }
1526 else
1527 {
1528 int movintYN = 0;
1529 // prim terrain contact
1530 if (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f)
1531 {
1532 movintYN = 1;
1533 }
1534
1535 int material = (int)Material.Wood;
1536
1537 if (p2 is OdePrim)
1538 {
1539 material = ((OdePrim)p2).m_material;
1540 p2ExpectedPoints = ((OdePrim)p2).ExpectedCollisionContacts;
1541 }
1542
1543 //m_log.DebugFormat("Material: {0}", material);
1544 m_materialContacts[material, movintYN].geom = curContact;
1545
1546 if (m_global_contactcount < maxContactsbeforedeath)
1547 {
1548 joint = d.JointCreateContact(world, contactgroup, ref m_materialContacts[material, movintYN]);
1549 m_global_contactcount++;
1550 }
1551 }
1552 }
1553 }
1554 //if (p2.PhysicsActorType == (int)ActorTypes.Prim)
1555 //{
1556 //m_log.Debug("[PHYSICS]: prim contacting with ground");
1557 //}
1558 }
1559 else if (name1 == "Water" || name2 == "Water")
1560 {
1561 /*
1562 if ((p2.PhysicsActorType == (int) ActorTypes.Prim))
1563 {
1564 }
1565 else
1566 {
1567 }
1568 */
1569 //WaterContact.surface.soft_cfm = 0.0000f;
1570 //WaterContact.surface.soft_erp = 0.00000f;
1571 if (curContact.depth > 0.1f)
1572 {
1573 curContact.depth *= 52;
1574 //contact.normal = new d.Vector3(0, 0, 1);
1575 //contact.pos = new d.Vector3(0, 0, contact.pos.Z - 5f);
1576 }
1577
1578 WaterContact.geom = curContact;
1579
1580 if (m_global_contactcount < maxContactsbeforedeath)
1581 {
1582 joint = d.JointCreateContact(world, contactgroup, ref WaterContact);
1583 m_global_contactcount++;
1584 }
1585 //m_log.Info("[PHYSICS]: Prim Water Contact" + contact.depth);
1586 }
1587 else
1588 {
1589 if ((p2.PhysicsActorType == (int)ActorTypes.Agent))
1590 {
1591 p2ExpectedPoints = avatarExpectedContacts;
1592 if ((Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f))
1593 {
1594 // Avatar is moving on a prim, use the Movement prim contact
1595 AvatarMovementprimContact.geom = curContact;
1596
1597 if (m_global_contactcount < maxContactsbeforedeath)
1598 {
1599 joint = d.JointCreateContact(world, contactgroup, ref AvatarMovementprimContact);
1600 m_global_contactcount++;
1601 }
1602 }
1603 else
1604 {
1605 // Avatar is standing still on a prim, use the non movement contact
1606 contact.geom = curContact;
1607
1608 if (m_global_contactcount < maxContactsbeforedeath)
1609 {
1610 joint = d.JointCreateContact(world, contactgroup, ref contact);
1611 m_global_contactcount++;
1612 }
1613 }
1614 }
1615 else if (p2.PhysicsActorType == (int)ActorTypes.Prim)
1616 {
1617 //p1.PhysicsActorType
1618 int material = (int)Material.Wood;
1619
1620 if (p2 is OdePrim)
1621 {
1622 material = ((OdePrim)p2).m_material;
1623 p2ExpectedPoints = ((OdePrim)p2).ExpectedCollisionContacts;
1624 }
1625
1626 //m_log.DebugFormat("Material: {0}", material);
1627 m_materialContacts[material, 0].geom = curContact;
1628
1629 if (m_global_contactcount < maxContactsbeforedeath)
1630 {
1631 joint = d.JointCreateContact(world, contactgroup, ref m_materialContacts[material, 0]);
1632 m_global_contactcount++;
1633 }
1634 }
1635 }
1636
1637 if (m_global_contactcount < maxContactsbeforedeath && joint != IntPtr.Zero) // stack collide!
1638 {
1639 d.JointAttach(joint, b1, b2);
1640 m_global_contactcount++;
1641 }
1642 }
1643
1644 collision_accounting_events(p1, p2, maxDepthContact);
1645
1646 if (count > ((p1ExpectedPoints + p2ExpectedPoints) * 0.25) + (geomContactPointsStartthrottle))
1647 {
1648 // If there are more then 3 contact points, it's likely
1649 // that we've got a pile of objects, so ...
1650 // We don't want to send out hundreds of terse updates over and over again
1651 // so lets throttle them and send them again after it's somewhat sorted out.
1652 p2.ThrottleUpdates = true;
1653 }
1654 //m_log.Debug(count.ToString());
1655 //m_log.Debug("near: A collision was detected between {1} and {2}", 0, name1, name2);
1656 }
1657 }
1658
1659 private bool checkDupe(d.ContactGeom contactGeom, int atype)
1660 {
1661 if (!m_filterCollisions)
1662 return false;
1663
1664 bool result = false;
1665
1666 ActorTypes at = (ActorTypes)atype;
1667
1668 foreach (d.ContactGeom contact in _perloopContact)
1669 {
1670 //if ((contact.g1 == contactGeom.g1 && contact.g2 == contactGeom.g2))
1671 //{
1672 // || (contact.g2 == contactGeom.g1 && contact.g1 == contactGeom.g2)
1673 if (at == ActorTypes.Agent)
1674 {
1675 if (((Math.Abs(contactGeom.normal.X - contact.normal.X) < 1.026f)
1676 && (Math.Abs(contactGeom.normal.Y - contact.normal.Y) < 0.303f)
1677 && (Math.Abs(contactGeom.normal.Z - contact.normal.Z) < 0.065f)))
1678 {
1679 if (Math.Abs(contact.depth - contactGeom.depth) < 0.052f)
1680 {
1681 //contactGeom.depth *= .00005f;
1682 //m_log.DebugFormat("[Collsion]: Depth {0}", Math.Abs(contact.depth - contactGeom.depth));
1683 // m_log.DebugFormat("[Collision]: <{0},{1},{2}>", Math.Abs(contactGeom.normal.X - contact.normal.X), Math.Abs(contactGeom.normal.Y - contact.normal.Y), Math.Abs(contactGeom.normal.Z - contact.normal.Z));
1684 result = true;
1685 break;
1686 }
1687// else
1688// {
1689// //m_log.DebugFormat("[Collsion]: Depth {0}", Math.Abs(contact.depth - contactGeom.depth));
1690// }
1691 }
1692// else
1693// {
1694// //m_log.DebugFormat("[Collision]: <{0},{1},{2}>", Math.Abs(contactGeom.normal.X - contact.normal.X), Math.Abs(contactGeom.normal.Y - contact.normal.Y), Math.Abs(contactGeom.normal.Z - contact.normal.Z));
1695// //int i = 0;
1696// }
1697 }
1698 else if (at == ActorTypes.Prim)
1699 {
1700 //d.AABB aabb1 = new d.AABB();
1701 //d.AABB aabb2 = new d.AABB();
1702
1703 //d.GeomGetAABB(contactGeom.g2, out aabb2);
1704 //d.GeomGetAABB(contactGeom.g1, out aabb1);
1705 //aabb1.
1706 if (((Math.Abs(contactGeom.normal.X - contact.normal.X) < 1.026f) && (Math.Abs(contactGeom.normal.Y - contact.normal.Y) < 0.303f) && (Math.Abs(contactGeom.normal.Z - contact.normal.Z) < 0.065f)))
1707 {
1708 if (contactGeom.normal.X == contact.normal.X && contactGeom.normal.Y == contact.normal.Y && contactGeom.normal.Z == contact.normal.Z)
1709 {
1710 if (Math.Abs(contact.depth - contactGeom.depth) < 0.272f)
1711 {
1712 result = true;
1713 break;
1714 }
1715 }
1716 //m_log.DebugFormat("[Collision]: Depth {0}", Math.Abs(contact.depth - contactGeom.depth));
1717 //m_log.DebugFormat("[Collision]: <{0},{1},{2}>", Math.Abs(contactGeom.normal.X - contact.normal.X), Math.Abs(contactGeom.normal.Y - contact.normal.Y), Math.Abs(contactGeom.normal.Z - contact.normal.Z));
1718 }
1719 }
1720 }
1721
1722 return result;
1723 }
1724
1725 private void collision_accounting_events(PhysicsActor p1, PhysicsActor p2, ContactPoint contact)
1726 {
1727 // obj1LocalID = 0;
1728 //returncollisions = false;
1729 obj2LocalID = 0;
1730 //ctype = 0;
1731 //cStartStop = 0;
1732 if (!p2.SubscribedEvents() && !p1.SubscribedEvents())
1733 return;
1734
1735 switch ((ActorTypes)p2.PhysicsActorType)
1736 {
1737 case ActorTypes.Agent:
1738 cc2 = (OdeCharacter)p2;
1739
1740 // obj1LocalID = cc2.m_localID;
1741 switch ((ActorTypes)p1.PhysicsActorType)
1742 {
1743 case ActorTypes.Agent:
1744 cc1 = (OdeCharacter)p1;
1745 obj2LocalID = cc1.LocalID;
1746 cc1.AddCollisionEvent(cc2.LocalID, contact);
1747 //ctype = (int)CollisionCategories.Character;
1748
1749 //if (cc1.CollidingObj)
1750 //cStartStop = (int)StatusIndicators.Generic;
1751 //else
1752 //cStartStop = (int)StatusIndicators.Start;
1753
1754 //returncollisions = true;
1755 break;
1756
1757 case ActorTypes.Prim:
1758 if (p1 is OdePrim)
1759 {
1760 cp1 = (OdePrim) p1;
1761 obj2LocalID = cp1.LocalID;
1762 cp1.AddCollisionEvent(cc2.LocalID, contact);
1763 }
1764 //ctype = (int)CollisionCategories.Geom;
1765
1766 //if (cp1.CollidingObj)
1767 //cStartStop = (int)StatusIndicators.Generic;
1768 //else
1769 //cStartStop = (int)StatusIndicators.Start;
1770
1771 //returncollisions = true;
1772 break;
1773
1774 case ActorTypes.Ground:
1775 case ActorTypes.Unknown:
1776 obj2LocalID = 0;
1777 //ctype = (int)CollisionCategories.Land;
1778 //returncollisions = true;
1779 break;
1780 }
1781
1782 cc2.AddCollisionEvent(obj2LocalID, contact);
1783 break;
1784
1785 case ActorTypes.Prim:
1786
1787 if (p2 is OdePrim)
1788 {
1789 cp2 = (OdePrim) p2;
1790
1791 // obj1LocalID = cp2.m_localID;
1792 switch ((ActorTypes) p1.PhysicsActorType)
1793 {
1794 case ActorTypes.Agent:
1795 if (p1 is OdeCharacter)
1796 {
1797 cc1 = (OdeCharacter) p1;
1798 obj2LocalID = cc1.LocalID;
1799 cc1.AddCollisionEvent(cp2.LocalID, contact);
1800 //ctype = (int)CollisionCategories.Character;
1801
1802 //if (cc1.CollidingObj)
1803 //cStartStop = (int)StatusIndicators.Generic;
1804 //else
1805 //cStartStop = (int)StatusIndicators.Start;
1806 //returncollisions = true;
1807 }
1808 break;
1809 case ActorTypes.Prim:
1810
1811 if (p1 is OdePrim)
1812 {
1813 cp1 = (OdePrim) p1;
1814 obj2LocalID = cp1.LocalID;
1815 cp1.AddCollisionEvent(cp2.LocalID, contact);
1816 //ctype = (int)CollisionCategories.Geom;
1817
1818 //if (cp1.CollidingObj)
1819 //cStartStop = (int)StatusIndicators.Generic;
1820 //else
1821 //cStartStop = (int)StatusIndicators.Start;
1822
1823 //returncollisions = true;
1824 }
1825 break;
1826
1827 case ActorTypes.Ground:
1828 case ActorTypes.Unknown:
1829 obj2LocalID = 0;
1830 //ctype = (int)CollisionCategories.Land;
1831
1832 //returncollisions = true;
1833 break;
1834 }
1835
1836 cp2.AddCollisionEvent(obj2LocalID, contact);
1837 }
1838 break;
1839 }
1840 //if (returncollisions)
1841 //{
1842
1843 //lock (m_storedCollisions)
1844 //{
1845 //cDictKey = obj1LocalID.ToString() + obj2LocalID.ToString() + cStartStop.ToString() + ctype.ToString();
1846 //if (m_storedCollisions.ContainsKey(cDictKey))
1847 //{
1848 //sCollisionData objd = m_storedCollisions[cDictKey];
1849 //objd.NumberOfCollisions += 1;
1850 //objd.lastframe = framecount;
1851 //m_storedCollisions[cDictKey] = objd;
1852 //}
1853 //else
1854 //{
1855 //sCollisionData objd = new sCollisionData();
1856 //objd.ColliderLocalId = obj1LocalID;
1857 //objd.CollidedWithLocalId = obj2LocalID;
1858 //objd.CollisionType = ctype;
1859 //objd.NumberOfCollisions = 1;
1860 //objd.lastframe = framecount;
1861 //objd.StatusIndicator = cStartStop;
1862 //m_storedCollisions.Add(cDictKey, objd);
1863 //}
1864 //}
1865 // }
1866 }
1867
1868 private int TriArrayCallback(IntPtr trimesh, IntPtr refObject, int[] triangleIndex, int triCount)
1869 {
1870 /* String name1 = null;
1871 String name2 = null;
1872
1873 if (!geom_name_map.TryGetValue(trimesh, out name1))
1874 {
1875 name1 = "null";
1876 }
1877 if (!geom_name_map.TryGetValue(refObject, out name2))
1878 {
1879 name2 = "null";
1880 }
1881
1882 m_log.InfoFormat("TriArrayCallback: A collision was detected between {1} and {2}", 0, name1, name2);
1883 */
1884 return 1;
1885 }
1886
1887 private int TriCallback(IntPtr trimesh, IntPtr refObject, int triangleIndex)
1888 {
1889// String name1 = null;
1890// String name2 = null;
1891//
1892// if (!geom_name_map.TryGetValue(trimesh, out name1))
1893// {
1894// name1 = "null";
1895// }
1896//
1897// if (!geom_name_map.TryGetValue(refObject, out name2))
1898// {
1899// name2 = "null";
1900// }
1901
1902 // m_log.InfoFormat("TriCallback: A collision was detected between {1} and {2}. Index was {3}", 0, name1, name2, triangleIndex);
1903
1904 d.Vector3 v0 = new d.Vector3();
1905 d.Vector3 v1 = new d.Vector3();
1906 d.Vector3 v2 = new d.Vector3();
1907
1908 d.GeomTriMeshGetTriangle(trimesh, 0, ref v0, ref v1, ref v2);
1909 // m_log.DebugFormat("Triangle {0} is <{1},{2},{3}>, <{4},{5},{6}>, <{7},{8},{9}>", triangleIndex, v0.X, v0.Y, v0.Z, v1.X, v1.Y, v1.Z, v2.X, v2.Y, v2.Z);
1910
1911 return 1;
1912 }
1913
1914 /// <summary>
1915 /// This is our collision testing routine in ODE
1916 /// </summary>
1917 private void collision_optimized()
1918 {
1919 _perloopContact.Clear();
1920
1921 foreach (OdeCharacter chr in _characters)
1922 {
1923 // Reset the collision values to false
1924 // since we don't know if we're colliding yet
1925 if (chr.Shell == IntPtr.Zero || chr.Body == IntPtr.Zero)
1926 continue;
1927
1928 chr.IsColliding = false;
1929 chr.CollidingGround = false;
1930 chr.CollidingObj = false;
1931
1932 // Test the avatar's geometry for collision with the space
1933 // This will return near and the space that they are the closest to
1934 // And we'll run this again against the avatar and the space segment
1935 // This will return with a bunch of possible objects in the space segment
1936 // and we'll run it again on all of them.
1937 try
1938 {
1939 CollideSpaces(space, chr.Shell, IntPtr.Zero);
1940 }
1941 catch (AccessViolationException)
1942 {
1943 m_log.ErrorFormat("[ODE SCENE]: Unable to space collide {0}", PhysicsSceneName);
1944 }
1945
1946 //float terrainheight = GetTerrainHeightAtXY(chr.Position.X, chr.Position.Y);
1947 //if (chr.Position.Z + (chr.Velocity.Z * timeStep) < terrainheight + 10)
1948 //{
1949 //chr.Position.Z = terrainheight + 10.0f;
1950 //forcedZ = true;
1951 //}
1952 }
1953
1954 if (CollectStats)
1955 {
1956 m_tempAvatarCollisionsThisFrame = _perloopContact.Count;
1957 m_stats[ODEAvatarContactsStatsName] += m_tempAvatarCollisionsThisFrame;
1958 }
1959
1960 List<OdePrim> removeprims = null;
1961 foreach (OdePrim chr in _activeprims)
1962 {
1963 if (chr.Body != IntPtr.Zero && d.BodyIsEnabled(chr.Body) && (!chr.m_disabled))
1964 {
1965 try
1966 {
1967 lock (chr)
1968 {
1969 if (space != IntPtr.Zero && chr.prim_geom != IntPtr.Zero && chr.m_taintremove == false)
1970 {
1971 CollideSpaces(space, chr.prim_geom, IntPtr.Zero);
1972 }
1973 else
1974 {
1975 if (removeprims == null)
1976 {
1977 removeprims = new List<OdePrim>();
1978 }
1979 removeprims.Add(chr);
1980 m_log.Error(
1981 "[ODE SCENE]: unable to collide test active prim against space. The space was zero, the geom was zero or it was in the process of being removed. Removed it from the active prim list. This needs to be fixed!");
1982 }
1983 }
1984 }
1985 catch (AccessViolationException)
1986 {
1987 m_log.Error("[ODE SCENE]: Unable to space collide");
1988 }
1989 }
1990 }
1991
1992 if (CollectStats)
1993 m_stats[ODEPrimContactsStatName] += _perloopContact.Count - m_tempAvatarCollisionsThisFrame;
1994
1995 if (removeprims != null)
1996 {
1997 foreach (OdePrim chr in removeprims)
1998 {
1999 _activeprims.Remove(chr);
2000 }
2001 }
2002 }
2003
2004 #endregion
2005
2006 public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
2007 {
2008 if (!m_suportCombine)
2009 return;
2010 m_worldOffset = offset;
2011 WorldExtents = new Vector2(extents.X, extents.Y);
2012 m_parentScene = pScene;
2013 }
2014
2015 // Recovered for use by fly height. Kitto Flora
2016 internal float GetTerrainHeightAtXY(float x, float y)
2017 {
2018 IntPtr heightFieldGeom = IntPtr.Zero;
2019 int offsetX = 0;
2020 int offsetY = 0;
2021
2022 if (m_suportCombine)
2023 {
2024 offsetX = ((int)(x / (int)Constants.RegionSize)) * (int)Constants.RegionSize;
2025 offsetY = ((int)(y / (int)Constants.RegionSize)) * (int)Constants.RegionSize;
2026 }
2027
2028 if(RegionTerrain.TryGetValue(new Vector3(offsetX,offsetY,0), out heightFieldGeom))
2029 {
2030 if (heightFieldGeom != IntPtr.Zero)
2031 {
2032 if (TerrainHeightFieldHeights.ContainsKey(heightFieldGeom))
2033 {
2034
2035 int index;
2036
2037
2038 if ((int)x > WorldExtents.X || (int)y > WorldExtents.Y ||
2039 (int)x < 0.001f || (int)y < 0.001f)
2040 return 0;
2041
2042 x = x - offsetX + 1f;
2043 y = y - offsetY + 1f;
2044
2045 index = (int)((int)x * ((int)m_regionHeight +3) + (int)y);
2046
2047 if (index < TerrainHeightFieldHeights[heightFieldGeom].Length)
2048 {
2049 //m_log.DebugFormat("x{0} y{1} = {2}", x, y, (float)TerrainHeightFieldHeights[heightFieldGeom][index]);
2050 return (float)TerrainHeightFieldHeights[heightFieldGeom][index];
2051 }
2052
2053 else
2054 return 0f;
2055 }
2056 else
2057 {
2058 return 0f;
2059 }
2060
2061 }
2062 else
2063 {
2064 return 0f;
2065 }
2066
2067 }
2068 else
2069 {
2070 return 0f;
2071 }
2072 }
2073// End recovered. Kitto Flora
2074
2075 /// <summary>
2076 /// Add actor to the list that should receive collision events in the simulate loop.
2077 /// </summary>
2078 /// <param name="obj"></param>
2079 internal void AddCollisionEventReporting(PhysicsActor obj)
2080 {
2081// m_log.DebugFormat("[PHYSICS]: Adding {0} {1} to collision event reporting", obj.SOPName, obj.LocalID);
2082
2083 lock (m_collisionEventActorsChanges)
2084 m_collisionEventActorsChanges[obj.LocalID] = obj;
2085 }
2086
2087 /// <summary>
2088 /// Remove actor from the list that should receive collision events in the simulate loop.
2089 /// </summary>
2090 /// <param name="obj"></param>
2091 internal void RemoveCollisionEventReporting(PhysicsActor obj)
2092 {
2093// m_log.DebugFormat("[PHYSICS]: Removing {0} {1} from collision event reporting", obj.SOPName, obj.LocalID);
2094
2095 lock (m_collisionEventActorsChanges)
2096 m_collisionEventActorsChanges[obj.LocalID] = null;
2097 }
2098
2099 #region Add/Remove Entities
2100
2101 public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 velocity, Vector3 size, bool isFlying)
2102 {
2103 OdeCharacter newAv
2104 = new OdeCharacter(
2105 avName, this, position, velocity, size, avPIDD, avPIDP,
2106 avCapRadius, avStandupTensor, avDensity,
2107 avMovementDivisorWalk, avMovementDivisorRun);
2108
2109 newAv.Flying = isFlying;
2110 newAv.MinimumGroundFlightOffset = minimumGroundFlightOffset;
2111 newAv.m_avatarplanted = avplanted;
2112
2113 return newAv;
2114 }
2115
2116 public override void RemoveAvatar(PhysicsActor actor)
2117 {
2118// m_log.DebugFormat(
2119// "[ODE SCENE]: Removing physics character {0} {1} from physics scene {2}",
2120// actor.Name, actor.LocalID, Name);
2121
2122 ((OdeCharacter) actor).Destroy();
2123 }
2124
2125 internal void AddCharacter(OdeCharacter chr)
2126 {
2127 chr.m_avatarplanted = avplanted;
2128 if (!_characters.Contains(chr))
2129 {
2130 _characters.Add(chr);
2131
2132// m_log.DebugFormat(
2133// "[ODE SCENE]: Adding physics character {0} {1} to physics scene {2}. Count now {3}",
2134// chr.Name, chr.LocalID, Name, _characters.Count);
2135
2136 if (chr.bad)
2137 m_log.ErrorFormat("[ODE SCENE]: Added BAD actor {0} to characters list", chr.m_uuid);
2138 }
2139 else
2140 {
2141 m_log.ErrorFormat(
2142 "[ODE SCENE]: Tried to add character {0} {1} but they are already in the set!",
2143 chr.Name, chr.LocalID);
2144 }
2145 }
2146
2147 internal void RemoveCharacter(OdeCharacter chr)
2148 {
2149 if (_characters.Contains(chr))
2150 {
2151 _characters.Remove(chr);
2152
2153// m_log.DebugFormat(
2154// "[ODE SCENE]: Removing physics character {0} {1} from physics scene {2}. Count now {3}",
2155// chr.Name, chr.LocalID, Name, _characters.Count);
2156 }
2157 else
2158 {
2159 m_log.ErrorFormat(
2160 "[ODE SCENE]: Tried to remove character {0} {1} but they are not in the list!",
2161 chr.Name, chr.LocalID);
2162 }
2163 }
2164
2165 private PhysicsActor AddPrim(String name, Vector3 position, Vector3 size, Quaternion rotation,
2166 PrimitiveBaseShape pbs, bool isphysical, uint localID)
2167 {
2168 Vector3 pos = position;
2169 Vector3 siz = size;
2170 Quaternion rot = rotation;
2171
2172 OdePrim newPrim;
2173 lock (OdeLock)
2174 {
2175 newPrim = new OdePrim(name, this, pos, siz, rot, pbs, isphysical);
2176
2177 lock (_prims)
2178 _prims.Add(newPrim);
2179 }
2180 newPrim.LocalID = localID;
2181 return newPrim;
2182 }
2183
2184 /// <summary>
2185 /// Make this prim subject to physics.
2186 /// </summary>
2187 /// <param name="prim"></param>
2188 internal void ActivatePrim(OdePrim prim)
2189 {
2190 // adds active prim.. (ones that should be iterated over in collisions_optimized
2191 if (!_activeprims.Contains(prim))
2192 _activeprims.Add(prim);
2193 //else
2194 // m_log.Warn("[PHYSICS]: Double Entry in _activeprims detected, potential crash immenent");
2195 }
2196
2197 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
2198 Vector3 size, Quaternion rotation, bool isPhysical, uint localid)
2199 {
2200// m_log.DebugFormat("[ODE SCENE]: Adding physics prim {0} {1} to physics scene {2}", primName, localid, Name);
2201
2202 return AddPrim(primName, position, size, rotation, pbs, isPhysical, localid);
2203 }
2204
2205 public override float TimeDilation
2206 {
2207 get { return m_timeDilation; }
2208 }
2209
2210 public override bool SupportsNINJAJoints
2211 {
2212 get { return m_NINJA_physics_joints_enabled; }
2213 }
2214
2215 // internal utility function: must be called within a lock (OdeLock)
2216 private void InternalAddActiveJoint(PhysicsJoint joint)
2217 {
2218 activeJoints.Add(joint);
2219 SOPName_to_activeJoint.Add(joint.ObjectNameInScene, joint);
2220 }
2221
2222 // internal utility function: must be called within a lock (OdeLock)
2223 private void InternalAddPendingJoint(OdePhysicsJoint joint)
2224 {
2225 pendingJoints.Add(joint);
2226 SOPName_to_pendingJoint.Add(joint.ObjectNameInScene, joint);
2227 }
2228
2229 // internal utility function: must be called within a lock (OdeLock)
2230 private void InternalRemovePendingJoint(PhysicsJoint joint)
2231 {
2232 pendingJoints.Remove(joint);
2233 SOPName_to_pendingJoint.Remove(joint.ObjectNameInScene);
2234 }
2235
2236 // internal utility function: must be called within a lock (OdeLock)
2237 private void InternalRemoveActiveJoint(PhysicsJoint joint)
2238 {
2239 activeJoints.Remove(joint);
2240 SOPName_to_activeJoint.Remove(joint.ObjectNameInScene);
2241 }
2242
2243 public override void DumpJointInfo()
2244 {
2245 string hdr = "[NINJA] JOINTINFO: ";
2246 foreach (PhysicsJoint j in pendingJoints)
2247 {
2248 m_log.Debug(hdr + " pending joint, Name: " + j.ObjectNameInScene + " raw parms:" + j.RawParams);
2249 }
2250 m_log.Debug(hdr + pendingJoints.Count + " total pending joints");
2251 foreach (string jointName in SOPName_to_pendingJoint.Keys)
2252 {
2253 m_log.Debug(hdr + " pending joints dict contains Name: " + jointName);
2254 }
2255 m_log.Debug(hdr + SOPName_to_pendingJoint.Keys.Count + " total pending joints dict entries");
2256 foreach (PhysicsJoint j in activeJoints)
2257 {
2258 m_log.Debug(hdr + " active joint, Name: " + j.ObjectNameInScene + " raw parms:" + j.RawParams);
2259 }
2260 m_log.Debug(hdr + activeJoints.Count + " total active joints");
2261 foreach (string jointName in SOPName_to_activeJoint.Keys)
2262 {
2263 m_log.Debug(hdr + " active joints dict contains Name: " + jointName);
2264 }
2265 m_log.Debug(hdr + SOPName_to_activeJoint.Keys.Count + " total active joints dict entries");
2266
2267 m_log.Debug(hdr + " Per-body joint connectivity information follows.");
2268 m_log.Debug(hdr + joints_connecting_actor.Keys.Count + " bodies are connected by joints.");
2269 foreach (string actorName in joints_connecting_actor.Keys)
2270 {
2271 m_log.Debug(hdr + " Actor " + actorName + " has the following joints connecting it");
2272 foreach (PhysicsJoint j in joints_connecting_actor[actorName])
2273 {
2274 m_log.Debug(hdr + " * joint Name: " + j.ObjectNameInScene + " raw parms:" + j.RawParams);
2275 }
2276 m_log.Debug(hdr + joints_connecting_actor[actorName].Count + " connecting joints total for this actor");
2277 }
2278 }
2279
2280 public override void RequestJointDeletion(string ObjectNameInScene)
2281 {
2282 lock (externalJointRequestsLock)
2283 {
2284 if (!requestedJointsToBeDeleted.Contains(ObjectNameInScene)) // forbid same deletion request from entering twice to prevent spurious deletions processed asynchronously
2285 {
2286 requestedJointsToBeDeleted.Add(ObjectNameInScene);
2287 }
2288 }
2289 }
2290
2291 private void DeleteRequestedJoints()
2292 {
2293 List<string> myRequestedJointsToBeDeleted;
2294 lock (externalJointRequestsLock)
2295 {
2296 // make a local copy of the shared list for processing (threading issues)
2297 myRequestedJointsToBeDeleted = new List<string>(requestedJointsToBeDeleted);
2298 }
2299
2300 foreach (string jointName in myRequestedJointsToBeDeleted)
2301 {
2302 lock (OdeLock)
2303 {
2304 //m_log.Debug("[NINJA] trying to deleting requested joint " + jointName);
2305 if (SOPName_to_activeJoint.ContainsKey(jointName) || SOPName_to_pendingJoint.ContainsKey(jointName))
2306 {
2307 OdePhysicsJoint joint = null;
2308 if (SOPName_to_activeJoint.ContainsKey(jointName))
2309 {
2310 joint = SOPName_to_activeJoint[jointName] as OdePhysicsJoint;
2311 InternalRemoveActiveJoint(joint);
2312 }
2313 else if (SOPName_to_pendingJoint.ContainsKey(jointName))
2314 {
2315 joint = SOPName_to_pendingJoint[jointName] as OdePhysicsJoint;
2316 InternalRemovePendingJoint(joint);
2317 }
2318
2319 if (joint != null)
2320 {
2321 //m_log.Debug("joint.BodyNames.Count is " + joint.BodyNames.Count + " and contents " + joint.BodyNames);
2322 for (int iBodyName = 0; iBodyName < 2; iBodyName++)
2323 {
2324 string bodyName = joint.BodyNames[iBodyName];
2325 if (bodyName != "NULL")
2326 {
2327 joints_connecting_actor[bodyName].Remove(joint);
2328 if (joints_connecting_actor[bodyName].Count == 0)
2329 {
2330 joints_connecting_actor.Remove(bodyName);
2331 }
2332 }
2333 }
2334
2335 DoJointDeactivated(joint);
2336 if (joint.jointID != IntPtr.Zero)
2337 {
2338 d.JointDestroy(joint.jointID);
2339 joint.jointID = IntPtr.Zero;
2340 //DoJointErrorMessage(joint, "successfully destroyed joint " + jointName);
2341 }
2342 else
2343 {
2344 //m_log.Warn("[NINJA] Ignoring re-request to destroy joint " + jointName);
2345 }
2346 }
2347 else
2348 {
2349 // DoJointErrorMessage(joint, "coult not find joint to destroy based on name " + jointName);
2350 }
2351 }
2352 else
2353 {
2354 // DoJointErrorMessage(joint, "WARNING - joint removal failed, joint " + jointName);
2355 }
2356 }
2357 }
2358
2359 // remove processed joints from the shared list
2360 lock (externalJointRequestsLock)
2361 {
2362 foreach (string jointName in myRequestedJointsToBeDeleted)
2363 {
2364 requestedJointsToBeDeleted.Remove(jointName);
2365 }
2366 }
2367 }
2368
2369 // for pending joints we don't know if their associated bodies exist yet or not.
2370 // the joint is actually created during processing of the taints
2371 private void CreateRequestedJoints()
2372 {
2373 List<PhysicsJoint> myRequestedJointsToBeCreated;
2374 lock (externalJointRequestsLock)
2375 {
2376 // make a local copy of the shared list for processing (threading issues)
2377 myRequestedJointsToBeCreated = new List<PhysicsJoint>(requestedJointsToBeCreated);
2378 }
2379
2380 foreach (PhysicsJoint joint in myRequestedJointsToBeCreated)
2381 {
2382 lock (OdeLock)
2383 {
2384 if (SOPName_to_pendingJoint.ContainsKey(joint.ObjectNameInScene) && SOPName_to_pendingJoint[joint.ObjectNameInScene] != null)
2385 {
2386 DoJointErrorMessage(joint, "WARNING: ignoring request to re-add already pending joint Name:" + joint.ObjectNameInScene + " type:" + joint.Type + " parms: " + joint.RawParams + " pos: " + joint.Position + " rot:" + joint.Rotation);
2387 continue;
2388 }
2389 if (SOPName_to_activeJoint.ContainsKey(joint.ObjectNameInScene) && SOPName_to_activeJoint[joint.ObjectNameInScene] != null)
2390 {
2391 DoJointErrorMessage(joint, "WARNING: ignoring request to re-add already active joint Name:" + joint.ObjectNameInScene + " type:" + joint.Type + " parms: " + joint.RawParams + " pos: " + joint.Position + " rot:" + joint.Rotation);
2392 continue;
2393 }
2394
2395 InternalAddPendingJoint(joint as OdePhysicsJoint);
2396
2397 if (joint.BodyNames.Count >= 2)
2398 {
2399 for (int iBodyName = 0; iBodyName < 2; iBodyName++)
2400 {
2401 string bodyName = joint.BodyNames[iBodyName];
2402 if (bodyName != "NULL")
2403 {
2404 if (!joints_connecting_actor.ContainsKey(bodyName))
2405 {
2406 joints_connecting_actor.Add(bodyName, new List<PhysicsJoint>());
2407 }
2408 joints_connecting_actor[bodyName].Add(joint);
2409 }
2410 }
2411 }
2412 }
2413 }
2414
2415 // remove processed joints from shared list
2416 lock (externalJointRequestsLock)
2417 {
2418 foreach (PhysicsJoint joint in myRequestedJointsToBeCreated)
2419 {
2420 requestedJointsToBeCreated.Remove(joint);
2421 }
2422 }
2423 }
2424
2425 /// <summary>
2426 /// Add a request for joint creation.
2427 /// </summary>
2428 /// <remarks>
2429 /// this joint will just be added to a waiting list that is NOT processed during the main
2430 /// Simulate() loop (to avoid deadlocks). After Simulate() is finished, we handle unprocessed joint requests.
2431 /// </remarks>
2432 /// <param name="objectNameInScene"></param>
2433 /// <param name="jointType"></param>
2434 /// <param name="position"></param>
2435 /// <param name="rotation"></param>
2436 /// <param name="parms"></param>
2437 /// <param name="bodyNames"></param>
2438 /// <param name="trackedBodyName"></param>
2439 /// <param name="localRotation"></param>
2440 /// <returns></returns>
2441 public override PhysicsJoint RequestJointCreation(
2442 string objectNameInScene, PhysicsJointType jointType, Vector3 position,
2443 Quaternion rotation, string parms, List<string> bodyNames, string trackedBodyName, Quaternion localRotation)
2444 {
2445 OdePhysicsJoint joint = new OdePhysicsJoint();
2446 joint.ObjectNameInScene = objectNameInScene;
2447 joint.Type = jointType;
2448 joint.Position = position;
2449 joint.Rotation = rotation;
2450 joint.RawParams = parms;
2451 joint.BodyNames = new List<string>(bodyNames);
2452 joint.TrackedBodyName = trackedBodyName;
2453 joint.LocalRotation = localRotation;
2454 joint.jointID = IntPtr.Zero;
2455 joint.ErrorMessageCount = 0;
2456
2457 lock (externalJointRequestsLock)
2458 {
2459 if (!requestedJointsToBeCreated.Contains(joint)) // forbid same creation request from entering twice
2460 {
2461 requestedJointsToBeCreated.Add(joint);
2462 }
2463 }
2464
2465 return joint;
2466 }
2467
2468 private void RemoveAllJointsConnectedToActor(PhysicsActor actor)
2469 {
2470 //m_log.Debug("RemoveAllJointsConnectedToActor: start");
2471 if (actor.SOPName != null && joints_connecting_actor.ContainsKey(actor.SOPName) && joints_connecting_actor[actor.SOPName] != null)
2472 {
2473 List<PhysicsJoint> jointsToRemove = new List<PhysicsJoint>();
2474 //TODO: merge these 2 loops (originally it was needed to avoid altering a list being iterated over, but it is no longer needed due to the joint request queue mechanism)
2475 foreach (PhysicsJoint j in joints_connecting_actor[actor.SOPName])
2476 {
2477 jointsToRemove.Add(j);
2478 }
2479 foreach (PhysicsJoint j in jointsToRemove)
2480 {
2481 //m_log.Debug("RemoveAllJointsConnectedToActor: about to request deletion of " + j.ObjectNameInScene);
2482 RequestJointDeletion(j.ObjectNameInScene);
2483 //m_log.Debug("RemoveAllJointsConnectedToActor: done request deletion of " + j.ObjectNameInScene);
2484 j.TrackedBodyName = null; // *IMMEDIATELY* prevent any further movement of this joint (else a deleted actor might cause spurious tracking motion of the joint for a few frames, leading to the joint proxy object disappearing)
2485 }
2486 }
2487 }
2488
2489 public override void RemoveAllJointsConnectedToActorThreadLocked(PhysicsActor actor)
2490 {
2491 //m_log.Debug("RemoveAllJointsConnectedToActorThreadLocked: start");
2492 lock (OdeLock)
2493 {
2494 //m_log.Debug("RemoveAllJointsConnectedToActorThreadLocked: got lock");
2495 RemoveAllJointsConnectedToActor(actor);
2496 }
2497 }
2498
2499 // normally called from within OnJointMoved, which is called from within a lock (OdeLock)
2500 public override Vector3 GetJointAnchor(PhysicsJoint joint)
2501 {
2502 Debug.Assert(joint.IsInPhysicsEngine);
2503 d.Vector3 pos = new d.Vector3();
2504
2505 if (!(joint is OdePhysicsJoint))
2506 {
2507 DoJointErrorMessage(joint, "warning: non-ODE joint requesting anchor: " + joint.ObjectNameInScene);
2508 }
2509 else
2510 {
2511 OdePhysicsJoint odeJoint = (OdePhysicsJoint)joint;
2512 switch (odeJoint.Type)
2513 {
2514 case PhysicsJointType.Ball:
2515 d.JointGetBallAnchor(odeJoint.jointID, out pos);
2516 break;
2517 case PhysicsJointType.Hinge:
2518 d.JointGetHingeAnchor(odeJoint.jointID, out pos);
2519 break;
2520 }
2521 }
2522 return new Vector3(pos.X, pos.Y, pos.Z);
2523 }
2524
2525 /// <summary>
2526 /// Get joint axis.
2527 /// </summary>
2528 /// <remarks>
2529 /// normally called from within OnJointMoved, which is called from within a lock (OdeLock)
2530 /// WARNING: ODE sometimes returns <0,0,0> as the joint axis! Therefore this function
2531 /// appears to be unreliable. Fortunately we can compute the joint axis ourselves by
2532 /// keeping track of the joint's original orientation relative to one of the involved bodies.
2533 /// </remarks>
2534 /// <param name="joint"></param>
2535 /// <returns></returns>
2536 public override Vector3 GetJointAxis(PhysicsJoint joint)
2537 {
2538 Debug.Assert(joint.IsInPhysicsEngine);
2539 d.Vector3 axis = new d.Vector3();
2540
2541 if (!(joint is OdePhysicsJoint))
2542 {
2543 DoJointErrorMessage(joint, "warning: non-ODE joint requesting anchor: " + joint.ObjectNameInScene);
2544 }
2545 else
2546 {
2547 OdePhysicsJoint odeJoint = (OdePhysicsJoint)joint;
2548 switch (odeJoint.Type)
2549 {
2550 case PhysicsJointType.Ball:
2551 DoJointErrorMessage(joint, "warning - axis requested for ball joint: " + joint.ObjectNameInScene);
2552 break;
2553 case PhysicsJointType.Hinge:
2554 d.JointGetHingeAxis(odeJoint.jointID, out axis);
2555 break;
2556 }
2557 }
2558 return new Vector3(axis.X, axis.Y, axis.Z);
2559 }
2560
2561 /// <summary>
2562 /// Stop this prim being subject to physics
2563 /// </summary>
2564 /// <param name="prim"></param>
2565 internal void DeactivatePrim(OdePrim prim)
2566 {
2567 _activeprims.Remove(prim);
2568 }
2569
2570 public override void RemovePrim(PhysicsActor prim)
2571 {
2572 // As with all ODE physics operations, we don't remove the prim immediately but signal that it should be
2573 // removed in the next physics simulate pass.
2574 if (prim is OdePrim)
2575 {
2576 lock (OdeLock)
2577 {
2578 OdePrim p = (OdePrim) prim;
2579
2580 p.setPrimForRemoval();
2581 AddPhysicsActorTaint(prim);
2582 }
2583 }
2584 }
2585
2586 /// <summary>
2587 /// This is called from within simulate but outside the locked portion
2588 /// We need to do our own locking here
2589 /// (Note: As of 20110801 this no longer appears to be true - this is being called within lock (odeLock) in
2590 /// Simulate() -- justincc).
2591 ///
2592 /// Essentially, we need to remove the prim from our space segment, whatever segment it's in.
2593 ///
2594 /// If there are no more prim in the segment, we need to empty (spacedestroy)the segment and reclaim memory
2595 /// that the space was using.
2596 /// </summary>
2597 /// <param name="prim"></param>
2598 internal void RemovePrimThreadLocked(OdePrim prim)
2599 {
2600// m_log.DebugFormat("[ODE SCENE]: Removing physical prim {0} {1}", prim.Name, prim.LocalID);
2601
2602 lock (prim)
2603 {
2604 RemoveCollisionEventReporting(prim);
2605
2606 if (prim.prim_geom != IntPtr.Zero)
2607 {
2608 prim.ResetTaints();
2609
2610 if (prim.IsPhysical)
2611 {
2612 prim.disableBody();
2613 if (prim.childPrim)
2614 {
2615 prim.childPrim = false;
2616 prim.Body = IntPtr.Zero;
2617 prim.m_disabled = true;
2618 prim.IsPhysical = false;
2619 }
2620
2621
2622 }
2623 // we don't want to remove the main space
2624
2625 // If the geometry is in the targetspace, remove it from the target space
2626 //m_log.Warn(prim.m_targetSpace);
2627
2628 //if (prim.m_targetSpace != IntPtr.Zero)
2629 //{
2630 //if (d.SpaceQuery(prim.m_targetSpace, prim.prim_geom))
2631 //{
2632
2633 //if (d.GeomIsSpace(prim.m_targetSpace))
2634 //{
2635 //waitForSpaceUnlock(prim.m_targetSpace);
2636 //d.SpaceRemove(prim.m_targetSpace, prim.prim_geom);
2637 prim.m_targetSpace = IntPtr.Zero;
2638 //}
2639 //else
2640 //{
2641 // m_log.Info("[Physics]: Invalid Scene passed to 'removeprim from scene':" +
2642 //((OdePrim)prim).m_targetSpace.ToString());
2643 //}
2644
2645 //}
2646 //}
2647 //m_log.Warn(prim.prim_geom);
2648
2649 if (!prim.RemoveGeom())
2650 m_log.Warn("[ODE SCENE]: Unable to remove prim from physics scene");
2651
2652 lock (_prims)
2653 _prims.Remove(prim);
2654
2655 //If there are no more geometries in the sub-space, we don't need it in the main space anymore
2656 //if (d.SpaceGetNumGeoms(prim.m_targetSpace) == 0)
2657 //{
2658 //if (prim.m_targetSpace != null)
2659 //{
2660 //if (d.GeomIsSpace(prim.m_targetSpace))
2661 //{
2662 //waitForSpaceUnlock(prim.m_targetSpace);
2663 //d.SpaceRemove(space, prim.m_targetSpace);
2664 // free up memory used by the space.
2665 //d.SpaceDestroy(prim.m_targetSpace);
2666 //int[] xyspace = calculateSpaceArrayItemFromPos(prim.Position);
2667 //resetSpaceArrayItemToZero(xyspace[0], xyspace[1]);
2668 //}
2669 //else
2670 //{
2671 //m_log.Info("[Physics]: Invalid Scene passed to 'removeprim from scene':" +
2672 //((OdePrim) prim).m_targetSpace.ToString());
2673 //}
2674 //}
2675 //}
2676
2677 if (SupportsNINJAJoints)
2678 RemoveAllJointsConnectedToActorThreadLocked(prim);
2679 }
2680 }
2681 }
2682
2683 #endregion
2684
2685 #region Space Separation Calculation
2686
2687 /// <summary>
2688 /// Takes a space pointer and zeros out the array we're using to hold the spaces
2689 /// </summary>
2690 /// <param name="pSpace"></param>
2691 private void resetSpaceArrayItemToZero(IntPtr pSpace)
2692 {
2693 for (int x = 0; x < staticPrimspace.GetLength(0); x++)
2694 {
2695 for (int y = 0; y < staticPrimspace.GetLength(1); y++)
2696 {
2697 if (staticPrimspace[x, y] == pSpace)
2698 staticPrimspace[x, y] = IntPtr.Zero;
2699 }
2700 }
2701 }
2702
2703// private void resetSpaceArrayItemToZero(int arrayitemX, int arrayitemY)
2704// {
2705// staticPrimspace[arrayitemX, arrayitemY] = IntPtr.Zero;
2706// }
2707
2708 /// <summary>
2709 /// Called when a static prim moves. Allocates a space for the prim based on its position
2710 /// </summary>
2711 /// <param name="geom">the pointer to the geom that moved</param>
2712 /// <param name="pos">the position that the geom moved to</param>
2713 /// <param name="currentspace">a pointer to the space it was in before it was moved.</param>
2714 /// <returns>a pointer to the new space it's in</returns>
2715 internal IntPtr recalculateSpaceForGeom(IntPtr geom, Vector3 pos, IntPtr currentspace)
2716 {
2717 // Called from setting the Position and Size of an ODEPrim so
2718 // it's already in locked space.
2719
2720 // we don't want to remove the main space
2721 // we don't need to test physical here because this function should
2722 // never be called if the prim is physical(active)
2723
2724 // All physical prim end up in the root space
2725 //Thread.Sleep(20);
2726 if (currentspace != space)
2727 {
2728 //m_log.Info("[SPACE]: C:" + currentspace.ToString() + " g:" + geom.ToString());
2729 //if (currentspace == IntPtr.Zero)
2730 //{
2731 //int adfadf = 0;
2732 //}
2733 if (d.SpaceQuery(currentspace, geom) && currentspace != IntPtr.Zero)
2734 {
2735 if (d.GeomIsSpace(currentspace))
2736 {
2737// waitForSpaceUnlock(currentspace);
2738 d.SpaceRemove(currentspace, geom);
2739 }
2740 else
2741 {
2742 m_log.Info("[ODE SCENE]: Invalid Scene passed to 'recalculatespace':" + currentspace +
2743 " Geom:" + geom);
2744 }
2745 }
2746 else
2747 {
2748 IntPtr sGeomIsIn = d.GeomGetSpace(geom);
2749 if (sGeomIsIn != IntPtr.Zero)
2750 {
2751 if (d.GeomIsSpace(currentspace))
2752 {
2753// waitForSpaceUnlock(sGeomIsIn);
2754 d.SpaceRemove(sGeomIsIn, geom);
2755 }
2756 else
2757 {
2758 m_log.Info("[ODE SCENE]: Invalid Scene passed to 'recalculatespace':" +
2759 sGeomIsIn + " Geom:" + geom);
2760 }
2761 }
2762 }
2763
2764 //If there are no more geometries in the sub-space, we don't need it in the main space anymore
2765 if (d.SpaceGetNumGeoms(currentspace) == 0)
2766 {
2767 if (currentspace != IntPtr.Zero)
2768 {
2769 if (d.GeomIsSpace(currentspace))
2770 {
2771// waitForSpaceUnlock(currentspace);
2772// waitForSpaceUnlock(space);
2773 d.SpaceRemove(space, currentspace);
2774 // free up memory used by the space.
2775
2776 //d.SpaceDestroy(currentspace);
2777 resetSpaceArrayItemToZero(currentspace);
2778 }
2779 else
2780 {
2781 m_log.Info("[ODE SCENE]: Invalid Scene passed to 'recalculatespace':" +
2782 currentspace + " Geom:" + geom);
2783 }
2784 }
2785 }
2786 }
2787 else
2788 {
2789 // this is a physical object that got disabled. ;.;
2790 if (currentspace != IntPtr.Zero && geom != IntPtr.Zero)
2791 {
2792 if (d.SpaceQuery(currentspace, geom))
2793 {
2794 if (d.GeomIsSpace(currentspace))
2795 {
2796// waitForSpaceUnlock(currentspace);
2797 d.SpaceRemove(currentspace, geom);
2798 }
2799 else
2800 {
2801 m_log.Info("[ODE SCENE]: Invalid Scene passed to 'recalculatespace':" +
2802 currentspace + " Geom:" + geom);
2803 }
2804 }
2805 else
2806 {
2807 IntPtr sGeomIsIn = d.GeomGetSpace(geom);
2808 if (sGeomIsIn != IntPtr.Zero)
2809 {
2810 if (d.GeomIsSpace(sGeomIsIn))
2811 {
2812// waitForSpaceUnlock(sGeomIsIn);
2813 d.SpaceRemove(sGeomIsIn, geom);
2814 }
2815 else
2816 {
2817 m_log.Info("[ODE SCENE]: Invalid Scene passed to 'recalculatespace':" +
2818 sGeomIsIn + " Geom:" + geom);
2819 }
2820 }
2821 }
2822 }
2823 }
2824
2825 // The routines in the Position and Size sections do the 'inserting' into the space,
2826 // so all we have to do is make sure that the space that we're putting the prim into
2827 // is in the 'main' space.
2828 int[] iprimspaceArrItem = calculateSpaceArrayItemFromPos(pos);
2829 IntPtr newspace = calculateSpaceForGeom(pos);
2830
2831 if (newspace == IntPtr.Zero)
2832 {
2833 newspace = createprimspace(iprimspaceArrItem[0], iprimspaceArrItem[1]);
2834 d.HashSpaceSetLevels(newspace, smallHashspaceLow, smallHashspaceHigh);
2835 }
2836
2837 return newspace;
2838 }
2839
2840 /// <summary>
2841 /// Creates a new space at X Y
2842 /// </summary>
2843 /// <param name="iprimspaceArrItemX"></param>
2844 /// <param name="iprimspaceArrItemY"></param>
2845 /// <returns>A pointer to the created space</returns>
2846 internal IntPtr createprimspace(int iprimspaceArrItemX, int iprimspaceArrItemY)
2847 {
2848 // creating a new space for prim and inserting it into main space.
2849 staticPrimspace[iprimspaceArrItemX, iprimspaceArrItemY] = d.HashSpaceCreate(IntPtr.Zero);
2850 d.GeomSetCategoryBits(staticPrimspace[iprimspaceArrItemX, iprimspaceArrItemY], (int)CollisionCategories.Space);
2851// waitForSpaceUnlock(space);
2852 d.SpaceSetSublevel(space, 1);
2853 d.SpaceAdd(space, staticPrimspace[iprimspaceArrItemX, iprimspaceArrItemY]);
2854
2855 return staticPrimspace[iprimspaceArrItemX, iprimspaceArrItemY];
2856 }
2857
2858 /// <summary>
2859 /// Calculates the space the prim should be in by its position
2860 /// </summary>
2861 /// <param name="pos"></param>
2862 /// <returns>a pointer to the space. This could be a new space or reused space.</returns>
2863 internal IntPtr calculateSpaceForGeom(Vector3 pos)
2864 {
2865 int[] xyspace = calculateSpaceArrayItemFromPos(pos);
2866 //m_log.Info("[Physics]: Attempting to use arrayItem: " + xyspace[0].ToString() + "," + xyspace[1].ToString());
2867 return staticPrimspace[xyspace[0], xyspace[1]];
2868 }
2869
2870 /// <summary>
2871 /// Holds the space allocation logic
2872 /// </summary>
2873 /// <param name="pos"></param>
2874 /// <returns>an array item based on the position</returns>
2875 internal int[] calculateSpaceArrayItemFromPos(Vector3 pos)
2876 {
2877 int[] returnint = new int[2];
2878
2879 returnint[0] = (int) (pos.X * spacesPerMeterX);
2880
2881 if (returnint[0] > spaceGridMaxX)
2882 returnint[0] = spaceGridMaxX;
2883 if (returnint[0] < 0)
2884 returnint[0] = 0;
2885
2886 returnint[1] = (int)(pos.Y * spacesPerMeterY);
2887 if (returnint[1] > spaceGridMaxY)
2888 returnint[1] = spaceGridMaxY;
2889 if (returnint[1] < 0)
2890 returnint[1] = 0;
2891
2892 return returnint;
2893 }
2894
2895 #endregion
2896
2897 /// <summary>
2898 /// Routine to figure out if we need to mesh this prim with our mesher
2899 /// </summary>
2900 /// <param name="pbs"></param>
2901 /// <returns></returns>
2902 internal bool needsMeshing(PrimitiveBaseShape pbs)
2903 {
2904 // most of this is redundant now as the mesher will return null if it cant mesh a prim
2905 // but we still need to check for sculptie meshing being enabled so this is the most
2906 // convenient place to do it for now...
2907
2908 // //if (pbs.PathCurve == (byte)Primitive.PathCurve.Circle && pbs.ProfileCurve == (byte)Primitive.ProfileCurve.Circle && pbs.PathScaleY <= 0.75f)
2909 // //m_log.Debug("needsMeshing: " + " pathCurve: " + pbs.PathCurve.ToString() + " profileCurve: " + pbs.ProfileCurve.ToString() + " pathScaleY: " + Primitive.UnpackPathScale(pbs.PathScaleY).ToString());
2910 int iPropertiesNotSupportedDefault = 0;
2911
2912 if (pbs.SculptEntry && !meshSculptedPrim)
2913 {
2914#if SPAM
2915 m_log.Warn("NonMesh");
2916#endif
2917 return false;
2918 }
2919
2920 // if it's a standard box or sphere with no cuts, hollows, twist or top shear, return false since ODE can use an internal representation for the prim
2921 if (!forceSimplePrimMeshing && !pbs.SculptEntry)
2922 {
2923 if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
2924 || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1
2925 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z))
2926 {
2927
2928 if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
2929 && pbs.ProfileHollow == 0
2930 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
2931 && pbs.PathBegin == 0 && pbs.PathEnd == 0
2932 && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
2933 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
2934 && pbs.PathShearX == 0 && pbs.PathShearY == 0)
2935 {
2936#if SPAM
2937 m_log.Warn("NonMesh");
2938#endif
2939 return false;
2940 }
2941 }
2942 }
2943
2944 if (pbs.ProfileHollow != 0)
2945 iPropertiesNotSupportedDefault++;
2946
2947 if ((pbs.PathBegin != 0) || pbs.PathEnd != 0)
2948 iPropertiesNotSupportedDefault++;
2949
2950 if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0))
2951 iPropertiesNotSupportedDefault++;
2952
2953 if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0)
2954 iPropertiesNotSupportedDefault++;
2955
2956 if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100))
2957 iPropertiesNotSupportedDefault++;
2958
2959 if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0))
2960 iPropertiesNotSupportedDefault++;
2961
2962 if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight)
2963 iPropertiesNotSupportedDefault++;
2964
2965 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))
2966 iPropertiesNotSupportedDefault++;
2967
2968 if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1)
2969 iPropertiesNotSupportedDefault++;
2970
2971 // test for torus
2972 if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square)
2973 {
2974 if (pbs.PathCurve == (byte)Extrusion.Curve1)
2975 {
2976 iPropertiesNotSupportedDefault++;
2977 }
2978 }
2979 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
2980 {
2981 if (pbs.PathCurve == (byte)Extrusion.Straight)
2982 {
2983 iPropertiesNotSupportedDefault++;
2984 }
2985
2986 // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits
2987 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
2988 {
2989 iPropertiesNotSupportedDefault++;
2990 }
2991 }
2992 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
2993 {
2994 if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2)
2995 {
2996 iPropertiesNotSupportedDefault++;
2997 }
2998 }
2999 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
3000 {
3001 if (pbs.PathCurve == (byte)Extrusion.Straight)
3002 {
3003 iPropertiesNotSupportedDefault++;
3004 }
3005 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
3006 {
3007 iPropertiesNotSupportedDefault++;
3008 }
3009 }
3010
3011 if (pbs.SculptEntry && meshSculptedPrim)
3012 iPropertiesNotSupportedDefault++;
3013
3014 if (iPropertiesNotSupportedDefault == 0)
3015 {
3016#if SPAM
3017 m_log.Warn("NonMesh");
3018#endif
3019 return false;
3020 }
3021#if SPAM
3022 m_log.Debug("Mesh");
3023#endif
3024 return true;
3025 }
3026
3027 /// <summary>
3028 /// Called after our prim properties are set Scale, position etc.
3029 /// </summary>
3030 /// <remarks>
3031 /// We use this event queue like method to keep changes to the physical scene occuring in the threadlocked mutex
3032 /// This assures us that we have no race conditions
3033 /// </remarks>
3034 /// <param name="actor"></param>
3035 public override void AddPhysicsActorTaint(PhysicsActor actor)
3036 {
3037 if (actor is OdePrim)
3038 {
3039 OdePrim taintedprim = ((OdePrim)actor);
3040 lock (_taintedPrims)
3041 _taintedPrims.Add(taintedprim);
3042 }
3043 else if (actor is OdeCharacter)
3044 {
3045 OdeCharacter taintedchar = ((OdeCharacter)actor);
3046 lock (_taintedActors)
3047 {
3048 _taintedActors.Add(taintedchar);
3049 if (taintedchar.bad)
3050 m_log.ErrorFormat("[ODE SCENE]: Added BAD actor {0} to tainted actors", taintedchar.m_uuid);
3051 }
3052 }
3053 }
3054
3055 /// <summary>
3056 /// This is our main simulate loop
3057 /// </summary>
3058 /// <remarks>
3059 /// It's thread locked by a Mutex in the scene.
3060 /// It holds Collisions, it instructs ODE to step through the physical reactions
3061 /// It moves the objects around in memory
3062 /// It calls the methods that report back to the object owners.. (scenepresence, SceneObjectGroup)
3063 /// </remarks>
3064 /// <param name="timeStep"></param>
3065 /// <returns>The number of frames simulated over that period.</returns>
3066 public override float Simulate(float timeStep)
3067 {
3068 if (!_worldInitialized) return 11f;
3069
3070 int startFrameTick = CollectStats ? Util.EnvironmentTickCount() : 0;
3071 int tempTick = 0, tempTick2 = 0;
3072
3073 if (framecount >= int.MaxValue)
3074 framecount = 0;
3075
3076 framecount++;
3077
3078 float fps = 0;
3079
3080 float timeLeft = timeStep;
3081
3082 //m_log.Info(timeStep.ToString());
3083// step_time += timeSte
3084//
3085// // If We're loaded down by something else,
3086// // or debugging with the Visual Studio project on pause
3087// // skip a few frames to catch up gracefully.
3088// // without shooting the physicsactors all over the place
3089//
3090// if (step_time >= m_SkipFramesAtms)
3091// {
3092// // Instead of trying to catch up, it'll do 5 physics frames only
3093// step_time = ODE_STEPSIZE;
3094// m_physicsiterations = 5;
3095// }
3096// else
3097// {
3098// m_physicsiterations = 10;
3099// }
3100
3101 // We change _collisionEventPrimChanges to avoid locking _collisionEventPrim itself and causing potential
3102 // deadlock if the collision event tries to lock something else later on which is already locked by a
3103 // caller that is adding or removing the collision event.
3104 lock (m_collisionEventActorsChanges)
3105 {
3106 foreach (KeyValuePair<uint, PhysicsActor> kvp in m_collisionEventActorsChanges)
3107 {
3108 if (kvp.Value == null)
3109 m_collisionEventActors.Remove(kvp.Key);
3110 else
3111 m_collisionEventActors[kvp.Key] = kvp.Value;
3112 }
3113
3114 m_collisionEventActorsChanges.Clear();
3115 }
3116
3117 if (SupportsNINJAJoints)
3118 {
3119 DeleteRequestedJoints(); // this must be outside of the lock (OdeLock) to avoid deadlocks
3120 CreateRequestedJoints(); // this must be outside of the lock (OdeLock) to avoid deadlocks
3121 }
3122
3123 lock (OdeLock)
3124 {
3125 // Process 10 frames if the sim is running normal..
3126 // process 5 frames if the sim is running slow
3127 //try
3128 //{
3129 //d.WorldSetQuickStepNumIterations(world, m_physicsiterations);
3130 //}
3131 //catch (StackOverflowException)
3132 //{
3133 // m_log.Error("[PHYSICS]: The operating system wasn't able to allocate enough memory for the simulation. Restarting the sim.");
3134 // ode.drelease(world);
3135 //base.TriggerPhysicsBasedRestart();
3136 //}
3137
3138 // Figure out the Frames Per Second we're going at.
3139 //(step_time == 0.004f, there's 250 of those per second. Times the step time/step size
3140
3141 fps = (timeStep / ODE_STEPSIZE) * 1000;
3142 // HACK: Using a time dilation of 1.0 to debug rubberbanding issues
3143 //m_timeDilation = Math.Min((step_time / ODE_STEPSIZE) / (0.09375f / ODE_STEPSIZE), 1.0f);
3144
3145 while (timeLeft > 0.0f)
3146 {
3147 try
3148 {
3149 if (CollectStats)
3150 tempTick = Util.EnvironmentTickCount();
3151
3152 lock (_taintedActors)
3153 {
3154 foreach (OdeCharacter character in _taintedActors)
3155 character.ProcessTaints();
3156
3157 _taintedActors.Clear();
3158 }
3159
3160 if (CollectStats)
3161 {
3162 tempTick2 = Util.EnvironmentTickCount();
3163 m_stats[ODEAvatarTaintMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
3164 tempTick = tempTick2;
3165 }
3166
3167 lock (_taintedPrims)
3168 {
3169 foreach (OdePrim prim in _taintedPrims)
3170 {
3171 if (prim.m_taintremove)
3172 {
3173// Console.WriteLine("Simulate calls RemovePrimThreadLocked for {0}", prim.Name);
3174 RemovePrimThreadLocked(prim);
3175 }
3176 else
3177 {
3178// Console.WriteLine("Simulate calls ProcessTaints for {0}", prim.Name);
3179 prim.ProcessTaints();
3180 }
3181
3182 prim.m_collisionscore = 0;
3183
3184 // This loop can block up the Heartbeat for a very long time on large regions.
3185 // We need to let the Watchdog know that the Heartbeat is not dead
3186 // NOTE: This is currently commented out, but if things like OAR loading are
3187 // timing the heartbeat out we will need to uncomment it
3188 //Watchdog.UpdateThread();
3189 }
3190
3191 if (SupportsNINJAJoints)
3192 SimulatePendingNINJAJoints();
3193
3194 _taintedPrims.Clear();
3195 }
3196
3197 if (CollectStats)
3198 {
3199 tempTick2 = Util.EnvironmentTickCount();
3200 m_stats[ODEPrimTaintMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
3201 tempTick = tempTick2;
3202 }
3203
3204 // Move characters
3205 foreach (OdeCharacter actor in _characters)
3206 actor.Move(defects);
3207
3208 if (defects.Count != 0)
3209 {
3210 foreach (OdeCharacter actor in defects)
3211 {
3212 m_log.ErrorFormat(
3213 "[ODE SCENE]: Removing physics character {0} {1} from physics scene {2} due to defect found when moving",
3214 actor.Name, actor.LocalID, PhysicsSceneName);
3215
3216 RemoveCharacter(actor);
3217 actor.DestroyOdeStructures();
3218 }
3219
3220 defects.Clear();
3221 }
3222
3223 if (CollectStats)
3224 {
3225 tempTick2 = Util.EnvironmentTickCount();
3226 m_stats[ODEAvatarForcesFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
3227 tempTick = tempTick2;
3228 }
3229
3230 // Move other active objects
3231 foreach (OdePrim prim in _activeprims)
3232 {
3233 prim.m_collisionscore = 0;
3234 prim.Move(timeStep);
3235 }
3236
3237 if (CollectStats)
3238 {
3239 tempTick2 = Util.EnvironmentTickCount();
3240 m_stats[ODEPrimForcesFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
3241 tempTick = tempTick2;
3242 }
3243
3244 //if ((framecount % m_randomizeWater) == 0)
3245 // randomizeWater(waterlevel);
3246
3247 //int RayCastTimeMS = m_rayCastManager.ProcessQueuedRequests();
3248 m_rayCastManager.ProcessQueuedRequests();
3249
3250 if (CollectStats)
3251 {
3252 tempTick2 = Util.EnvironmentTickCount();
3253 m_stats[ODERaycastingFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
3254 tempTick = tempTick2;
3255 }
3256
3257 collision_optimized();
3258
3259 if (CollectStats)
3260 {
3261 tempTick2 = Util.EnvironmentTickCount();
3262 m_stats[ODEOtherCollisionFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
3263 tempTick = tempTick2;
3264 }
3265
3266 foreach (PhysicsActor obj in m_collisionEventActors.Values)
3267 {
3268// m_log.DebugFormat("[PHYSICS]: Assessing {0} {1} for collision events", obj.SOPName, obj.LocalID);
3269
3270 switch ((ActorTypes)obj.PhysicsActorType)
3271 {
3272 case ActorTypes.Agent:
3273 OdeCharacter cobj = (OdeCharacter)obj;
3274 cobj.AddCollisionFrameTime(100);
3275 cobj.SendCollisions();
3276 break;
3277
3278 case ActorTypes.Prim:
3279 OdePrim pobj = (OdePrim)obj;
3280 pobj.SendCollisions();
3281 break;
3282 }
3283 }
3284
3285// if (m_global_contactcount > 0)
3286// m_log.DebugFormat(
3287// "[PHYSICS]: Collision contacts to process this frame = {0}", m_global_contactcount);
3288
3289 m_global_contactcount = 0;
3290
3291 if (CollectStats)
3292 {
3293 tempTick2 = Util.EnvironmentTickCount();
3294 m_stats[ODECollisionNotificationFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
3295 tempTick = tempTick2;
3296 }
3297
3298 d.WorldQuickStep(world, ODE_STEPSIZE);
3299
3300 if (CollectStats)
3301 m_stats[ODENativeStepFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick);
3302
3303 d.JointGroupEmpty(contactgroup);
3304 }
3305 catch (Exception e)
3306 {
3307 m_log.ErrorFormat("[ODE SCENE]: {0}, {1}, {2}", e.Message, e.TargetSite, e);
3308 }
3309
3310 timeLeft -= ODE_STEPSIZE;
3311 }
3312
3313 if (CollectStats)
3314 tempTick = Util.EnvironmentTickCount();
3315
3316 foreach (OdeCharacter actor in _characters)
3317 {
3318 if (actor.bad)
3319 m_log.ErrorFormat("[ODE SCENE]: BAD Actor {0} in _characters list was not removed?", actor.m_uuid);
3320
3321 actor.UpdatePositionAndVelocity(defects);
3322 }
3323
3324 if (defects.Count != 0)
3325 {
3326 foreach (OdeCharacter actor in defects)
3327 {
3328 m_log.ErrorFormat(
3329 "[ODE SCENE]: Removing physics character {0} {1} from physics scene {2} due to defect found when updating position and velocity",
3330 actor.Name, actor.LocalID, PhysicsSceneName);
3331
3332 RemoveCharacter(actor);
3333 actor.DestroyOdeStructures();
3334 }
3335
3336 defects.Clear();
3337 }
3338
3339 if (CollectStats)
3340 {
3341 tempTick2 = Util.EnvironmentTickCount();
3342 m_stats[ODEAvatarUpdateFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick);
3343 tempTick = tempTick2;
3344 }
3345
3346 //if (timeStep < 0.2f)
3347
3348 foreach (OdePrim prim in _activeprims)
3349 {
3350 if (prim.IsPhysical && (d.BodyIsEnabled(prim.Body) || !prim._zeroFlag))
3351 {
3352 prim.UpdatePositionAndVelocity();
3353
3354 if (SupportsNINJAJoints)
3355 SimulateActorPendingJoints(prim);
3356 }
3357 }
3358
3359 if (CollectStats)
3360 m_stats[ODEPrimUpdateFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick);
3361
3362 //DumpJointInfo();
3363
3364 // Finished with all sim stepping. If requested, dump world state to file for debugging.
3365 // TODO: This call to the export function is already inside lock (OdeLock) - but is an extra lock needed?
3366 // TODO: This overwrites all dump files in-place. Should this be a growing logfile, or separate snapshots?
3367 if (physics_logging && (physics_logging_interval > 0) && (framecount % physics_logging_interval == 0))
3368 {
3369 string fname = "state-" + world.ToString() + ".DIF"; // give each physics world a separate filename
3370 string prefix = "world" + world.ToString(); // prefix for variable names in exported .DIF file
3371
3372 if (physics_logging_append_existing_logfile)
3373 {
3374 string header = "-------------- START OF PHYSICS FRAME " + framecount.ToString() + " --------------";
3375 TextWriter fwriter = File.AppendText(fname);
3376 fwriter.WriteLine(header);
3377 fwriter.Close();
3378 }
3379
3380 d.WorldExportDIF(world, fname, physics_logging_append_existing_logfile, prefix);
3381 }
3382
3383 latertickcount = Util.EnvironmentTickCountSubtract(tickCountFrameRun);
3384
3385 // OpenSimulator above does 10 fps. 10 fps = means that the main thread loop and physics
3386 // has a max of 100 ms to run theoretically.
3387 // If the main loop stalls, it calls Simulate later which makes the tick count ms larger.
3388 // If Physics stalls, it takes longer which makes the tick count ms larger.
3389
3390 if (latertickcount < 100)
3391 {
3392 m_timeDilation = 1.0f;
3393 }
3394 else
3395 {
3396 m_timeDilation = 100f / latertickcount;
3397 //m_timeDilation = Math.Min((Math.Max(100 - (Util.EnvironmentTickCount() - tickCountFrameRun), 1) / 100f), 1.0f);
3398 }
3399
3400 tickCountFrameRun = Util.EnvironmentTickCount();
3401
3402 if (CollectStats)
3403 m_stats[ODETotalFrameMsStatName] += Util.EnvironmentTickCountSubtract(startFrameTick);
3404 }
3405
3406 return fps;
3407 }
3408
3409 /// <summary>
3410 /// Simulate pending NINJA joints.
3411 /// </summary>
3412 /// <remarks>
3413 /// Called by the main Simulate() loop if NINJA joints are active. Should not be called from anywhere else.
3414 /// </remarks>
3415 private void SimulatePendingNINJAJoints()
3416 {
3417 // Create pending joints, if possible
3418
3419 // joints can only be processed after ALL bodies are processed (and exist in ODE), since creating
3420 // a joint requires specifying the body id of both involved bodies
3421 if (pendingJoints.Count > 0)
3422 {
3423 List<PhysicsJoint> successfullyProcessedPendingJoints = new List<PhysicsJoint>();
3424 //DoJointErrorMessage(joints_connecting_actor, "taint: " + pendingJoints.Count + " pending joints");
3425 foreach (PhysicsJoint joint in pendingJoints)
3426 {
3427 //DoJointErrorMessage(joint, "taint: time to create joint with parms: " + joint.RawParams);
3428 string[] jointParams = joint.RawParams.Split(" ".ToCharArray(), System.StringSplitOptions.RemoveEmptyEntries);
3429 List<IntPtr> jointBodies = new List<IntPtr>();
3430 bool allJointBodiesAreReady = true;
3431 foreach (string jointParam in jointParams)
3432 {
3433 if (jointParam == "NULL")
3434 {
3435 //DoJointErrorMessage(joint, "attaching NULL joint to world");
3436 jointBodies.Add(IntPtr.Zero);
3437 }
3438 else
3439 {
3440 //DoJointErrorMessage(joint, "looking for prim name: " + jointParam);
3441 bool foundPrim = false;
3442 lock (_prims)
3443 {
3444 foreach (OdePrim prim in _prims) // FIXME: inefficient
3445 {
3446 if (prim.SOPName == jointParam)
3447 {
3448 //DoJointErrorMessage(joint, "found for prim name: " + jointParam);
3449 if (prim.IsPhysical && prim.Body != IntPtr.Zero)
3450 {
3451 jointBodies.Add(prim.Body);
3452 foundPrim = true;
3453 break;
3454 }
3455 else
3456 {
3457 DoJointErrorMessage(joint, "prim name " + jointParam +
3458 " exists but is not (yet) physical; deferring joint creation. " +
3459 "IsPhysical property is " + prim.IsPhysical +
3460 " and body is " + prim.Body);
3461 foundPrim = false;
3462 break;
3463 }
3464 }
3465 }
3466 }
3467 if (foundPrim)
3468 {
3469 // all is fine
3470 }
3471 else
3472 {
3473 allJointBodiesAreReady = false;
3474 break;
3475 }
3476 }
3477 }
3478
3479 if (allJointBodiesAreReady)
3480 {
3481 //DoJointErrorMessage(joint, "allJointBodiesAreReady for " + joint.ObjectNameInScene + " with parms " + joint.RawParams);
3482 if (jointBodies[0] == jointBodies[1])
3483 {
3484 DoJointErrorMessage(joint, "ERROR: joint cannot be created; the joint bodies are the same, body1==body2. Raw body is " + jointBodies[0] + ". raw parms: " + joint.RawParams);
3485 }
3486 else
3487 {
3488 switch (joint.Type)
3489 {
3490 case PhysicsJointType.Ball:
3491 {
3492 IntPtr odeJoint;
3493 //DoJointErrorMessage(joint, "ODE creating ball joint ");
3494 odeJoint = d.JointCreateBall(world, IntPtr.Zero);
3495 //DoJointErrorMessage(joint, "ODE attaching ball joint: " + odeJoint + " with b1:" + jointBodies[0] + " b2:" + jointBodies[1]);
3496 d.JointAttach(odeJoint, jointBodies[0], jointBodies[1]);
3497 //DoJointErrorMessage(joint, "ODE setting ball anchor: " + odeJoint + " to vec:" + joint.Position);
3498 d.JointSetBallAnchor(odeJoint,
3499 joint.Position.X,
3500 joint.Position.Y,
3501 joint.Position.Z);
3502 //DoJointErrorMessage(joint, "ODE joint setting OK");
3503 //DoJointErrorMessage(joint, "The ball joint's bodies are here: b0: ");
3504 //DoJointErrorMessage(joint, "" + (jointBodies[0] != IntPtr.Zero ? "" + d.BodyGetPosition(jointBodies[0]) : "fixed environment"));
3505 //DoJointErrorMessage(joint, "The ball joint's bodies are here: b1: ");
3506 //DoJointErrorMessage(joint, "" + (jointBodies[1] != IntPtr.Zero ? "" + d.BodyGetPosition(jointBodies[1]) : "fixed environment"));
3507
3508 if (joint is OdePhysicsJoint)
3509 {
3510 ((OdePhysicsJoint)joint).jointID = odeJoint;
3511 }
3512 else
3513 {
3514 DoJointErrorMessage(joint, "WARNING: non-ode joint in ODE!");
3515 }
3516 }
3517 break;
3518 case PhysicsJointType.Hinge:
3519 {
3520 IntPtr odeJoint;
3521 //DoJointErrorMessage(joint, "ODE creating hinge joint ");
3522 odeJoint = d.JointCreateHinge(world, IntPtr.Zero);
3523 //DoJointErrorMessage(joint, "ODE attaching hinge joint: " + odeJoint + " with b1:" + jointBodies[0] + " b2:" + jointBodies[1]);
3524 d.JointAttach(odeJoint, jointBodies[0], jointBodies[1]);
3525 //DoJointErrorMessage(joint, "ODE setting hinge anchor: " + odeJoint + " to vec:" + joint.Position);
3526 d.JointSetHingeAnchor(odeJoint,
3527 joint.Position.X,
3528 joint.Position.Y,
3529 joint.Position.Z);
3530 // We use the orientation of the x-axis of the joint's coordinate frame
3531 // as the axis for the hinge.
3532
3533 // Therefore, we must get the joint's coordinate frame based on the
3534 // joint.Rotation field, which originates from the orientation of the
3535 // joint's proxy object in the scene.
3536
3537 // The joint's coordinate frame is defined as the transformation matrix
3538 // that converts a vector from joint-local coordinates into world coordinates.
3539 // World coordinates are defined as the XYZ coordinate system of the sim,
3540 // as shown in the top status-bar of the viewer.
3541
3542 // Once we have the joint's coordinate frame, we extract its X axis (AtAxis)
3543 // and use that as the hinge axis.
3544
3545 //joint.Rotation.Normalize();
3546 Matrix4 proxyFrame = Matrix4.CreateFromQuaternion(joint.Rotation);
3547
3548 // Now extract the X axis of the joint's coordinate frame.
3549
3550 // Do not try to use proxyFrame.AtAxis or you will become mired in the
3551 // tar pit of transposed, inverted, and generally messed-up orientations.
3552 // (In other words, Matrix4.AtAxis() is borked.)
3553 // Vector3 jointAxis = proxyFrame.AtAxis; <--- this path leadeth to madness
3554
3555 // Instead, compute the X axis of the coordinate frame by transforming
3556 // the (1,0,0) vector. At least that works.
3557
3558 //m_log.Debug("PHY: making axis: complete matrix is " + proxyFrame);
3559 Vector3 jointAxis = Vector3.Transform(Vector3.UnitX, proxyFrame);
3560 //m_log.Debug("PHY: making axis: hinge joint axis is " + jointAxis);
3561 //DoJointErrorMessage(joint, "ODE setting hinge axis: " + odeJoint + " to vec:" + jointAxis);
3562 d.JointSetHingeAxis(odeJoint,
3563 jointAxis.X,
3564 jointAxis.Y,
3565 jointAxis.Z);
3566 //d.JointSetHingeParam(odeJoint, (int)dParam.CFM, 0.1f);
3567 if (joint is OdePhysicsJoint)
3568 {
3569 ((OdePhysicsJoint)joint).jointID = odeJoint;
3570 }
3571 else
3572 {
3573 DoJointErrorMessage(joint, "WARNING: non-ode joint in ODE!");
3574 }
3575 }
3576 break;
3577 }
3578 successfullyProcessedPendingJoints.Add(joint);
3579 }
3580 }
3581 else
3582 {
3583 DoJointErrorMessage(joint, "joint could not yet be created; still pending");
3584 }
3585 }
3586
3587 foreach (PhysicsJoint successfullyProcessedJoint in successfullyProcessedPendingJoints)
3588 {
3589 //DoJointErrorMessage(successfullyProcessedJoint, "finalizing succesfully procsssed joint " + successfullyProcessedJoint.ObjectNameInScene + " parms " + successfullyProcessedJoint.RawParams);
3590 //DoJointErrorMessage(successfullyProcessedJoint, "removing from pending");
3591 InternalRemovePendingJoint(successfullyProcessedJoint);
3592 //DoJointErrorMessage(successfullyProcessedJoint, "adding to active");
3593 InternalAddActiveJoint(successfullyProcessedJoint);
3594 //DoJointErrorMessage(successfullyProcessedJoint, "done");
3595 }
3596 }
3597 }
3598
3599 /// <summary>
3600 /// Simulate the joint proxies of a NINJA actor.
3601 /// </summary>
3602 /// <remarks>
3603 /// Called as part of the Simulate() loop if NINJA physics is active. Must only be called from there.
3604 /// </remarks>
3605 /// <param name="actor"></param>
3606 private void SimulateActorPendingJoints(OdePrim actor)
3607 {
3608 // If an actor moved, move its joint proxy objects as well.
3609 // There seems to be an event PhysicsActor.OnPositionUpdate that could be used
3610 // for this purpose but it is never called! So we just do the joint
3611 // movement code here.
3612
3613 if (actor.SOPName != null &&
3614 joints_connecting_actor.ContainsKey(actor.SOPName) &&
3615 joints_connecting_actor[actor.SOPName] != null &&
3616 joints_connecting_actor[actor.SOPName].Count > 0)
3617 {
3618 foreach (PhysicsJoint affectedJoint in joints_connecting_actor[actor.SOPName])
3619 {
3620 if (affectedJoint.IsInPhysicsEngine)
3621 {
3622 DoJointMoved(affectedJoint);
3623 }
3624 else
3625 {
3626 DoJointErrorMessage(affectedJoint, "a body connected to a joint was moved, but the joint doesn't exist yet! this will lead to joint error. joint was: " + affectedJoint.ObjectNameInScene + " parms:" + affectedJoint.RawParams);
3627 }
3628 }
3629 }
3630 }
3631
3632 public override void GetResults()
3633 {
3634 }
3635
3636 public override bool IsThreaded
3637 {
3638 // for now we won't be multithreaded
3639 get { return false; }
3640 }
3641
3642 public override void SetTerrain(float[] heightMap)
3643 {
3644 if (m_worldOffset != Vector3.Zero && m_parentScene != null)
3645 {
3646 if (m_parentScene is OdeScene)
3647 {
3648 ((OdeScene)m_parentScene).SetTerrain(heightMap, m_worldOffset);
3649 }
3650 }
3651 else
3652 {
3653 SetTerrain(heightMap, m_worldOffset);
3654 }
3655 }
3656
3657 private void SetTerrain(float[] heightMap, Vector3 pOffset)
3658 {
3659 int startTime = Util.EnvironmentTickCount();
3660 m_log.DebugFormat("[ODE SCENE]: Setting terrain for {0} with offset {1}", PhysicsSceneName, pOffset);
3661
3662
3663 float[] _heightmap;
3664
3665 // ok im lasy this are just a aliases
3666 uint regionsizeX = m_regionWidth;
3667 uint regionsizeY = m_regionHeight;
3668
3669 // map is rotated
3670 uint heightmapWidth = regionsizeY + 2;
3671 uint heightmapHeight = regionsizeX + 2;
3672
3673 uint heightmapWidthSamples = heightmapWidth + 1;
3674 uint heightmapHeightSamples = heightmapHeight + 1;
3675
3676 _heightmap = new float[heightmapWidthSamples * heightmapHeightSamples];
3677
3678
3679 const float scale = 1.0f;
3680 const float offset = 0.0f;
3681 const float thickness = 10f;
3682 const int wrap = 0;
3683
3684
3685 float hfmin = float.MaxValue;
3686 float hfmax = float.MinValue;
3687 float val;
3688 uint xx;
3689 uint yy;
3690
3691 uint maxXX = regionsizeX - 1;
3692 uint maxYY = regionsizeY - 1;
3693
3694 // flipping map adding one margin all around so things don't fall in edges
3695
3696 uint xt = 0;
3697 xx = 0;
3698
3699
3700 for (uint x = 0; x < heightmapWidthSamples; x++)
3701 {
3702 if (x > 1 && xx < maxXX)
3703 xx++;
3704 yy = 0;
3705 for (uint y = 0; y < heightmapHeightSamples; y++)
3706 {
3707 if (y > 1 && y < maxYY)
3708 yy += regionsizeX;
3709
3710 val = heightMap[yy + xx];
3711 if (val < 0.0f)
3712 val = 0.0f;
3713 _heightmap[xt + y] = val;
3714
3715 if (hfmin > val)
3716 hfmin = val;
3717 if (hfmax < val)
3718 hfmax = val;
3719 }
3720 xt += heightmapHeightSamples;
3721 }
3722
3723 lock (OdeLock)
3724 {
3725 IntPtr GroundGeom = IntPtr.Zero;
3726 if (RegionTerrain.TryGetValue(pOffset, out GroundGeom))
3727 {
3728 RegionTerrain.Remove(pOffset);
3729 if (GroundGeom != IntPtr.Zero)
3730 {
3731 if (TerrainHeightFieldHeights.ContainsKey(GroundGeom))
3732 {
3733 TerrainHeightFieldHeights.Remove(GroundGeom);
3734 }
3735 d.SpaceRemove(space, GroundGeom);
3736 d.GeomDestroy(GroundGeom);
3737 }
3738
3739 }
3740 IntPtr HeightmapData = d.GeomHeightfieldDataCreate();
3741 d.GeomHeightfieldDataBuildSingle(HeightmapData, _heightmap, 0,
3742 heightmapWidth, heightmapHeight,
3743 (int)heightmapWidthSamples, (int)heightmapHeightSamples,
3744 scale, offset, thickness, wrap);
3745
3746 d.GeomHeightfieldDataSetBounds(HeightmapData, hfmin - 1, hfmax + 1);
3747 GroundGeom = d.CreateHeightfield(space, HeightmapData, 1);
3748 if (GroundGeom != IntPtr.Zero)
3749 {
3750 d.GeomSetCategoryBits(GroundGeom, (int)(CollisionCategories.Land));
3751 d.GeomSetCollideBits(GroundGeom, (int)(CollisionCategories.Space));
3752
3753 }
3754 geom_name_map[GroundGeom] = "Terrain";
3755
3756 d.Matrix3 R = new d.Matrix3();
3757
3758 Quaternion q1 = Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), 1.5707f);
3759 Quaternion q2 = Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), 1.5707f);
3760
3761 q1 = q1 * q2;
3762 Vector3 v3;
3763 float angle;
3764 q1.GetAxisAngle(out v3, out angle);
3765
3766 d.RFromAxisAndAngle(out R, v3.X, v3.Y, v3.Z, angle);
3767 d.GeomSetRotation(GroundGeom, ref R);
3768 d.GeomSetPosition(GroundGeom, pOffset.X + regionsizeX * 0.5f, pOffset.Y + regionsizeY * 0.5f, 0f);
3769 IntPtr testGround = IntPtr.Zero;
3770 if (RegionTerrain.TryGetValue(pOffset, out testGround))
3771 {
3772 RegionTerrain.Remove(pOffset);
3773 }
3774 RegionTerrain.Add(pOffset, GroundGeom, GroundGeom);
3775 TerrainHeightFieldHeights.Add(GroundGeom,_heightmap);
3776 }
3777
3778 m_log.DebugFormat(
3779 "[ODE SCENE]: Setting terrain for {0} took {1}ms", PhysicsSceneName, Util.EnvironmentTickCountSubtract(startTime));
3780 }
3781
3782 public override void DeleteTerrain()
3783 {
3784 }
3785
3786 internal float GetWaterLevel()
3787 {
3788 return waterlevel;
3789 }
3790
3791 public override bool SupportsCombining()
3792 {
3793 return m_suportCombine;
3794 }
3795
3796 public override void SetWaterLevel(float baseheight)
3797 {
3798 waterlevel = baseheight;
3799// randomizeWater(waterlevel);
3800 }
3801
3802/*
3803 private void randomizeWater(float baseheight)
3804 {
3805 uint heightmapWidth = m_regionWidth + 2;
3806 uint heightmapHeight = m_regionHeight + 2;
3807 uint heightmapWidthSamples = m_regionWidth + 2;
3808 uint heightmapHeightSamples = m_regionHeight + 2;
3809 float scale = 1.0f;
3810 float offset = 0.0f;
3811 float thickness = 2.9f;
3812 int wrap = 0;
3813
3814 for (int i = 0; i < (258 * 258); i++)
3815 {
3816 _watermap[i] = (baseheight-0.1f) + ((float)fluidRandomizer.Next(1,9) / 10f);
3817 // m_log.Info((baseheight - 0.1f) + ((float)fluidRandomizer.Next(1, 9) / 10f));
3818 }
3819
3820 lock (OdeLock)
3821 {
3822 if (WaterGeom != IntPtr.Zero)
3823 {
3824 d.SpaceRemove(space, WaterGeom);
3825 }
3826 IntPtr HeightmapData = d.GeomHeightfieldDataCreate();
3827 d.GeomHeightfieldDataBuildSingle(HeightmapData, _watermap, 0, heightmapWidth, heightmapHeight,
3828 (int)heightmapWidthSamples, (int)heightmapHeightSamples, scale,
3829 offset, thickness, wrap);
3830 d.GeomHeightfieldDataSetBounds(HeightmapData, m_regionWidth, m_regionHeight);
3831 WaterGeom = d.CreateHeightfield(space, HeightmapData, 1);
3832 if (WaterGeom != IntPtr.Zero)
3833 {
3834 d.GeomSetCategoryBits(WaterGeom, (int)(CollisionCategories.Water));
3835 d.GeomSetCollideBits(WaterGeom, (int)(CollisionCategories.Space));
3836 }
3837
3838 geom_name_map[WaterGeom] = "Water";
3839
3840 d.Matrix3 R = new d.Matrix3();
3841
3842 Quaternion q1 = Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), 1.5707f);
3843 Quaternion q2 = Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), 1.5707f);
3844 //Axiom.Math.Quaternion q3 = Axiom.Math.Quaternion.FromAngleAxis(3.14f, new Axiom.Math.Vector3(0, 0, 1));
3845
3846 q1 = q1 * q2;
3847 //q1 = q1 * q3;
3848 Vector3 v3;
3849 float angle;
3850 q1.GetAxisAngle(out v3, out angle);
3851
3852 d.RFromAxisAndAngle(out R, v3.X, v3.Y, v3.Z, angle);
3853 d.GeomSetRotation(WaterGeom, ref R);
3854 d.GeomSetPosition(WaterGeom, 128, 128, 0);
3855 }
3856 }
3857*/
3858 public override void Dispose()
3859 {
3860 _worldInitialized = false;
3861
3862 m_rayCastManager.Dispose();
3863 m_rayCastManager = null;
3864
3865 lock (OdeLock)
3866 {
3867 lock (_prims)
3868 {
3869 foreach (OdePrim prm in _prims)
3870 {
3871 RemovePrim(prm);
3872 }
3873 }
3874
3875 //foreach (OdeCharacter act in _characters)
3876 //{
3877 //RemoveAvatar(act);
3878 //}
3879 d.WorldDestroy(world);
3880 //d.CloseODE();
3881 }
3882
3883 }
3884
3885 public override Dictionary<uint, float> GetTopColliders()
3886 {
3887 Dictionary<uint, float> topColliders;
3888
3889 lock (_prims)
3890 {
3891 List<OdePrim> orderedPrims = new List<OdePrim>(_prims);
3892 orderedPrims.OrderByDescending(p => p.CollisionScore);
3893 topColliders = orderedPrims.Take(25).ToDictionary(p => p.LocalID, p => p.CollisionScore);
3894
3895 foreach (OdePrim p in _prims)
3896 p.CollisionScore = 0;
3897 }
3898
3899 return topColliders;
3900 }
3901
3902 public override bool SupportsRayCast()
3903 {
3904 return true;
3905 }
3906
3907 public override void RaycastWorld(Vector3 position, Vector3 direction, float length, RaycastCallback retMethod)
3908 {
3909 if (retMethod != null)
3910 {
3911 m_rayCastManager.QueueRequest(position, direction, length, retMethod);
3912 }
3913 }
3914
3915 public override void RaycastWorld(Vector3 position, Vector3 direction, float length, int Count, RayCallback retMethod)
3916 {
3917 if (retMethod != null)
3918 {
3919 m_rayCastManager.QueueRequest(position, direction, length, Count, retMethod);
3920 }
3921 }
3922
3923 public override List<ContactResult> RaycastWorld(Vector3 position, Vector3 direction, float length, int Count)
3924 {
3925 ContactResult[] ourResults = null;
3926 RayCallback retMethod = delegate(List<ContactResult> results)
3927 {
3928 ourResults = new ContactResult[results.Count];
3929 results.CopyTo(ourResults, 0);
3930 };
3931 int waitTime = 0;
3932 m_rayCastManager.QueueRequest(position, direction, length, Count, retMethod);
3933 while (ourResults == null && waitTime < 1000)
3934 {
3935 Thread.Sleep(1);
3936 waitTime++;
3937 }
3938 if (ourResults == null)
3939 return new List<ContactResult> ();
3940 return new List<ContactResult>(ourResults);
3941 }
3942
3943#if USE_DRAWSTUFF
3944 // Keyboard callback
3945 public void command(int cmd)
3946 {
3947 IntPtr geom;
3948 d.Mass mass;
3949 d.Vector3 sides = new d.Vector3(d.RandReal() * 0.5f + 0.1f, d.RandReal() * 0.5f + 0.1f, d.RandReal() * 0.5f + 0.1f);
3950
3951
3952
3953 Char ch = Char.ToLower((Char)cmd);
3954 switch ((Char)ch)
3955 {
3956 case 'w':
3957 try
3958 {
3959 Vector3 rotate = (new Vector3(1, 0, 0) * Quaternion.CreateFromEulers(hpr.Z * Utils.DEG_TO_RAD, hpr.Y * Utils.DEG_TO_RAD, hpr.X * Utils.DEG_TO_RAD));
3960
3961 xyz.X += rotate.X; xyz.Y += rotate.Y; xyz.Z += rotate.Z;
3962 ds.SetViewpoint(ref xyz, ref hpr);
3963 }
3964 catch (ArgumentException)
3965 { hpr.X = 0; }
3966 break;
3967
3968 case 'a':
3969 hpr.X++;
3970 ds.SetViewpoint(ref xyz, ref hpr);
3971 break;
3972
3973 case 's':
3974 try
3975 {
3976 Vector3 rotate2 = (new Vector3(-1, 0, 0) * Quaternion.CreateFromEulers(hpr.Z * Utils.DEG_TO_RAD, hpr.Y * Utils.DEG_TO_RAD, hpr.X * Utils.DEG_TO_RAD));
3977
3978 xyz.X += rotate2.X; xyz.Y += rotate2.Y; xyz.Z += rotate2.Z;
3979 ds.SetViewpoint(ref xyz, ref hpr);
3980 }
3981 catch (ArgumentException)
3982 { hpr.X = 0; }
3983 break;
3984 case 'd':
3985 hpr.X--;
3986 ds.SetViewpoint(ref xyz, ref hpr);
3987 break;
3988 case 'r':
3989 xyz.Z++;
3990 ds.SetViewpoint(ref xyz, ref hpr);
3991 break;
3992 case 'f':
3993 xyz.Z--;
3994 ds.SetViewpoint(ref xyz, ref hpr);
3995 break;
3996 case 'e':
3997 xyz.Y++;
3998 ds.SetViewpoint(ref xyz, ref hpr);
3999 break;
4000 case 'q':
4001 xyz.Y--;
4002 ds.SetViewpoint(ref xyz, ref hpr);
4003 break;
4004 }
4005 }
4006
4007 public void step(int pause)
4008 {
4009
4010 ds.SetColor(1.0f, 1.0f, 0.0f);
4011 ds.SetTexture(ds.Texture.Wood);
4012 lock (_prims)
4013 {
4014 foreach (OdePrim prm in _prims)
4015 {
4016 //IntPtr body = d.GeomGetBody(prm.prim_geom);
4017 if (prm.prim_geom != IntPtr.Zero)
4018 {
4019 d.Vector3 pos;
4020 d.GeomCopyPosition(prm.prim_geom, out pos);
4021 //d.BodyCopyPosition(body, out pos);
4022
4023 d.Matrix3 R;
4024 d.GeomCopyRotation(prm.prim_geom, out R);
4025 //d.BodyCopyRotation(body, out R);
4026
4027
4028 d.Vector3 sides = new d.Vector3();
4029 sides.X = prm.Size.X;
4030 sides.Y = prm.Size.Y;
4031 sides.Z = prm.Size.Z;
4032
4033 ds.DrawBox(ref pos, ref R, ref sides);
4034 }
4035 }
4036 }
4037 ds.SetColor(1.0f, 0.0f, 0.0f);
4038
4039 foreach (OdeCharacter chr in _characters)
4040 {
4041 if (chr.Shell != IntPtr.Zero)
4042 {
4043 IntPtr body = d.GeomGetBody(chr.Shell);
4044
4045 d.Vector3 pos;
4046 d.GeomCopyPosition(chr.Shell, out pos);
4047 //d.BodyCopyPosition(body, out pos);
4048
4049 d.Matrix3 R;
4050 d.GeomCopyRotation(chr.Shell, out R);
4051 //d.BodyCopyRotation(body, out R);
4052
4053 ds.DrawCapsule(ref pos, ref R, chr.Size.Z, 0.35f);
4054 d.Vector3 sides = new d.Vector3();
4055 sides.X = 0.5f;
4056 sides.Y = 0.5f;
4057 sides.Z = 0.5f;
4058
4059 ds.DrawBox(ref pos, ref R, ref sides);
4060 }
4061 }
4062 }
4063
4064 public void start(int unused)
4065 {
4066 ds.SetViewpoint(ref xyz, ref hpr);
4067 }
4068#endif
4069
4070 public override Dictionary<string, float> GetStats()
4071 {
4072 if (!CollectStats)
4073 return null;
4074
4075 Dictionary<string, float> returnStats;
4076
4077 lock (OdeLock)
4078 {
4079 returnStats = new Dictionary<string, float>(m_stats);
4080
4081 // FIXME: This is a SUPER DUMB HACK until we can establish stats that aren't subject to a division by
4082 // 3 from the SimStatsReporter.
4083 returnStats[ODETotalAvatarsStatName] = _characters.Count * 3;
4084 returnStats[ODETotalPrimsStatName] = _prims.Count * 3;
4085 returnStats[ODEActivePrimsStatName] = _activeprims.Count * 3;
4086
4087 InitializeExtraStats();
4088 }
4089
4090 returnStats[ODEOtherCollisionFrameMsStatName]
4091 = returnStats[ODEOtherCollisionFrameMsStatName]
4092 - returnStats[ODENativeSpaceCollisionFrameMsStatName]
4093 - returnStats[ODENativeGeomCollisionFrameMsStatName];
4094
4095 return returnStats;
4096 }
4097
4098 private void InitializeExtraStats()
4099 {
4100 m_stats[ODETotalFrameMsStatName] = 0;
4101 m_stats[ODEAvatarTaintMsStatName] = 0;
4102 m_stats[ODEPrimTaintMsStatName] = 0;
4103 m_stats[ODEAvatarForcesFrameMsStatName] = 0;
4104 m_stats[ODEPrimForcesFrameMsStatName] = 0;
4105 m_stats[ODERaycastingFrameMsStatName] = 0;
4106 m_stats[ODENativeStepFrameMsStatName] = 0;
4107 m_stats[ODENativeSpaceCollisionFrameMsStatName] = 0;
4108 m_stats[ODENativeGeomCollisionFrameMsStatName] = 0;
4109 m_stats[ODEOtherCollisionFrameMsStatName] = 0;
4110 m_stats[ODECollisionNotificationFrameMsStatName] = 0;
4111 m_stats[ODEAvatarContactsStatsName] = 0;
4112 m_stats[ODEPrimContactsStatName] = 0;
4113 m_stats[ODEAvatarUpdateFrameMsStatName] = 0;
4114 m_stats[ODEPrimUpdateFrameMsStatName] = 0;
4115 }
4116 }
4117}