aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/UbitOdePlugin/OdeScene.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics/UbitOdePlugin/OdeScene.cs')
-rw-r--r--OpenSim/Region/Physics/UbitOdePlugin/OdeScene.cs2855
1 files changed, 2855 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/UbitOdePlugin/OdeScene.cs b/OpenSim/Region/Physics/UbitOdePlugin/OdeScene.cs
new file mode 100644
index 0000000..510cbe9
--- /dev/null
+++ b/OpenSim/Region/Physics/UbitOdePlugin/OdeScene.cs
@@ -0,0 +1,2855 @@
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//#define SPAM
29
30using System;
31using System.Collections.Generic;
32using System.Reflection;
33using System.Runtime.InteropServices;
34using System.Threading;
35using System.IO;
36using System.Diagnostics;
37using log4net;
38using Nini.Config;
39using OdeAPI;
40using OpenSim.Framework;
41using OpenSim.Region.Physics.Manager;
42using OpenMetaverse;
43
44namespace OpenSim.Region.Physics.OdePlugin
45{
46 public enum StatusIndicators : int
47 {
48 Generic = 0,
49 Start = 1,
50 End = 2
51 }
52
53 public struct sCollisionData
54 {
55 public uint ColliderLocalId;
56 public uint CollidedWithLocalId;
57 public int NumberOfCollisions;
58 public int CollisionType;
59 public int StatusIndicator;
60 public int lastframe;
61 }
62
63
64 // colision flags of things others can colide with
65 // rays, sensors, probes removed since can't be colided with
66 // The top space where things are placed provided further selection
67 // ie physical are in active space nonphysical in static
68 // this should be exclusive as possible
69
70 [Flags]
71 public enum CollisionCategories : uint
72 {
73 Disabled = 0,
74 //by 'things' types
75 Space = 0x01,
76 Geom = 0x02, // aka prim/part
77 Character = 0x04,
78 Land = 0x08,
79 Water = 0x010,
80
81 // by state
82 Phantom = 0x01000,
83 VolumeDtc = 0x02000,
84 Selected = 0x04000,
85 NoShape = 0x08000,
86
87
88 All = 0xffffffff
89 }
90
91 /// <summary>
92 /// Material type for a primitive
93 /// </summary>
94 public enum Material : int
95 {
96 /// <summary></summary>
97 Stone = 0,
98 /// <summary></summary>
99 Metal = 1,
100 /// <summary></summary>
101 Glass = 2,
102 /// <summary></summary>
103 Wood = 3,
104 /// <summary></summary>
105 Flesh = 4,
106 /// <summary></summary>
107 Plastic = 5,
108 /// <summary></summary>
109 Rubber = 6,
110
111 light = 7 // compatibility with old viewers
112 }
113
114 public enum changes : int
115 {
116 Add = 0, // arg null. finishs the prim creation. should be used internally only ( to remove later ?)
117 Remove,
118 Link, // arg AuroraODEPrim new parent prim or null to delink. Makes the prim part of a object with prim parent as root
119 // or removes from a object if arg is null
120 DeLink,
121 Position, // arg Vector3 new position in world coords. Changes prim position. Prim must know if it is root or child
122 Orientation, // arg Quaternion new orientation in world coords. Changes prim position. Prim must know it it is root or child
123 PosOffset, // not in use
124 // arg Vector3 new position in local coords. Changes prim position in object
125 OriOffset, // not in use
126 // arg Vector3 new position in local coords. Changes prim position in object
127 Velocity,
128 AngVelocity,
129 Acceleration,
130 Force,
131 Torque,
132 Momentum,
133
134 AddForce,
135 AddAngForce,
136 AngLock,
137
138 Buoyancy,
139
140 PIDTarget,
141 PIDTau,
142 PIDActive,
143
144 PIDHoverHeight,
145 PIDHoverType,
146 PIDHoverTau,
147 PIDHoverActive,
148
149 Size,
150 AvatarSize,
151 Shape,
152 PhysRepData,
153 AddPhysRep,
154
155 CollidesWater,
156 VolumeDtc,
157
158 Physical,
159 Phantom,
160 Selected,
161 disabled,
162 building,
163
164 VehicleType,
165 VehicleFloatParam,
166 VehicleVectorParam,
167 VehicleRotationParam,
168 VehicleFlags,
169 SetVehicle,
170
171 Null //keep this last used do dim the methods array. does nothing but pulsing the prim
172 }
173
174 public struct ODEchangeitem
175 {
176 public PhysicsActor actor;
177 public OdeCharacter character;
178 public changes what;
179 public Object arg;
180 }
181
182
183
184 public class OdeScene : PhysicsScene
185 {
186 private readonly ILog m_log;
187 // private Dictionary<string, sCollisionData> m_storedCollisions = new Dictionary<string, sCollisionData>();
188
189 public bool OdeUbitLib = false;
190// private int threadid = 0;
191 private Random fluidRandomizer = new Random(Environment.TickCount);
192
193 const d.ContactFlags comumContactFlags = d.ContactFlags.SoftERP | d.ContactFlags.SoftCFM |d.ContactFlags.Approx1 | d.ContactFlags.Bounce;
194 const float MaxERP = 0.8f;
195 const float minERP = 0.1f;
196 const float comumContactCFM = 0.0001f;
197
198 float frictionMovementMult = 0.8f;
199
200 float TerrainBounce = 0.1f;
201 float TerrainFriction = 0.3f;
202
203 public float AvatarFriction = 0;// 0.9f * 0.5f;
204
205 private const uint m_regionWidth = Constants.RegionSize;
206 private const uint m_regionHeight = Constants.RegionSize;
207
208 public float ODE_STEPSIZE = 0.020f;
209 public float HalfOdeStep = 0.01f;
210 public int odetimestepMS = 20; // rounded
211 private float metersInSpace = 25.6f;
212 private float m_timeDilation = 1.0f;
213
214 private DateTime m_lastframe;
215 private DateTime m_lastMeshExpire;
216
217 public float gravityx = 0f;
218 public float gravityy = 0f;
219 public float gravityz = -9.8f;
220
221 private float waterlevel = 0f;
222 private int framecount = 0;
223
224 private int m_meshExpireCntr;
225
226// private IntPtr WaterGeom = IntPtr.Zero;
227// private IntPtr WaterHeightmapData = IntPtr.Zero;
228// private GCHandle WaterMapHandler = new GCHandle();
229
230 private float avDensity = 3f;
231 private float avMovementDivisorWalk = 1.3f;
232 private float avMovementDivisorRun = 0.8f;
233 private float minimumGroundFlightOffset = 3f;
234 public float maximumMassObject = 10000.01f;
235
236
237 public float geomDefaultDensity = 10.000006836f;
238
239 public int geomContactPointsStartthrottle = 3;
240 public int geomUpdatesPerThrottledUpdate = 15;
241
242 public float bodyPIDD = 35f;
243 public float bodyPIDG = 25;
244
245// public int geomCrossingFailuresBeforeOutofbounds = 6;
246
247 public int bodyFramesAutoDisable = 5;
248
249
250 private d.NearCallback nearCallback;
251
252 private HashSet<OdeCharacter> _characters = new HashSet<OdeCharacter>();
253 private HashSet<OdePrim> _prims = new HashSet<OdePrim>();
254 private HashSet<OdePrim> _activeprims = new HashSet<OdePrim>();
255 private HashSet<OdePrim> _activegroups = new HashSet<OdePrim>();
256
257 public OpenSim.Framework.LocklessQueue<ODEchangeitem> ChangesQueue = new OpenSim.Framework.LocklessQueue<ODEchangeitem>();
258
259 /// <summary>
260 /// A list of actors that should receive collision events.
261 /// </summary>
262 private List<PhysicsActor> _collisionEventPrim = new List<PhysicsActor>();
263 private List<PhysicsActor> _collisionEventPrimRemove = new List<PhysicsActor>();
264
265 private HashSet<OdeCharacter> _badCharacter = new HashSet<OdeCharacter>();
266// public Dictionary<IntPtr, String> geom_name_map = new Dictionary<IntPtr, String>();
267 public Dictionary<IntPtr, PhysicsActor> actor_name_map = new Dictionary<IntPtr, PhysicsActor>();
268
269 private float contactsurfacelayer = 0.002f;
270
271 private int contactsPerCollision = 80;
272 internal IntPtr ContactgeomsArray = IntPtr.Zero;
273 private IntPtr GlobalContactsArray = IntPtr.Zero;
274
275 const int maxContactsbeforedeath = 4000;
276 private volatile int m_global_contactcount = 0;
277
278 private IntPtr contactgroup;
279
280 public ContactData[] m_materialContactsData = new ContactData[8];
281
282 private Dictionary<Vector3, IntPtr> RegionTerrain = new Dictionary<Vector3, IntPtr>();
283 private Dictionary<IntPtr, float[]> TerrainHeightFieldHeights = new Dictionary<IntPtr, float[]>();
284 private Dictionary<IntPtr, GCHandle> TerrainHeightFieldHeightsHandlers = new Dictionary<IntPtr, GCHandle>();
285
286 private int m_physicsiterations = 10;
287 private const float m_SkipFramesAtms = 0.40f; // Drop frames gracefully at a 400 ms lag
288// private PhysicsActor PANull = new NullPhysicsActor();
289 private float step_time = 0.0f;
290
291 public IntPtr world;
292
293
294 // split the spaces acording to contents type
295 // ActiveSpace contains characters and active prims
296 // StaticSpace contains land and other that is mostly static in enviroment
297 // this can contain subspaces, like the grid in staticspace
298 // as now space only contains this 2 top spaces
299
300 public IntPtr TopSpace; // the global space
301 public IntPtr ActiveSpace; // space for active prims
302 public IntPtr CharsSpace; // space for active prims
303 public IntPtr StaticSpace; // space for the static things around
304 public IntPtr GroundSpace; // space for ground
305
306 public IntPtr SharedRay;
307
308 // some speedup variables
309 private int spaceGridMaxX;
310 private int spaceGridMaxY;
311 private float spacesPerMeter;
312
313 // split static geometry collision into a grid as before
314 private IntPtr[,] staticPrimspace;
315 private IntPtr[] staticPrimspaceOffRegion;
316
317 public Object OdeLock;
318 public static Object SimulationLock;
319
320 public IMesher mesher;
321
322 private IConfigSource m_config;
323
324 public bool physics_logging = false;
325 public int physics_logging_interval = 0;
326 public bool physics_logging_append_existing_logfile = false;
327
328 private Vector3 m_worldOffset = Vector3.Zero;
329 public Vector2 WorldExtents = new Vector2((int)Constants.RegionSize, (int)Constants.RegionSize);
330 private PhysicsScene m_parentScene = null;
331
332 private ODERayCastRequestManager m_rayCastManager;
333 public ODEMeshWorker m_meshWorker;
334
335/* maybe needed if ode uses tls
336 private void checkThread()
337 {
338
339 int th = Thread.CurrentThread.ManagedThreadId;
340 if(th != threadid)
341 {
342 threadid = th;
343 d.AllocateODEDataForThread(~0U);
344 }
345 }
346 */
347 /// <summary>
348 /// Initiailizes the scene
349 /// Sets many properties that ODE requires to be stable
350 /// These settings need to be tweaked 'exactly' right or weird stuff happens.
351 /// </summary>
352 public OdeScene(string sceneIdentifier)
353 {
354 m_log
355 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType.ToString() + "." + sceneIdentifier);
356
357// checkThread();
358 Name = sceneIdentifier;
359
360 OdeLock = new Object();
361 SimulationLock = new Object();
362
363 nearCallback = near;
364
365 m_rayCastManager = new ODERayCastRequestManager(this);
366
367 lock (OdeLock)
368 {
369 // Create the world and the first space
370 try
371 {
372 world = d.WorldCreate();
373 TopSpace = d.HashSpaceCreate(IntPtr.Zero);
374
375 // now the major subspaces
376 ActiveSpace = d.HashSpaceCreate(TopSpace);
377 CharsSpace = d.HashSpaceCreate(TopSpace);
378 StaticSpace = d.HashSpaceCreate(TopSpace);
379 GroundSpace = d.HashSpaceCreate(TopSpace);
380 }
381 catch
382 {
383 // i must RtC#FM
384 // i did!
385 }
386
387 d.HashSpaceSetLevels(TopSpace, -2, 8);
388 d.HashSpaceSetLevels(ActiveSpace, -2, 8);
389 d.HashSpaceSetLevels(CharsSpace, -4, 3);
390 d.HashSpaceSetLevels(StaticSpace, -2, 8);
391 d.HashSpaceSetLevels(GroundSpace, 0, 8);
392
393 // demote to second level
394 d.SpaceSetSublevel(ActiveSpace, 1);
395 d.SpaceSetSublevel(CharsSpace, 1);
396 d.SpaceSetSublevel(StaticSpace, 1);
397 d.SpaceSetSublevel(GroundSpace, 1);
398
399 d.GeomSetCategoryBits(ActiveSpace, (uint)(CollisionCategories.Space |
400 CollisionCategories.Geom |
401 CollisionCategories.Character |
402 CollisionCategories.Phantom |
403 CollisionCategories.VolumeDtc
404 ));
405 d.GeomSetCollideBits(ActiveSpace, (uint)(CollisionCategories.Space |
406 CollisionCategories.Geom |
407 CollisionCategories.Character |
408 CollisionCategories.Phantom |
409 CollisionCategories.VolumeDtc
410 ));
411 d.GeomSetCategoryBits(CharsSpace, (uint)(CollisionCategories.Space |
412 CollisionCategories.Geom |
413 CollisionCategories.Character |
414 CollisionCategories.Phantom |
415 CollisionCategories.VolumeDtc
416 ));
417 d.GeomSetCollideBits(CharsSpace, 0);
418
419 d.GeomSetCategoryBits(StaticSpace, (uint)(CollisionCategories.Space |
420 CollisionCategories.Geom |
421// CollisionCategories.Land |
422// CollisionCategories.Water |
423 CollisionCategories.Phantom |
424 CollisionCategories.VolumeDtc
425 ));
426 d.GeomSetCollideBits(StaticSpace, 0);
427
428 d.GeomSetCategoryBits(GroundSpace, (uint)(CollisionCategories.Land));
429 d.GeomSetCollideBits(GroundSpace, 0);
430
431 contactgroup = d.JointGroupCreate(0);
432 //contactgroup
433
434 SharedRay = d.CreateRay(TopSpace, 1.0f);
435
436 d.WorldSetAutoDisableFlag(world, false);
437 }
438 }
439
440 // Initialize the mesh plugin
441// public override void Initialise(IMesher meshmerizer, IConfigSource config, RegionInfo region )
442 public override void Initialise(IMesher meshmerizer, IConfigSource config)
443 {
444// checkThread();
445 mesher = meshmerizer;
446 m_config = config;
447
448 string ode_config = d.GetConfiguration();
449 if (ode_config != null && ode_config != "")
450 {
451 m_log.WarnFormat("ODE configuration: {0}", ode_config);
452
453 if (ode_config.Contains("ODE_Ubit"))
454 {
455 OdeUbitLib = true;
456 }
457 }
458
459 /*
460 if (region != null)
461 {
462 WorldExtents.X = region.RegionSizeX;
463 WorldExtents.Y = region.RegionSizeY;
464 }
465 */
466
467 // Defaults
468
469 int contactsPerCollision = 80;
470
471 IConfig physicsconfig = null;
472
473 if (m_config != null)
474 {
475 physicsconfig = m_config.Configs["ODEPhysicsSettings"];
476 if (physicsconfig != null)
477 {
478 gravityx = physicsconfig.GetFloat("world_gravityx", gravityx);
479 gravityy = physicsconfig.GetFloat("world_gravityy", gravityy);
480 gravityz = physicsconfig.GetFloat("world_gravityz", gravityz);
481
482 metersInSpace = physicsconfig.GetFloat("meters_in_small_space", metersInSpace);
483
484 contactsurfacelayer = physicsconfig.GetFloat("world_contact_surface_layer", contactsurfacelayer);
485
486 ODE_STEPSIZE = physicsconfig.GetFloat("world_stepsize", ODE_STEPSIZE);
487 m_physicsiterations = physicsconfig.GetInt("world_internal_steps_without_collisions", m_physicsiterations);
488
489 avDensity = physicsconfig.GetFloat("av_density", avDensity);
490 avMovementDivisorWalk = physicsconfig.GetFloat("av_movement_divisor_walk", avMovementDivisorWalk);
491 avMovementDivisorRun = physicsconfig.GetFloat("av_movement_divisor_run", avMovementDivisorRun);
492
493 contactsPerCollision = physicsconfig.GetInt("contacts_per_collision", contactsPerCollision);
494
495 geomContactPointsStartthrottle = physicsconfig.GetInt("geom_contactpoints_start_throttling", 3);
496 geomUpdatesPerThrottledUpdate = physicsconfig.GetInt("geom_updates_before_throttled_update", 15);
497// geomCrossingFailuresBeforeOutofbounds = physicsconfig.GetInt("geom_crossing_failures_before_outofbounds", 5);
498
499 geomDefaultDensity = physicsconfig.GetFloat("geometry_default_density", geomDefaultDensity);
500 bodyFramesAutoDisable = physicsconfig.GetInt("body_frames_auto_disable", bodyFramesAutoDisable);
501
502 physics_logging = physicsconfig.GetBoolean("physics_logging", false);
503 physics_logging_interval = physicsconfig.GetInt("physics_logging_interval", 0);
504 physics_logging_append_existing_logfile = physicsconfig.GetBoolean("physics_logging_append_existing_logfile", false);
505
506 minimumGroundFlightOffset = physicsconfig.GetFloat("minimum_ground_flight_offset", minimumGroundFlightOffset);
507 maximumMassObject = physicsconfig.GetFloat("maximum_mass_object", maximumMassObject);
508 }
509 }
510
511 m_meshWorker = new ODEMeshWorker(this, m_log, meshmerizer, physicsconfig);
512
513 HalfOdeStep = ODE_STEPSIZE * 0.5f;
514 odetimestepMS = (int)(1000.0f * ODE_STEPSIZE +0.5f);
515
516 ContactgeomsArray = Marshal.AllocHGlobal(contactsPerCollision * d.ContactGeom.unmanagedSizeOf);
517 GlobalContactsArray = Marshal.AllocHGlobal(maxContactsbeforedeath * d.Contact.unmanagedSizeOf);
518
519 m_materialContactsData[(int)Material.Stone].mu = 0.8f;
520 m_materialContactsData[(int)Material.Stone].bounce = 0.4f;
521
522 m_materialContactsData[(int)Material.Metal].mu = 0.3f;
523 m_materialContactsData[(int)Material.Metal].bounce = 0.4f;
524
525 m_materialContactsData[(int)Material.Glass].mu = 0.2f;
526 m_materialContactsData[(int)Material.Glass].bounce = 0.7f;
527
528 m_materialContactsData[(int)Material.Wood].mu = 0.6f;
529 m_materialContactsData[(int)Material.Wood].bounce = 0.5f;
530
531 m_materialContactsData[(int)Material.Flesh].mu = 0.9f;
532 m_materialContactsData[(int)Material.Flesh].bounce = 0.3f;
533
534 m_materialContactsData[(int)Material.Plastic].mu = 0.4f;
535 m_materialContactsData[(int)Material.Plastic].bounce = 0.7f;
536
537 m_materialContactsData[(int)Material.Rubber].mu = 0.9f;
538 m_materialContactsData[(int)Material.Rubber].bounce = 0.95f;
539
540 m_materialContactsData[(int)Material.light].mu = 0.0f;
541 m_materialContactsData[(int)Material.light].bounce = 0.0f;
542
543 // Set the gravity,, don't disable things automatically (we set it explicitly on some things)
544
545 d.WorldSetGravity(world, gravityx, gravityy, gravityz);
546 d.WorldSetContactSurfaceLayer(world, contactsurfacelayer);
547
548 d.WorldSetLinearDamping(world, 0.002f);
549 d.WorldSetAngularDamping(world, 0.002f);
550 d.WorldSetAngularDampingThreshold(world, 0f);
551 d.WorldSetLinearDampingThreshold(world, 0f);
552 d.WorldSetMaxAngularSpeed(world, 100f);
553
554 d.WorldSetCFM(world,1e-6f); // a bit harder than default
555 //d.WorldSetCFM(world, 1e-4f); // a bit harder than default
556 d.WorldSetERP(world, 0.6f); // higher than original
557
558 // Set how many steps we go without running collision testing
559 // This is in addition to the step size.
560 // Essentially Steps * m_physicsiterations
561 d.WorldSetQuickStepNumIterations(world, m_physicsiterations);
562
563 d.WorldSetContactMaxCorrectingVel(world, 60.0f);
564
565 spacesPerMeter = 1 / metersInSpace;
566 spaceGridMaxX = (int)(WorldExtents.X * spacesPerMeter);
567 spaceGridMaxY = (int)(WorldExtents.Y * spacesPerMeter);
568
569 staticPrimspace = new IntPtr[spaceGridMaxX, spaceGridMaxY];
570
571 // create all spaces now
572 int i, j;
573 IntPtr newspace;
574
575 for (i = 0; i < spaceGridMaxX; i++)
576 for (j = 0; j < spaceGridMaxY; j++)
577 {
578 newspace = d.HashSpaceCreate(StaticSpace);
579 d.GeomSetCategoryBits(newspace, (int)CollisionCategories.Space);
580 waitForSpaceUnlock(newspace);
581 d.SpaceSetSublevel(newspace, 2);
582 d.HashSpaceSetLevels(newspace, -2, 8);
583 d.GeomSetCategoryBits(newspace, (uint)(CollisionCategories.Space |
584 CollisionCategories.Geom |
585 CollisionCategories.Land |
586 CollisionCategories.Water |
587 CollisionCategories.Phantom |
588 CollisionCategories.VolumeDtc
589 ));
590 d.GeomSetCollideBits(newspace, 0);
591
592 staticPrimspace[i, j] = newspace;
593 }
594 // let this now be real maximum values
595 spaceGridMaxX--;
596 spaceGridMaxY--;
597
598 // create 4 off world spaces (x<0,x>max,y<0,y>max)
599 staticPrimspaceOffRegion = new IntPtr[4];
600
601 for (i = 0; i < 4; i++)
602 {
603 newspace = d.HashSpaceCreate(StaticSpace);
604 d.GeomSetCategoryBits(newspace, (int)CollisionCategories.Space);
605 waitForSpaceUnlock(newspace);
606 d.SpaceSetSublevel(newspace, 2);
607 d.HashSpaceSetLevels(newspace, -2, 8);
608 d.GeomSetCategoryBits(newspace, (uint)(CollisionCategories.Space |
609 CollisionCategories.Geom |
610 CollisionCategories.Land |
611 CollisionCategories.Water |
612 CollisionCategories.Phantom |
613 CollisionCategories.VolumeDtc
614 ));
615 d.GeomSetCollideBits(newspace, 0);
616
617 staticPrimspaceOffRegion[i] = newspace;
618 }
619
620 m_lastframe = DateTime.UtcNow;
621 m_lastMeshExpire = m_lastframe;
622 }
623
624 internal void waitForSpaceUnlock(IntPtr space)
625 {
626 //if (space != IntPtr.Zero)
627 //while (d.SpaceLockQuery(space)) { } // Wait and do nothing
628 }
629
630 #region Collision Detection
631
632 // sets a global contact for a joint for contactgeom , and base contact description)
633
634 private IntPtr CreateContacJoint(ref d.ContactGeom contactGeom, float mu, float bounce, float cfm, float erpscale, float dscale)
635 {
636 if (GlobalContactsArray == IntPtr.Zero || m_global_contactcount >= maxContactsbeforedeath)
637 return IntPtr.Zero;
638
639 float erp = contactGeom.depth;
640 erp *= erpscale;
641 if (erp < minERP)
642 erp = minERP;
643 else if (erp > MaxERP)
644 erp = MaxERP;
645
646 float depth = contactGeom.depth * dscale;
647 if (depth > 0.5f)
648 depth = 0.5f;
649
650 d.Contact newcontact = new d.Contact();
651 newcontact.geom.depth = depth;
652 newcontact.geom.g1 = contactGeom.g1;
653 newcontact.geom.g2 = contactGeom.g2;
654 newcontact.geom.pos = contactGeom.pos;
655 newcontact.geom.normal = contactGeom.normal;
656 newcontact.geom.side1 = contactGeom.side1;
657 newcontact.geom.side2 = contactGeom.side2;
658
659 // this needs bounce also
660 newcontact.surface.mode = comumContactFlags;
661 newcontact.surface.mu = mu;
662 newcontact.surface.bounce = bounce;
663 newcontact.surface.soft_cfm = cfm;
664 newcontact.surface.soft_erp = erp;
665
666 IntPtr contact = new IntPtr(GlobalContactsArray.ToInt64() + (Int64)(m_global_contactcount * d.Contact.unmanagedSizeOf));
667 Marshal.StructureToPtr(newcontact, contact, true);
668 return d.JointCreateContactPtr(world, contactgroup, contact);
669 }
670
671 private bool GetCurContactGeom(int index, ref d.ContactGeom newcontactgeom)
672 {
673 if (ContactgeomsArray == IntPtr.Zero || index >= contactsPerCollision)
674 return false;
675
676 IntPtr contactptr = new IntPtr(ContactgeomsArray.ToInt64() + (Int64)(index * d.ContactGeom.unmanagedSizeOf));
677 newcontactgeom = (d.ContactGeom)Marshal.PtrToStructure(contactptr, typeof(d.ContactGeom));
678 return true;
679 }
680
681 /// <summary>
682 /// This is our near callback. A geometry is near a body
683 /// </summary>
684 /// <param name="space">The space that contains the geoms. Remember, spaces are also geoms</param>
685 /// <param name="g1">a geometry or space</param>
686 /// <param name="g2">another geometry or space</param>
687 ///
688
689 private void near(IntPtr space, IntPtr g1, IntPtr g2)
690 {
691 // no lock here! It's invoked from within Simulate(), which is thread-locked
692
693 if (m_global_contactcount >= maxContactsbeforedeath)
694 return;
695
696 // Test if we're colliding a geom with a space.
697 // If so we have to drill down into the space recursively
698
699 if (g1 == IntPtr.Zero || g2 == IntPtr.Zero)
700 return;
701
702 if (d.GeomIsSpace(g1) || d.GeomIsSpace(g2))
703 {
704 // We'll be calling near recursivly if one
705 // of them is a space to find all of the
706 // contact points in the space
707 try
708 {
709 d.SpaceCollide2(g1, g2, IntPtr.Zero, nearCallback);
710 }
711 catch (AccessViolationException)
712 {
713 m_log.Warn("[PHYSICS]: Unable to collide test a space");
714 return;
715 }
716 //here one should check collisions of geoms inside a space
717 // but on each space we only should have geoms that not colide amoung each other
718 // so we don't dig inside spaces
719 return;
720 }
721
722 // get geom bodies to check if we already a joint contact
723 // guess this shouldn't happen now
724 IntPtr b1 = d.GeomGetBody(g1);
725 IntPtr b2 = d.GeomGetBody(g2);
726
727 // d.GeomClassID id = d.GeomGetClass(g1);
728
729 // Figure out how many contact points we have
730 int count = 0;
731 try
732 {
733 // Colliding Geom To Geom
734 // This portion of the function 'was' blatantly ripped off from BoxStack.cs
735
736 if (g1 == g2)
737 return; // Can't collide with yourself
738
739 if (b1 != IntPtr.Zero && b2 != IntPtr.Zero && d.AreConnectedExcluding(b1, b2, d.JointType.Contact))
740 return;
741 /*
742 // debug
743 PhysicsActor dp2;
744 if (d.GeomGetClass(g1) == d.GeomClassID.HeightfieldClass)
745 {
746 d.AABB aabb;
747 d.GeomGetAABB(g2, out aabb);
748 float x = aabb.MaxX - aabb.MinX;
749 float y = aabb.MaxY - aabb.MinY;
750 float z = aabb.MaxZ - aabb.MinZ;
751 if (x > 60.0f || y > 60.0f || z > 60.0f)
752 {
753 if (!actor_name_map.TryGetValue(g2, out dp2))
754 m_log.WarnFormat("[PHYSICS]: failed actor mapping for geom 2");
755 else
756 m_log.WarnFormat("[PHYSICS]: land versus large prim geo {0},size {1}, AABBsize <{2},{3},{4}>, at {5} ori {6},({7})",
757 dp2.Name, dp2.Size, x, y, z,
758 dp2.Position.ToString(),
759 dp2.Orientation.ToString(),
760 dp2.Orientation.Length());
761 return;
762 }
763 }
764 //
765 */
766
767
768 if (d.GeomGetCategoryBits(g1) == (uint)CollisionCategories.VolumeDtc ||
769 d.GeomGetCategoryBits(g2) == (uint)CollisionCategories.VolumeDtc)
770 {
771 int cflags;
772 unchecked
773 {
774 cflags = (int)(1 | d.CONTACTS_UNIMPORTANT);
775 }
776 count = d.CollidePtr(g1, g2, cflags, ContactgeomsArray, d.ContactGeom.unmanagedSizeOf);
777 }
778 else
779 count = d.CollidePtr(g1, g2, (contactsPerCollision & 0xffff), ContactgeomsArray, d.ContactGeom.unmanagedSizeOf);
780 }
781 catch (SEHException)
782 {
783 m_log.Error("[PHYSICS]: 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.");
784 // ode.drelease(world);
785 base.TriggerPhysicsBasedRestart();
786 }
787 catch (Exception e)
788 {
789 m_log.WarnFormat("[PHYSICS]: Unable to collide test an object: {0}", e.Message);
790 return;
791 }
792
793 // contacts done
794 if (count == 0)
795 return;
796
797 // try get physical actors
798 PhysicsActor p1;
799 PhysicsActor p2;
800
801 if (!actor_name_map.TryGetValue(g1, out p1))
802 {
803 m_log.WarnFormat("[PHYSICS]: failed actor mapping for geom 1");
804 return;
805 }
806
807 if (!actor_name_map.TryGetValue(g2, out p2))
808 {
809 m_log.WarnFormat("[PHYSICS]: failed actor mapping for geom 2");
810 return;
811 }
812
813 // update actors collision score
814 if (p1.CollisionScore >= float.MaxValue - count)
815 p1.CollisionScore = 0;
816 p1.CollisionScore += count;
817
818 if (p2.CollisionScore >= float.MaxValue - count)
819 p2.CollisionScore = 0;
820 p2.CollisionScore += count;
821
822 // get first contact
823 d.ContactGeom curContact = new d.ContactGeom();
824
825 if (!GetCurContactGeom(0, ref curContact))
826 return;
827
828 // do volume detection case
829 if ((p1.IsVolumeDtc || p2.IsVolumeDtc))
830 {
831 ContactPoint maxDepthContact = new ContactPoint(
832 new Vector3(curContact.pos.X, curContact.pos.Y, curContact.pos.Z),
833 new Vector3(curContact.normal.X, curContact.normal.Y, curContact.normal.Z),
834 curContact.depth, false
835 );
836
837 collision_accounting_events(p1, p2, maxDepthContact);
838 return;
839 }
840
841 // big messy collision analises
842
843 float mu = 0;
844 float bounce = 0;
845 float cfm = 0.0001f;
846 float erpscale = 1.0f;
847 float dscale = 1.0f;
848 bool IgnoreNegSides = false;
849
850 ContactData contactdata1 = new ContactData(0, 0, false);
851 ContactData contactdata2 = new ContactData(0, 0, false);
852
853 bool dop1ava = false;
854 bool dop2ava = false;
855 bool ignore = false;
856
857 switch (p1.PhysicsActorType)
858 {
859 case (int)ActorTypes.Agent:
860 {
861 dop1ava = true;
862 switch (p2.PhysicsActorType)
863 {
864 case (int)ActorTypes.Agent:
865 p1.CollidingObj = true;
866 p2.CollidingObj = true;
867 break;
868
869 case (int)ActorTypes.Prim:
870 if (p2.Velocity.LengthSquared() > 0.0f)
871 p2.CollidingObj = true;
872 break;
873
874 default:
875 ignore = true; // avatar to terrain and water ignored
876 break;
877 }
878 break;
879 }
880
881 case (int)ActorTypes.Prim:
882 switch (p2.PhysicsActorType)
883 {
884 case (int)ActorTypes.Agent:
885
886 dop2ava = true;
887
888 if (p1.Velocity.LengthSquared() > 0.0f)
889 p1.CollidingObj = true;
890 break;
891
892 case (int)ActorTypes.Prim:
893 if ((p1.Velocity - p2.Velocity).LengthSquared() > 0.0f)
894 {
895 p1.CollidingObj = true;
896 p2.CollidingObj = true;
897 }
898 p1.getContactData(ref contactdata1);
899 p2.getContactData(ref contactdata2);
900 bounce = contactdata1.bounce * contactdata2.bounce;
901 mu = (float)Math.Sqrt(contactdata1.mu * contactdata2.mu);
902
903 cfm = p1.Mass;
904 if (cfm > p2.Mass)
905 cfm = p2.Mass;
906 dscale = 10 / cfm;
907 dscale = (float)Math.Sqrt(dscale);
908 if (dscale > 1.0f)
909 dscale = 1.0f;
910 erpscale = cfm * 0.01f;
911 cfm = 0.0001f / cfm;
912 if (cfm > 0.01f)
913 cfm = 0.01f;
914 else if (cfm < 0.00001f)
915 cfm = 0.00001f;
916
917 if ((Math.Abs(p2.Velocity.X - p1.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y - p1.Velocity.Y) > 0.1f))
918 mu *= frictionMovementMult;
919
920 break;
921
922 case (int)ActorTypes.Ground:
923 p1.getContactData(ref contactdata1);
924 bounce = contactdata1.bounce * TerrainBounce;
925 mu = (float)Math.Sqrt(contactdata1.mu * TerrainFriction);
926 if (Math.Abs(p1.Velocity.X) > 0.1f || Math.Abs(p1.Velocity.Y) > 0.1f)
927 mu *= frictionMovementMult;
928 p1.CollidingGround = true;
929
930 cfm = p1.Mass;
931 dscale = 10 / cfm;
932 dscale = (float)Math.Sqrt(dscale);
933 if (dscale > 1.0f)
934 dscale = 1.0f;
935 erpscale = cfm * 0.01f;
936 cfm = 0.0001f / cfm;
937 if (cfm > 0.01f)
938 cfm = 0.01f;
939 else if (cfm < 0.00001f)
940 cfm = 0.00001f;
941
942 if (d.GeomGetClass(g1) == d.GeomClassID.TriMeshClass)
943 {
944 if (curContact.side1 > 0)
945 IgnoreNegSides = true;
946 }
947 break;
948
949 case (int)ActorTypes.Water:
950 default:
951 ignore = true;
952 break;
953 }
954 break;
955
956 case (int)ActorTypes.Ground:
957 if (p2.PhysicsActorType == (int)ActorTypes.Prim)
958 {
959 p2.CollidingGround = true;
960 p2.getContactData(ref contactdata2);
961 bounce = contactdata2.bounce * TerrainBounce;
962 mu = (float)Math.Sqrt(contactdata2.mu * TerrainFriction);
963
964 cfm = p2.Mass;
965 dscale = 10 / cfm;
966 dscale = (float)Math.Sqrt(dscale);
967
968 if (dscale > 1.0f)
969 dscale = 1.0f;
970
971 erpscale = cfm * 0.01f;
972 cfm = 0.0001f / cfm;
973 if (cfm > 0.01f)
974 cfm = 0.01f;
975 else if (cfm < 0.00001f)
976 cfm = 0.00001f;
977
978 if (curContact.side1 > 0) // should be 2 ?
979 IgnoreNegSides = true;
980
981 if (Math.Abs(p2.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y) > 0.1f)
982 mu *= frictionMovementMult;
983 }
984 else
985 ignore = true;
986 break;
987
988 case (int)ActorTypes.Water:
989 default:
990 break;
991 }
992
993 if (ignore)
994 return;
995
996
997 d.ContactGeom maxContact = curContact;
998// if (IgnoreNegSides && curContact.side1 < 0)
999// maxContact.depth = float.MinValue;
1000
1001 d.ContactGeom minContact = curContact;
1002// if (IgnoreNegSides && curContact.side1 < 0)
1003// minContact.depth = float.MaxValue;
1004
1005 IntPtr Joint;
1006 bool FeetCollision = false;
1007 int ncontacts = 0;
1008
1009
1010 int i = 0;
1011
1012 while (true)
1013 {
1014 if (m_global_contactcount >= maxContactsbeforedeath)
1015 break;
1016
1017// if (!(IgnoreNegSides && curContact.side1 < 0))
1018 {
1019 bool noskip = true;
1020 if (dop1ava)
1021 {
1022 if (!(((OdeCharacter)p1).Collide(g1,false, ref curContact, ref FeetCollision)))
1023
1024 noskip = false;
1025 }
1026 else if (dop2ava)
1027 {
1028 if (!(((OdeCharacter)p2).Collide(g2,true, ref curContact, ref FeetCollision)))
1029 noskip = false;
1030 }
1031
1032 if (noskip)
1033 {
1034 m_global_contactcount++;
1035 ncontacts++;
1036
1037 Joint = CreateContacJoint(ref curContact, mu, bounce, cfm, erpscale, dscale);
1038 d.JointAttach(Joint, b1, b2);
1039
1040 if (curContact.depth > maxContact.depth)
1041 maxContact = curContact;
1042
1043 if (curContact.depth < minContact.depth)
1044 minContact = curContact;
1045 }
1046 }
1047
1048 if (++i >= count)
1049 break;
1050
1051 if (!GetCurContactGeom(i, ref curContact))
1052 break;
1053 }
1054
1055 if (ncontacts > 0)
1056 {
1057 ContactPoint maxDepthContact = new ContactPoint(
1058 new Vector3(maxContact.pos.X, maxContact.pos.Y, maxContact.pos.Z),
1059 new Vector3(minContact.normal.X, minContact.normal.Y, minContact.normal.Z),
1060 maxContact.depth, FeetCollision
1061 );
1062 collision_accounting_events(p1, p2, maxDepthContact);
1063 }
1064 }
1065
1066 private void collision_accounting_events(PhysicsActor p1, PhysicsActor p2, ContactPoint contact)
1067 {
1068 uint obj2LocalID = 0;
1069
1070 bool p1events = p1.SubscribedEvents();
1071 bool p2events = p2.SubscribedEvents();
1072
1073 if (p1.IsVolumeDtc)
1074 p2events = false;
1075 if (p2.IsVolumeDtc)
1076 p1events = false;
1077
1078 if (!p2events && !p1events)
1079 return;
1080
1081 Vector3 vel = Vector3.Zero;
1082 if (p2 != null && p2.IsPhysical)
1083 vel = p2.Velocity;
1084
1085 if (p1 != null && p1.IsPhysical)
1086 vel -= p1.Velocity;
1087
1088 contact.RelativeSpeed = Vector3.Dot(vel, contact.SurfaceNormal);
1089
1090 switch ((ActorTypes)p1.PhysicsActorType)
1091 {
1092 case ActorTypes.Agent:
1093 case ActorTypes.Prim:
1094 {
1095 switch ((ActorTypes)p2.PhysicsActorType)
1096 {
1097 case ActorTypes.Agent:
1098 case ActorTypes.Prim:
1099 if (p2events)
1100 {
1101 AddCollisionEventReporting(p2);
1102 p2.AddCollisionEvent(p1.ParentActor.LocalID, contact);
1103 }
1104 obj2LocalID = p2.ParentActor.LocalID;
1105 break;
1106
1107 case ActorTypes.Ground:
1108 case ActorTypes.Unknown:
1109 default:
1110 obj2LocalID = 0;
1111 break;
1112 }
1113 if (p1events)
1114 {
1115 contact.SurfaceNormal = -contact.SurfaceNormal;
1116 AddCollisionEventReporting(p1);
1117 p1.AddCollisionEvent(obj2LocalID, contact);
1118 }
1119 break;
1120 }
1121 case ActorTypes.Ground:
1122 case ActorTypes.Unknown:
1123 default:
1124 {
1125 if (p2events && !p2.IsVolumeDtc)
1126 {
1127 AddCollisionEventReporting(p2);
1128 p2.AddCollisionEvent(0, contact);
1129 }
1130 break;
1131 }
1132 }
1133 }
1134
1135 /// <summary>
1136 /// This is our collision testing routine in ODE
1137 /// </summary>
1138 /// <param name="timeStep"></param>
1139 private void collision_optimized()
1140 {
1141 lock (_characters)
1142 {
1143 try
1144 {
1145 foreach (OdeCharacter chr in _characters)
1146 {
1147 if (chr == null || chr.Body == IntPtr.Zero)
1148 continue;
1149
1150 chr.IsColliding = false;
1151 // chr.CollidingGround = false; not done here
1152 chr.CollidingObj = false;
1153 // do colisions with static space
1154 d.SpaceCollide2(chr.collider, StaticSpace, IntPtr.Zero, nearCallback);
1155
1156 // chars with chars
1157 d.SpaceCollide(CharsSpace, IntPtr.Zero, nearCallback);
1158 // no coll with gnd
1159 }
1160 }
1161 catch (AccessViolationException)
1162 {
1163 m_log.Warn("[PHYSICS]: Unable to collide Character to static space");
1164 }
1165
1166 }
1167
1168 lock (_activeprims)
1169 {
1170 foreach (OdePrim aprim in _activeprims)
1171 {
1172 aprim.CollisionScore = 0;
1173 aprim.IsColliding = false;
1174 }
1175 }
1176
1177 // collide active prims with static enviroment
1178 lock (_activegroups)
1179 {
1180 try
1181 {
1182 foreach (OdePrim prm in _activegroups)
1183 {
1184 if (!prm.m_outbounds)
1185 {
1186 if (d.BodyIsEnabled(prm.Body))
1187 {
1188 d.SpaceCollide2(StaticSpace, prm.collide_geom, IntPtr.Zero, nearCallback);
1189 d.SpaceCollide2(GroundSpace, prm.collide_geom, IntPtr.Zero, nearCallback);
1190 }
1191 }
1192 }
1193 }
1194 catch (AccessViolationException)
1195 {
1196 m_log.Warn("[PHYSICS]: Unable to collide Active prim to static space");
1197 }
1198 }
1199 // colide active amoung them
1200 try
1201 {
1202 d.SpaceCollide(ActiveSpace, IntPtr.Zero, nearCallback);
1203 }
1204 catch (AccessViolationException)
1205 {
1206 m_log.Warn("[PHYSICS]: Unable to collide Active with Characters space");
1207 }
1208 // and with chars
1209 try
1210 {
1211 d.SpaceCollide2(CharsSpace,ActiveSpace, IntPtr.Zero, nearCallback);
1212 }
1213 catch (AccessViolationException)
1214 {
1215 m_log.Warn("[PHYSICS]: Unable to collide in Active space");
1216 }
1217 // _perloopContact.Clear();
1218 }
1219
1220 #endregion
1221 /// <summary>
1222 /// Add actor to the list that should receive collision events in the simulate loop.
1223 /// </summary>
1224 /// <param name="obj"></param>
1225 public void AddCollisionEventReporting(PhysicsActor obj)
1226 {
1227 if (!_collisionEventPrim.Contains(obj))
1228 _collisionEventPrim.Add(obj);
1229 }
1230
1231 /// <summary>
1232 /// Remove actor from the list that should receive collision events in the simulate loop.
1233 /// </summary>
1234 /// <param name="obj"></param>
1235 public void RemoveCollisionEventReporting(PhysicsActor obj)
1236 {
1237 if (_collisionEventPrim.Contains(obj) && !_collisionEventPrimRemove.Contains(obj))
1238 _collisionEventPrimRemove.Add(obj);
1239 }
1240
1241 public override float TimeDilation
1242 {
1243 get { return m_timeDilation; }
1244 }
1245
1246 public override bool SupportsNINJAJoints
1247 {
1248 get { return false; }
1249 }
1250
1251 #region Add/Remove Entities
1252
1253 public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, float feetOffset, bool isFlying)
1254 {
1255 Vector3 pos;
1256 pos.X = position.X;
1257 pos.Y = position.Y;
1258 pos.Z = position.Z;
1259 OdeCharacter newAv = new OdeCharacter(localID,avName, this, pos, size, feetOffset, avDensity, avMovementDivisorWalk, avMovementDivisorRun);
1260 newAv.Flying = isFlying;
1261 newAv.MinimumGroundFlightOffset = minimumGroundFlightOffset;
1262
1263 return newAv;
1264 }
1265
1266 public void AddCharacter(OdeCharacter chr)
1267 {
1268 lock (_characters)
1269 {
1270 if (!_characters.Contains(chr))
1271 {
1272 _characters.Add(chr);
1273 if (chr.bad)
1274 m_log.DebugFormat("[PHYSICS] Added BAD actor {0} to characters list", chr.m_uuid);
1275 }
1276 }
1277 }
1278
1279 public void RemoveCharacter(OdeCharacter chr)
1280 {
1281 lock (_characters)
1282 {
1283 if (_characters.Contains(chr))
1284 {
1285 _characters.Remove(chr);
1286 }
1287 }
1288 }
1289
1290 public void BadCharacter(OdeCharacter chr)
1291 {
1292 lock (_badCharacter)
1293 {
1294 if (!_badCharacter.Contains(chr))
1295 _badCharacter.Add(chr);
1296 }
1297 }
1298
1299 public override void RemoveAvatar(PhysicsActor actor)
1300 {
1301 //m_log.Debug("[PHYSICS]:ODELOCK");
1302 ((OdeCharacter) actor).Destroy();
1303 }
1304
1305
1306 public void addActivePrim(OdePrim activatePrim)
1307 {
1308 // adds active prim..
1309 lock (_activeprims)
1310 {
1311 if (!_activeprims.Contains(activatePrim))
1312 _activeprims.Add(activatePrim);
1313 }
1314 }
1315
1316 public void addActiveGroups(OdePrim activatePrim)
1317 {
1318 lock (_activegroups)
1319 {
1320 if (!_activegroups.Contains(activatePrim))
1321 _activegroups.Add(activatePrim);
1322 }
1323 }
1324
1325 private PhysicsActor AddPrim(String name, Vector3 position, Vector3 size, Quaternion rotation,
1326 PrimitiveBaseShape pbs, bool isphysical, bool isPhantom, byte shapeType, uint localID)
1327 {
1328 OdePrim newPrim;
1329 lock (OdeLock)
1330 {
1331 newPrim = new OdePrim(name, this, position, size, rotation, pbs, isphysical, isPhantom, shapeType, localID);
1332 lock (_prims)
1333 _prims.Add(newPrim);
1334 }
1335 return newPrim;
1336 }
1337
1338 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
1339 Vector3 size, Quaternion rotation, bool isPhysical, bool isPhantom, uint localid)
1340 {
1341 return AddPrim(primName, position, size, rotation, pbs, isPhysical, isPhantom, 0 , localid);
1342 }
1343
1344
1345 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
1346 Vector3 size, Quaternion rotation, bool isPhysical, uint localid)
1347 {
1348 return AddPrim(primName, position, size, rotation, pbs, isPhysical,false, 0, localid);
1349 }
1350
1351 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
1352 Vector3 size, Quaternion rotation, bool isPhysical, bool isPhantom, byte shapeType, uint localid)
1353 {
1354
1355 return AddPrim(primName, position, size, rotation, pbs, isPhysical,isPhantom, shapeType, localid);
1356 }
1357
1358 public void remActivePrim(OdePrim deactivatePrim)
1359 {
1360 lock (_activeprims)
1361 {
1362 _activeprims.Remove(deactivatePrim);
1363 }
1364 }
1365 public void remActiveGroup(OdePrim deactivatePrim)
1366 {
1367 lock (_activegroups)
1368 {
1369 _activegroups.Remove(deactivatePrim);
1370 }
1371 }
1372
1373 public override void RemovePrim(PhysicsActor prim)
1374 {
1375 // As with all ODE physics operations, we don't remove the prim immediately but signal that it should be
1376 // removed in the next physics simulate pass.
1377 if (prim is OdePrim)
1378 {
1379// lock (OdeLock)
1380 {
1381
1382 OdePrim p = (OdePrim)prim;
1383 p.setPrimForRemoval();
1384 }
1385 }
1386 }
1387 /// <summary>
1388 /// This is called from within simulate but outside the locked portion
1389 /// We need to do our own locking here
1390 /// (Note: As of 20110801 this no longer appears to be true - this is being called within lock (odeLock) in
1391 /// Simulate() -- justincc).
1392 ///
1393 /// Essentially, we need to remove the prim from our space segment, whatever segment it's in.
1394 ///
1395 /// If there are no more prim in the segment, we need to empty (spacedestroy)the segment and reclaim memory
1396 /// that the space was using.
1397 /// </summary>
1398 /// <param name="prim"></param>
1399 public void RemovePrimThreadLocked(OdePrim prim)
1400 {
1401 //Console.WriteLine("RemovePrimThreadLocked " + prim.m_primName);
1402 lock (prim)
1403 {
1404// RemoveCollisionEventReporting(prim);
1405 lock (_prims)
1406 _prims.Remove(prim);
1407 }
1408
1409 }
1410
1411 public bool havePrim(OdePrim prm)
1412 {
1413 lock (_prims)
1414 return _prims.Contains(prm);
1415 }
1416
1417 public bool haveActor(PhysicsActor actor)
1418 {
1419 if (actor is OdePrim)
1420 {
1421 lock (_prims)
1422 return _prims.Contains((OdePrim)actor);
1423 }
1424 else if (actor is OdeCharacter)
1425 {
1426 lock (_characters)
1427 return _characters.Contains((OdeCharacter)actor);
1428 }
1429 return false;
1430 }
1431
1432 #endregion
1433
1434 #region Space Separation Calculation
1435
1436 /// <summary>
1437 /// Called when a static prim moves or becomes static
1438 /// Places the prim in a space one the static sub-spaces grid
1439 /// </summary>
1440 /// <param name="geom">the pointer to the geom that moved</param>
1441 /// <param name="pos">the position that the geom moved to</param>
1442 /// <param name="currentspace">a pointer to the space it was in before it was moved.</param>
1443 /// <returns>a pointer to the new space it's in</returns>
1444 public IntPtr MoveGeomToStaticSpace(IntPtr geom, Vector3 pos, IntPtr currentspace)
1445 {
1446 // moves a prim into another static sub-space or from another space into a static sub-space
1447
1448 // Called ODEPrim so
1449 // it's already in locked space.
1450
1451 if (geom == IntPtr.Zero) // shouldn't happen
1452 return IntPtr.Zero;
1453
1454 // get the static sub-space for current position
1455 IntPtr newspace = calculateSpaceForGeom(pos);
1456
1457 if (newspace == currentspace) // if we are there all done
1458 return newspace;
1459
1460 // else remove it from its current space
1461 if (currentspace != IntPtr.Zero && d.SpaceQuery(currentspace, geom))
1462 {
1463 if (d.GeomIsSpace(currentspace))
1464 {
1465 waitForSpaceUnlock(currentspace);
1466 d.SpaceRemove(currentspace, geom);
1467
1468 if (d.SpaceGetSublevel(currentspace) > 2 && d.SpaceGetNumGeoms(currentspace) == 0)
1469 {
1470 d.SpaceDestroy(currentspace);
1471 }
1472 }
1473 else
1474 {
1475 m_log.Info("[Physics]: Invalid or empty Space passed to 'MoveGeomToStaticSpace':" + currentspace +
1476 " Geom:" + geom);
1477 }
1478 }
1479 else // odd currentspace is null or doesn't contain the geom? lets try the geom ideia of current space
1480 {
1481 currentspace = d.GeomGetSpace(geom);
1482 if (currentspace != IntPtr.Zero)
1483 {
1484 if (d.GeomIsSpace(currentspace))
1485 {
1486 waitForSpaceUnlock(currentspace);
1487 d.SpaceRemove(currentspace, geom);
1488
1489 if (d.SpaceGetSublevel(currentspace) > 2 && d.SpaceGetNumGeoms(currentspace) == 0)
1490 {
1491 d.SpaceDestroy(currentspace);
1492 }
1493
1494 }
1495 }
1496 }
1497
1498 // put the geom in the newspace
1499 waitForSpaceUnlock(newspace);
1500 d.SpaceAdd(newspace, geom);
1501
1502 // let caller know this newspace
1503 return newspace;
1504 }
1505
1506 /// <summary>
1507 /// Calculates the space the prim should be in by its position
1508 /// </summary>
1509 /// <param name="pos"></param>
1510 /// <returns>a pointer to the space. This could be a new space or reused space.</returns>
1511 public IntPtr calculateSpaceForGeom(Vector3 pos)
1512 {
1513 int x, y;
1514
1515 if (pos.X < 0)
1516 return staticPrimspaceOffRegion[0];
1517
1518 if (pos.Y < 0)
1519 return staticPrimspaceOffRegion[2];
1520
1521 x = (int)(pos.X * spacesPerMeter);
1522 if (x > spaceGridMaxX)
1523 return staticPrimspaceOffRegion[1];
1524
1525 y = (int)(pos.Y * spacesPerMeter);
1526 if (y > spaceGridMaxY)
1527 return staticPrimspaceOffRegion[3];
1528
1529 return staticPrimspace[x, y];
1530 }
1531
1532 #endregion
1533
1534
1535 /// <summary>
1536 /// Called to queue a change to a actor
1537 /// to use in place of old taint mechanism so changes do have a time sequence
1538 /// </summary>
1539
1540 public void AddChange(PhysicsActor actor, changes what, Object arg)
1541 {
1542 ODEchangeitem item = new ODEchangeitem();
1543 item.actor = actor;
1544 item.what = what;
1545 item.arg = arg;
1546 ChangesQueue.Enqueue(item);
1547 }
1548
1549 /// <summary>
1550 /// Called after our prim properties are set Scale, position etc.
1551 /// We use this event queue like method to keep changes to the physical scene occuring in the threadlocked mutex
1552 /// This assures us that we have no race conditions
1553 /// </summary>
1554 /// <param name="prim"></param>
1555 public override void AddPhysicsActorTaint(PhysicsActor prim)
1556 {
1557 }
1558
1559 // does all pending changes generated during region load process
1560 public override void PrepareSimulation()
1561 {
1562 lock (OdeLock)
1563 {
1564 if (world == IntPtr.Zero)
1565 {
1566 ChangesQueue.Clear();
1567 return;
1568 }
1569
1570 ODEchangeitem item;
1571
1572 int donechanges = 0;
1573 if (ChangesQueue.Count > 0)
1574 {
1575 m_log.InfoFormat("[ODE] start processing pending actor operations");
1576 int tstart = Util.EnvironmentTickCount();
1577
1578 while (ChangesQueue.Dequeue(out item))
1579 {
1580 if (item.actor != null)
1581 {
1582 try
1583 {
1584 if (item.actor is OdeCharacter)
1585 ((OdeCharacter)item.actor).DoAChange(item.what, item.arg);
1586 else if (((OdePrim)item.actor).DoAChange(item.what, item.arg))
1587 RemovePrimThreadLocked((OdePrim)item.actor);
1588 }
1589 catch
1590 {
1591 m_log.WarnFormat("[PHYSICS]: Operation failed for a actor {0} {1}",
1592 item.actor.Name, item.what.ToString());
1593 }
1594 }
1595 donechanges++;
1596 }
1597 int time = Util.EnvironmentTickCountSubtract(tstart);
1598 m_log.InfoFormat("[ODE] finished {0} operations in {1}ms", donechanges, time);
1599 }
1600 }
1601 }
1602
1603 /// <summary>
1604 /// This is our main simulate loop
1605 /// It's thread locked by a Mutex in the scene.
1606 /// It holds Collisions, it instructs ODE to step through the physical reactions
1607 /// It moves the objects around in memory
1608 /// It calls the methods that report back to the object owners.. (scenepresence, SceneObjectGroup)
1609 /// </summary>
1610 /// <param name="timeStep"></param>
1611 /// <returns></returns>
1612 public override float Simulate(float timeStep)
1613 {
1614 DateTime now = DateTime.UtcNow;
1615 TimeSpan timedif = now - m_lastframe;
1616 timeStep = (float)timedif.TotalSeconds;
1617 m_lastframe = now;
1618
1619 // acumulate time so we can reduce error
1620 step_time += timeStep;
1621
1622 if (step_time < HalfOdeStep)
1623 return 0;
1624
1625 if (framecount < 0)
1626 framecount = 0;
1627
1628
1629 framecount++;
1630
1631 int curphysiteractions;
1632
1633 // if in trouble reduce step resolution
1634 if (step_time >= m_SkipFramesAtms)
1635 curphysiteractions = m_physicsiterations / 2;
1636 else
1637 curphysiteractions = m_physicsiterations;
1638
1639// checkThread();
1640 int nodeframes = 0;
1641
1642 lock (SimulationLock)
1643 lock(OdeLock)
1644 {
1645 if (world == IntPtr.Zero)
1646 {
1647 ChangesQueue.Clear();
1648 return 0;
1649 }
1650
1651 ODEchangeitem item;
1652
1653
1654
1655 d.WorldSetQuickStepNumIterations(world, curphysiteractions);
1656
1657 int loopstartMS = Util.EnvironmentTickCount();
1658 int looptimeMS = 0;
1659
1660
1661 while (step_time > HalfOdeStep)
1662 {
1663 try
1664 {
1665 // clear pointer/counter to contacts to pass into joints
1666 m_global_contactcount = 0;
1667
1668 if (ChangesQueue.Count > 0)
1669 {
1670 int changestartMS = Util.EnvironmentTickCount();
1671 int ttmp;
1672 while (ChangesQueue.Dequeue(out item))
1673 {
1674 if (item.actor != null)
1675 {
1676 try
1677 {
1678 if (item.actor is OdeCharacter)
1679 ((OdeCharacter)item.actor).DoAChange(item.what, item.arg);
1680 else if (((OdePrim)item.actor).DoAChange(item.what, item.arg))
1681 RemovePrimThreadLocked((OdePrim)item.actor);
1682 }
1683 catch
1684 {
1685 m_log.WarnFormat("[PHYSICS]: doChange failed for a actor {0} {1}",
1686 item.actor.Name, item.what.ToString());
1687 }
1688 }
1689 ttmp = Util.EnvironmentTickCountSubtract(changestartMS);
1690 if (ttmp > 20)
1691 break;
1692 }
1693 }
1694
1695 // Move characters
1696 lock (_characters)
1697 {
1698 List<OdeCharacter> defects = new List<OdeCharacter>();
1699 foreach (OdeCharacter actor in _characters)
1700 {
1701 if (actor != null)
1702 actor.Move(defects);
1703 }
1704 if (defects.Count != 0)
1705 {
1706 foreach (OdeCharacter defect in defects)
1707 {
1708 RemoveCharacter(defect);
1709 }
1710 defects.Clear();
1711 }
1712 }
1713
1714 // Move other active objects
1715 lock (_activegroups)
1716 {
1717 foreach (OdePrim aprim in _activegroups)
1718 {
1719 aprim.Move();
1720 }
1721 }
1722
1723 //if ((framecount % m_randomizeWater) == 0)
1724 // randomizeWater(waterlevel);
1725
1726 m_rayCastManager.ProcessQueuedRequests();
1727
1728 collision_optimized();
1729
1730 foreach (PhysicsActor obj in _collisionEventPrim)
1731 {
1732 if (obj == null)
1733 continue;
1734
1735 switch ((ActorTypes)obj.PhysicsActorType)
1736 {
1737 case ActorTypes.Agent:
1738 OdeCharacter cobj = (OdeCharacter)obj;
1739 cobj.AddCollisionFrameTime((int)(odetimestepMS));
1740 cobj.SendCollisions();
1741 break;
1742
1743 case ActorTypes.Prim:
1744 OdePrim pobj = (OdePrim)obj;
1745 if (pobj.Body == IntPtr.Zero || (d.BodyIsEnabled(pobj.Body) && !pobj.m_outbounds))
1746 if (!pobj.m_outbounds)
1747 {
1748 pobj.AddCollisionFrameTime((int)(odetimestepMS));
1749 pobj.SendCollisions();
1750 }
1751 break;
1752 }
1753 }
1754
1755 foreach (PhysicsActor obj in _collisionEventPrimRemove)
1756 _collisionEventPrim.Remove(obj);
1757
1758 _collisionEventPrimRemove.Clear();
1759
1760 // do a ode simulation step
1761 d.WorldQuickStep(world, ODE_STEPSIZE);
1762 d.JointGroupEmpty(contactgroup);
1763
1764 // update managed ideia of physical data and do updates to core
1765 /*
1766 lock (_characters)
1767 {
1768 foreach (OdeCharacter actor in _characters)
1769 {
1770 if (actor != null)
1771 {
1772 if (actor.bad)
1773 m_log.WarnFormat("[PHYSICS]: BAD Actor {0} in _characters list was not removed?", actor.m_uuid);
1774
1775 actor.UpdatePositionAndVelocity();
1776 }
1777 }
1778 }
1779 */
1780
1781 lock (_activegroups)
1782 {
1783 {
1784 foreach (OdePrim actor in _activegroups)
1785 {
1786 if (actor.IsPhysical)
1787 {
1788 actor.UpdatePositionAndVelocity();
1789 }
1790 }
1791 }
1792 }
1793 }
1794 catch (Exception e)
1795 {
1796 m_log.ErrorFormat("[PHYSICS]: {0}, {1}, {2}", e.Message, e.TargetSite, e);
1797// ode.dunlock(world);
1798 }
1799
1800 step_time -= ODE_STEPSIZE;
1801 nodeframes++;
1802
1803 looptimeMS = Util.EnvironmentTickCountSubtract(loopstartMS);
1804 if (looptimeMS > 100)
1805 break;
1806 }
1807
1808 lock (_badCharacter)
1809 {
1810 if (_badCharacter.Count > 0)
1811 {
1812 foreach (OdeCharacter chr in _badCharacter)
1813 {
1814 RemoveCharacter(chr);
1815 }
1816
1817 _badCharacter.Clear();
1818 }
1819 }
1820
1821 timedif = now - m_lastMeshExpire;
1822
1823 if (timedif.Seconds > 10)
1824 {
1825 mesher.ExpireReleaseMeshs();
1826 m_lastMeshExpire = now;
1827 }
1828
1829// information block running in debug only
1830/*
1831 int ntopactivegeoms = d.SpaceGetNumGeoms(ActiveSpace);
1832 int ntopstaticgeoms = d.SpaceGetNumGeoms(StaticSpace);
1833 int ngroundgeoms = d.SpaceGetNumGeoms(GroundSpace);
1834
1835 int nactivegeoms = 0;
1836 int nactivespaces = 0;
1837
1838 int nstaticgeoms = 0;
1839 int nstaticspaces = 0;
1840 IntPtr sp;
1841
1842 for (int i = 0; i < ntopactivegeoms; i++)
1843 {
1844 sp = d.SpaceGetGeom(ActiveSpace, i);
1845 if (d.GeomIsSpace(sp))
1846 {
1847 nactivespaces++;
1848 nactivegeoms += d.SpaceGetNumGeoms(sp);
1849 }
1850 else
1851 nactivegeoms++;
1852 }
1853
1854 for (int i = 0; i < ntopstaticgeoms; i++)
1855 {
1856 sp = d.SpaceGetGeom(StaticSpace, i);
1857 if (d.GeomIsSpace(sp))
1858 {
1859 nstaticspaces++;
1860 nstaticgeoms += d.SpaceGetNumGeoms(sp);
1861 }
1862 else
1863 nstaticgeoms++;
1864 }
1865
1866 int ntopgeoms = d.SpaceGetNumGeoms(TopSpace);
1867
1868 int totgeoms = nstaticgeoms + nactivegeoms + ngroundgeoms + 1; // one ray
1869 int nbodies = d.NTotalBodies;
1870 int ngeoms = d.NTotalGeoms;
1871*/
1872 // Finished with all sim stepping. If requested, dump world state to file for debugging.
1873 // TODO: This call to the export function is already inside lock (OdeLock) - but is an extra lock needed?
1874 // TODO: This overwrites all dump files in-place. Should this be a growing logfile, or separate snapshots?
1875 if (physics_logging && (physics_logging_interval > 0) && (framecount % physics_logging_interval == 0))
1876 {
1877 string fname = "state-" + world.ToString() + ".DIF"; // give each physics world a separate filename
1878 string prefix = "world" + world.ToString(); // prefix for variable names in exported .DIF file
1879
1880 if (physics_logging_append_existing_logfile)
1881 {
1882 string header = "-------------- START OF PHYSICS FRAME " + framecount.ToString() + " --------------";
1883 TextWriter fwriter = File.AppendText(fname);
1884 fwriter.WriteLine(header);
1885 fwriter.Close();
1886 }
1887
1888 d.WorldExportDIF(world, fname, physics_logging_append_existing_logfile, prefix);
1889 }
1890
1891 // think time dilation as to do with dinamic step size that we dont' have
1892 // even so tell something to world
1893 if (looptimeMS < 100) // we did the requested loops
1894 m_timeDilation = 1.0f;
1895 else if (step_time > 0)
1896 {
1897 m_timeDilation = timeStep / step_time;
1898 if (m_timeDilation > 1)
1899 m_timeDilation = 1;
1900 if (step_time > m_SkipFramesAtms)
1901 step_time = 0;
1902 m_lastframe = DateTime.UtcNow; // skip also the time lost
1903 }
1904 }
1905
1906// return nodeframes * ODE_STEPSIZE; // return real simulated time
1907 return 1000 * nodeframes; // return steps for now * 1000 to keep core happy
1908 }
1909
1910 /// <summary>
1911 public override void GetResults()
1912 {
1913 }
1914
1915 public override bool IsThreaded
1916 {
1917 // for now we won't be multithreaded
1918 get { return (false); }
1919 }
1920
1921 public float GetTerrainHeightAtXY(float x, float y)
1922 {
1923
1924
1925 int offsetX = ((int)(x / (int)Constants.RegionSize)) * (int)Constants.RegionSize;
1926 int offsetY = ((int)(y / (int)Constants.RegionSize)) * (int)Constants.RegionSize;
1927
1928
1929 IntPtr heightFieldGeom = IntPtr.Zero;
1930
1931 // get region map
1932 if (!RegionTerrain.TryGetValue(new Vector3(offsetX, offsetY, 0), out heightFieldGeom))
1933 return 0f;
1934
1935 if (heightFieldGeom == IntPtr.Zero)
1936 return 0f;
1937
1938 if (!TerrainHeightFieldHeights.ContainsKey(heightFieldGeom))
1939 return 0f;
1940
1941 // TerrainHeightField for ODE as offset 1m
1942 x += 1f - offsetX;
1943 y += 1f - offsetY;
1944
1945 // make position fit into array
1946 if (x < 0)
1947 x = 0;
1948 if (y < 0)
1949 y = 0;
1950
1951 // integer indexs
1952 int ix;
1953 int iy;
1954 // interpolators offset
1955 float dx;
1956 float dy;
1957
1958 int regsize = (int)Constants.RegionSize + 3; // map size see setterrain number of samples
1959
1960 if (OdeUbitLib)
1961 {
1962 if (x < regsize - 1)
1963 {
1964 ix = (int)x;
1965 dx = x - (float)ix;
1966 }
1967 else // out world use external height
1968 {
1969 ix = regsize - 2;
1970 dx = 0;
1971 }
1972 if (y < regsize - 1)
1973 {
1974 iy = (int)y;
1975 dy = y - (float)iy;
1976 }
1977 else
1978 {
1979 iy = regsize - 2;
1980 dy = 0;
1981 }
1982 }
1983
1984 else
1985 {
1986 // we still have square fixed size regions
1987 // also flip x and y because of how map is done for ODE fliped axis
1988 // so ix,iy,dx and dy are inter exchanged
1989 if (x < regsize - 1)
1990 {
1991 iy = (int)x;
1992 dy = x - (float)iy;
1993 }
1994 else // out world use external height
1995 {
1996 iy = regsize - 2;
1997 dy = 0;
1998 }
1999 if (y < regsize - 1)
2000 {
2001 ix = (int)y;
2002 dx = y - (float)ix;
2003 }
2004 else
2005 {
2006 ix = regsize - 2;
2007 dx = 0;
2008 }
2009 }
2010
2011 float h0;
2012 float h1;
2013 float h2;
2014
2015 iy *= regsize;
2016 iy += ix; // all indexes have iy + ix
2017
2018 float[] heights = TerrainHeightFieldHeights[heightFieldGeom];
2019 /*
2020 if ((dx + dy) <= 1.0f)
2021 {
2022 h0 = ((float)heights[iy]); // 0,0 vertice
2023 h1 = (((float)heights[iy + 1]) - h0) * dx; // 1,0 vertice minus 0,0
2024 h2 = (((float)heights[iy + regsize]) - h0) * dy; // 0,1 vertice minus 0,0
2025 }
2026 else
2027 {
2028 h0 = ((float)heights[iy + regsize + 1]); // 1,1 vertice
2029 h1 = (((float)heights[iy + 1]) - h0) * (1 - dy); // 1,1 vertice minus 1,0
2030 h2 = (((float)heights[iy + regsize]) - h0) * (1 - dx); // 1,1 vertice minus 0,1
2031 }
2032 */
2033 h0 = ((float)heights[iy]); // 0,0 vertice
2034
2035 if ((dy > dx))
2036 {
2037 iy += regsize;
2038 h2 = (float)heights[iy]; // 0,1 vertice
2039 h1 = (h2 - h0) * dy; // 0,1 vertice minus 0,0
2040 h2 = ((float)heights[iy + 1] - h2) * dx; // 1,1 vertice minus 0,1
2041 }
2042 else
2043 {
2044 iy++;
2045 h2 = (float)heights[iy]; // vertice 1,0
2046 h1 = (h2 - h0) * dx; // 1,0 vertice minus 0,0
2047 h2 = (((float)heights[iy + regsize]) - h2) * dy; // 1,1 vertice minus 1,0
2048 }
2049
2050 return h0 + h1 + h2;
2051 }
2052
2053
2054 public override void SetTerrain(float[] heightMap)
2055 {
2056 if (m_worldOffset != Vector3.Zero && m_parentScene != null)
2057 {
2058 if (m_parentScene is OdeScene)
2059 {
2060 ((OdeScene)m_parentScene).SetTerrain(heightMap, m_worldOffset);
2061 }
2062 }
2063 else
2064 {
2065 SetTerrain(heightMap, m_worldOffset);
2066 }
2067 }
2068
2069 public override void CombineTerrain(float[] heightMap, Vector3 pOffset)
2070 {
2071 SetTerrain(heightMap, pOffset);
2072 }
2073
2074 public void SetTerrain(float[] heightMap, Vector3 pOffset)
2075 {
2076 if (OdeUbitLib)
2077 UbitSetTerrain(heightMap, pOffset);
2078 else
2079 OriSetTerrain(heightMap, pOffset);
2080 }
2081
2082 public void OriSetTerrain(float[] heightMap, Vector3 pOffset)
2083 {
2084 // assumes 1m size grid and constante size square regions
2085 // needs to know about sims around in future
2086
2087 float[] _heightmap;
2088
2089 uint heightmapWidth = Constants.RegionSize + 2;
2090 uint heightmapHeight = Constants.RegionSize + 2;
2091
2092 uint heightmapWidthSamples = heightmapWidth + 1;
2093 uint heightmapHeightSamples = heightmapHeight + 1;
2094
2095 _heightmap = new float[heightmapWidthSamples * heightmapHeightSamples];
2096
2097 const float scale = 1.0f;
2098 const float offset = 0.0f;
2099 const float thickness = 10f;
2100 const int wrap = 0;
2101
2102 uint regionsize = Constants.RegionSize;
2103
2104 float hfmin = float.MaxValue;
2105 float hfmax = float.MinValue;
2106 float val;
2107 uint xx;
2108 uint yy;
2109
2110 uint maxXXYY = regionsize - 1;
2111 // flipping map adding one margin all around so things don't fall in edges
2112
2113 uint xt = 0;
2114 xx = 0;
2115
2116 for (uint x = 0; x < heightmapWidthSamples; x++)
2117 {
2118 if (x > 1 && xx < maxXXYY)
2119 xx++;
2120 yy = 0;
2121 for (uint y = 0; y < heightmapHeightSamples; y++)
2122 {
2123 if (y > 1 && y < maxXXYY)
2124 yy += regionsize;
2125
2126 val = heightMap[yy + xx];
2127 if (val < 0.0f)
2128 val = 0.0f; // no neg terrain as in chode
2129 _heightmap[xt + y] = val;
2130
2131 if (hfmin > val)
2132 hfmin = val;
2133 if (hfmax < val)
2134 hfmax = val;
2135 }
2136 xt += heightmapHeightSamples;
2137 }
2138 lock (OdeLock)
2139 {
2140 IntPtr GroundGeom = IntPtr.Zero;
2141 if (RegionTerrain.TryGetValue(pOffset, out GroundGeom))
2142 {
2143 RegionTerrain.Remove(pOffset);
2144 if (GroundGeom != IntPtr.Zero)
2145 {
2146 actor_name_map.Remove(GroundGeom);
2147 d.GeomDestroy(GroundGeom);
2148
2149 if (TerrainHeightFieldHeights.ContainsKey(GroundGeom))
2150 {
2151 TerrainHeightFieldHeightsHandlers[GroundGeom].Free();
2152 TerrainHeightFieldHeightsHandlers.Remove(GroundGeom);
2153 TerrainHeightFieldHeights.Remove(GroundGeom);
2154 }
2155 }
2156 }
2157 IntPtr HeightmapData = d.GeomHeightfieldDataCreate();
2158
2159 GCHandle _heightmaphandler = GCHandle.Alloc(_heightmap, GCHandleType.Pinned);
2160
2161 d.GeomHeightfieldDataBuildSingle(HeightmapData, _heightmaphandler.AddrOfPinnedObject(), 0, heightmapWidth , heightmapHeight,
2162 (int)heightmapWidthSamples, (int)heightmapHeightSamples, scale,
2163 offset, thickness, wrap);
2164
2165 d.GeomHeightfieldDataSetBounds(HeightmapData, hfmin - 1, hfmax + 1);
2166
2167 GroundGeom = d.CreateHeightfield(GroundSpace, HeightmapData, 1);
2168
2169 if (GroundGeom != IntPtr.Zero)
2170 {
2171 d.GeomSetCategoryBits(GroundGeom, (uint)(CollisionCategories.Land));
2172 d.GeomSetCollideBits(GroundGeom, 0);
2173
2174 PhysicsActor pa = new NullPhysicsActor();
2175 pa.Name = "Terrain";
2176 pa.PhysicsActorType = (int)ActorTypes.Ground;
2177 actor_name_map[GroundGeom] = pa;
2178
2179// geom_name_map[GroundGeom] = "Terrain";
2180
2181 d.Matrix3 R = new d.Matrix3();
2182
2183 Quaternion q1 = Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), 1.5707f);
2184 Quaternion q2 = Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), 1.5707f);
2185
2186
2187 q1 = q1 * q2;
2188
2189 Vector3 v3;
2190 float angle;
2191 q1.GetAxisAngle(out v3, out angle);
2192
2193 d.RFromAxisAndAngle(out R, v3.X, v3.Y, v3.Z, angle);
2194 d.GeomSetRotation(GroundGeom, ref R);
2195 d.GeomSetPosition(GroundGeom, pOffset.X + (float)Constants.RegionSize * 0.5f, pOffset.Y + (float)Constants.RegionSize * 0.5f, 0);
2196 RegionTerrain.Add(pOffset, GroundGeom);
2197 TerrainHeightFieldHeights.Add(GroundGeom, _heightmap);
2198 TerrainHeightFieldHeightsHandlers.Add(GroundGeom, _heightmaphandler);
2199 }
2200 }
2201 }
2202
2203 public void UbitSetTerrain(float[] heightMap, Vector3 pOffset)
2204 {
2205 // assumes 1m size grid and constante size square regions
2206 // needs to know about sims around in future
2207
2208 float[] _heightmap;
2209
2210 uint heightmapWidth = Constants.RegionSize + 2;
2211 uint heightmapHeight = Constants.RegionSize + 2;
2212
2213 uint heightmapWidthSamples = heightmapWidth + 1;
2214 uint heightmapHeightSamples = heightmapHeight + 1;
2215
2216 _heightmap = new float[heightmapWidthSamples * heightmapHeightSamples];
2217
2218
2219 uint regionsize = Constants.RegionSize;
2220
2221 float hfmin = float.MaxValue;
2222// float hfmax = float.MinValue;
2223 float val;
2224
2225
2226 uint maxXXYY = regionsize - 1;
2227 // adding one margin all around so things don't fall in edges
2228
2229 uint xx;
2230 uint yy = 0;
2231 uint yt = 0;
2232
2233 for (uint y = 0; y < heightmapHeightSamples; y++)
2234 {
2235 if (y > 1 && y < maxXXYY)
2236 yy += regionsize;
2237 xx = 0;
2238 for (uint x = 0; x < heightmapWidthSamples; x++)
2239 {
2240 if (x > 1 && x < maxXXYY)
2241 xx++;
2242
2243 val = heightMap[yy + xx];
2244 if (val < 0.0f)
2245 val = 0.0f; // no neg terrain as in chode
2246 _heightmap[yt + x] = val;
2247
2248 if (hfmin > val)
2249 hfmin = val;
2250// if (hfmax < val)
2251// hfmax = val;
2252 }
2253 yt += heightmapWidthSamples;
2254 }
2255 lock (OdeLock)
2256 {
2257 IntPtr GroundGeom = IntPtr.Zero;
2258 if (RegionTerrain.TryGetValue(pOffset, out GroundGeom))
2259 {
2260 RegionTerrain.Remove(pOffset);
2261 if (GroundGeom != IntPtr.Zero)
2262 {
2263 actor_name_map.Remove(GroundGeom);
2264 d.GeomDestroy(GroundGeom);
2265
2266 if (TerrainHeightFieldHeights.ContainsKey(GroundGeom))
2267 {
2268 if (TerrainHeightFieldHeightsHandlers[GroundGeom].IsAllocated)
2269 TerrainHeightFieldHeightsHandlers[GroundGeom].Free();
2270 TerrainHeightFieldHeightsHandlers.Remove(GroundGeom);
2271 TerrainHeightFieldHeights.Remove(GroundGeom);
2272 }
2273 }
2274 }
2275 IntPtr HeightmapData = d.GeomUbitTerrainDataCreate();
2276
2277 const int wrap = 0;
2278 float thickness = hfmin;
2279 if (thickness < 0)
2280 thickness = 1;
2281
2282 GCHandle _heightmaphandler = GCHandle.Alloc(_heightmap, GCHandleType.Pinned);
2283
2284 d.GeomUbitTerrainDataBuild(HeightmapData, _heightmaphandler.AddrOfPinnedObject(), 0, 1.0f,
2285 (int)heightmapWidthSamples, (int)heightmapHeightSamples,
2286 thickness, wrap);
2287
2288// d.GeomUbitTerrainDataSetBounds(HeightmapData, hfmin - 1, hfmax + 1);
2289 GroundGeom = d.CreateUbitTerrain(GroundSpace, HeightmapData, 1);
2290 if (GroundGeom != IntPtr.Zero)
2291 {
2292 d.GeomSetCategoryBits(GroundGeom, (uint)(CollisionCategories.Land));
2293 d.GeomSetCollideBits(GroundGeom, 0);
2294
2295
2296 PhysicsActor pa = new NullPhysicsActor();
2297 pa.Name = "Terrain";
2298 pa.PhysicsActorType = (int)ActorTypes.Ground;
2299 actor_name_map[GroundGeom] = pa;
2300
2301// geom_name_map[GroundGeom] = "Terrain";
2302
2303 d.GeomSetPosition(GroundGeom, pOffset.X + (float)Constants.RegionSize * 0.5f, pOffset.Y + (float)Constants.RegionSize * 0.5f, 0);
2304 RegionTerrain.Add(pOffset, GroundGeom);
2305 TerrainHeightFieldHeights.Add(GroundGeom, _heightmap);
2306 TerrainHeightFieldHeightsHandlers.Add(GroundGeom, _heightmaphandler);
2307 }
2308 }
2309 }
2310
2311
2312 public override void DeleteTerrain()
2313 {
2314 }
2315
2316 public float GetWaterLevel()
2317 {
2318 return waterlevel;
2319 }
2320
2321 public override bool SupportsCombining()
2322 {
2323 return true;
2324 }
2325/*
2326 public override void UnCombine(PhysicsScene pScene)
2327 {
2328 IntPtr localGround = IntPtr.Zero;
2329// float[] localHeightfield;
2330 bool proceed = false;
2331 List<IntPtr> geomDestroyList = new List<IntPtr>();
2332
2333 lock (OdeLock)
2334 {
2335 if (RegionTerrain.TryGetValue(Vector3.Zero, out localGround))
2336 {
2337 foreach (IntPtr geom in TerrainHeightFieldHeights.Keys)
2338 {
2339 if (geom == localGround)
2340 {
2341// localHeightfield = TerrainHeightFieldHeights[geom];
2342 proceed = true;
2343 }
2344 else
2345 {
2346 geomDestroyList.Add(geom);
2347 }
2348 }
2349
2350 if (proceed)
2351 {
2352 m_worldOffset = Vector3.Zero;
2353 WorldExtents = new Vector2((int)Constants.RegionSize, (int)Constants.RegionSize);
2354 m_parentScene = null;
2355
2356 foreach (IntPtr g in geomDestroyList)
2357 {
2358 // removingHeightField needs to be done or the garbage collector will
2359 // collect the terrain data before we tell ODE to destroy it causing
2360 // memory corruption
2361 if (TerrainHeightFieldHeights.ContainsKey(g))
2362 {
2363// float[] removingHeightField = TerrainHeightFieldHeights[g];
2364 TerrainHeightFieldHeights.Remove(g);
2365
2366 if (RegionTerrain.ContainsKey(g))
2367 {
2368 RegionTerrain.Remove(g);
2369 }
2370
2371 d.GeomDestroy(g);
2372 //removingHeightField = new float[0];
2373 }
2374 }
2375
2376 }
2377 else
2378 {
2379 m_log.Warn("[PHYSICS]: Couldn't proceed with UnCombine. Region has inconsistant data.");
2380 }
2381 }
2382 }
2383 }
2384*/
2385 public override void SetWaterLevel(float baseheight)
2386 {
2387 waterlevel = baseheight;
2388// randomizeWater(waterlevel);
2389 }
2390/*
2391 public void randomizeWater(float baseheight)
2392 {
2393 const uint heightmapWidth = Constants.RegionSize + 2;
2394 const uint heightmapHeight = Constants.RegionSize + 2;
2395 const uint heightmapWidthSamples = heightmapWidth + 1;
2396 const uint heightmapHeightSamples = heightmapHeight + 1;
2397
2398 const float scale = 1.0f;
2399 const float offset = 0.0f;
2400 const int wrap = 0;
2401
2402 float[] _watermap = new float[heightmapWidthSamples * heightmapWidthSamples];
2403
2404 float maxheigh = float.MinValue;
2405 float minheigh = float.MaxValue;
2406 float val;
2407 for (int i = 0; i < (heightmapWidthSamples * heightmapHeightSamples); i++)
2408 {
2409
2410 val = (baseheight - 0.1f) + ((float)fluidRandomizer.Next(1, 9) / 10f);
2411 _watermap[i] = val;
2412 if (maxheigh < val)
2413 maxheigh = val;
2414 if (minheigh > val)
2415 minheigh = val;
2416 }
2417
2418 float thickness = minheigh;
2419
2420 lock (OdeLock)
2421 {
2422 if (WaterGeom != IntPtr.Zero)
2423 {
2424 actor_name_map.Remove(WaterGeom);
2425 d.GeomDestroy(WaterGeom);
2426 d.GeomHeightfieldDataDestroy(WaterHeightmapData);
2427 WaterGeom = IntPtr.Zero;
2428 WaterHeightmapData = IntPtr.Zero;
2429 if(WaterMapHandler.IsAllocated)
2430 WaterMapHandler.Free();
2431 }
2432
2433 WaterHeightmapData = d.GeomHeightfieldDataCreate();
2434
2435 WaterMapHandler = GCHandle.Alloc(_watermap, GCHandleType.Pinned);
2436
2437 d.GeomHeightfieldDataBuildSingle(WaterHeightmapData, WaterMapHandler.AddrOfPinnedObject(), 0, heightmapWidth, heightmapHeight,
2438 (int)heightmapWidthSamples, (int)heightmapHeightSamples, scale,
2439 offset, thickness, wrap);
2440 d.GeomHeightfieldDataSetBounds(WaterHeightmapData, minheigh, maxheigh);
2441 WaterGeom = d.CreateHeightfield(StaticSpace, WaterHeightmapData, 1);
2442 if (WaterGeom != IntPtr.Zero)
2443 {
2444 d.GeomSetCategoryBits(WaterGeom, (uint)(CollisionCategories.Water));
2445 d.GeomSetCollideBits(WaterGeom, 0);
2446
2447
2448 PhysicsActor pa = new NullPhysicsActor();
2449 pa.Name = "Water";
2450 pa.PhysicsActorType = (int)ActorTypes.Water;
2451
2452 actor_name_map[WaterGeom] = pa;
2453// geom_name_map[WaterGeom] = "Water";
2454
2455 d.Matrix3 R = new d.Matrix3();
2456
2457 Quaternion q1 = Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), 1.5707f);
2458 Quaternion q2 = Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), 1.5707f);
2459
2460 q1 = q1 * q2;
2461 Vector3 v3;
2462 float angle;
2463 q1.GetAxisAngle(out v3, out angle);
2464
2465 d.RFromAxisAndAngle(out R, v3.X, v3.Y, v3.Z, angle);
2466 d.GeomSetRotation(WaterGeom, ref R);
2467 d.GeomSetPosition(WaterGeom, (float)Constants.RegionSize * 0.5f, (float)Constants.RegionSize * 0.5f, 0);
2468 }
2469 }
2470 }
2471*/
2472 public override void Dispose()
2473 {
2474 if (m_meshWorker != null)
2475 m_meshWorker.Stop();
2476
2477 lock (OdeLock)
2478 {
2479 m_rayCastManager.Dispose();
2480 m_rayCastManager = null;
2481
2482 lock (_prims)
2483 {
2484 ChangesQueue.Clear();
2485 foreach (OdePrim prm in _prims)
2486 {
2487 prm.DoAChange(changes.Remove, null);
2488 _collisionEventPrim.Remove(prm);
2489 }
2490 _prims.Clear();
2491 }
2492
2493 OdeCharacter[] chtorem;
2494 lock (_characters)
2495 {
2496 chtorem = new OdeCharacter[_characters.Count];
2497 _characters.CopyTo(chtorem);
2498 }
2499
2500 ChangesQueue.Clear();
2501 foreach (OdeCharacter ch in chtorem)
2502 ch.DoAChange(changes.Remove, null);
2503
2504
2505 foreach (IntPtr GroundGeom in RegionTerrain.Values)
2506 {
2507 if (GroundGeom != IntPtr.Zero)
2508 d.GeomDestroy(GroundGeom);
2509 }
2510
2511
2512 RegionTerrain.Clear();
2513
2514 if (TerrainHeightFieldHeightsHandlers.Count > 0)
2515 {
2516 foreach (GCHandle gch in TerrainHeightFieldHeightsHandlers.Values)
2517 {
2518 if (gch.IsAllocated)
2519 gch.Free();
2520 }
2521 }
2522
2523 TerrainHeightFieldHeightsHandlers.Clear();
2524 TerrainHeightFieldHeights.Clear();
2525/*
2526 if (WaterGeom != IntPtr.Zero)
2527 {
2528 d.GeomDestroy(WaterGeom);
2529 WaterGeom = IntPtr.Zero;
2530 if (WaterHeightmapData != IntPtr.Zero)
2531 d.GeomHeightfieldDataDestroy(WaterHeightmapData);
2532 WaterHeightmapData = IntPtr.Zero;
2533
2534 if (WaterMapHandler.IsAllocated)
2535 WaterMapHandler.Free();
2536 }
2537*/
2538 if (ContactgeomsArray != IntPtr.Zero)
2539 Marshal.FreeHGlobal(ContactgeomsArray);
2540 if (GlobalContactsArray != IntPtr.Zero)
2541 Marshal.FreeHGlobal(GlobalContactsArray);
2542
2543
2544 d.WorldDestroy(world);
2545 world = IntPtr.Zero;
2546 //d.CloseODE();
2547 }
2548 }
2549
2550 public override Dictionary<uint, float> GetTopColliders()
2551 {
2552 Dictionary<uint, float> returncolliders = new Dictionary<uint, float>();
2553 int cnt = 0;
2554 lock (_prims)
2555 {
2556 foreach (OdePrim prm in _prims)
2557 {
2558 if (prm.CollisionScore > 0)
2559 {
2560 returncolliders.Add(prm.LocalID, prm.CollisionScore);
2561 cnt++;
2562 prm.CollisionScore = 0f;
2563 if (cnt > 25)
2564 {
2565 break;
2566 }
2567 }
2568 }
2569 }
2570 return returncolliders;
2571 }
2572
2573 public override bool SupportsRayCast()
2574 {
2575 return true;
2576 }
2577
2578 public override void RaycastWorld(Vector3 position, Vector3 direction, float length, RaycastCallback retMethod)
2579 {
2580 if (retMethod != null)
2581 {
2582 ODERayRequest req = new ODERayRequest();
2583 req.actor = null;
2584 req.callbackMethod = retMethod;
2585 req.length = length;
2586 req.Normal = direction;
2587 req.Origin = position;
2588 req.Count = 0;
2589 req.filter = RayFilterFlags.AllPrims;
2590
2591 m_rayCastManager.QueueRequest(req);
2592 }
2593 }
2594
2595 public override void RaycastWorld(Vector3 position, Vector3 direction, float length, int Count, RayCallback retMethod)
2596 {
2597 if (retMethod != null)
2598 {
2599 ODERayRequest req = new ODERayRequest();
2600 req.actor = null;
2601 req.callbackMethod = retMethod;
2602 req.length = length;
2603 req.Normal = direction;
2604 req.Origin = position;
2605 req.Count = Count;
2606 req.filter = RayFilterFlags.AllPrims;
2607
2608 m_rayCastManager.QueueRequest(req);
2609 }
2610 }
2611
2612
2613 public override List<ContactResult> RaycastWorld(Vector3 position, Vector3 direction, float length, int Count)
2614 {
2615 List<ContactResult> ourresults = new List<ContactResult>();
2616 object SyncObject = new object();
2617
2618 RayCallback retMethod = delegate(List<ContactResult> results)
2619 {
2620 lock (SyncObject)
2621 {
2622 ourresults = results;
2623 Monitor.PulseAll(SyncObject);
2624 }
2625 };
2626
2627 ODERayRequest req = new ODERayRequest();
2628 req.actor = null;
2629 req.callbackMethod = retMethod;
2630 req.length = length;
2631 req.Normal = direction;
2632 req.Origin = position;
2633 req.Count = Count;
2634 req.filter = RayFilterFlags.AllPrims;
2635
2636 lock (SyncObject)
2637 {
2638 m_rayCastManager.QueueRequest(req);
2639 if (!Monitor.Wait(SyncObject, 500))
2640 return null;
2641 else
2642 return ourresults;
2643 }
2644 }
2645
2646 public override bool SupportsRaycastWorldFiltered()
2647 {
2648 return true;
2649 }
2650
2651 public override object RaycastWorld(Vector3 position, Vector3 direction, float length, int Count, RayFilterFlags filter)
2652 {
2653 object SyncObject = new object();
2654 List<ContactResult> ourresults = new List<ContactResult>();
2655
2656 RayCallback retMethod = delegate(List<ContactResult> results)
2657 {
2658 lock (SyncObject)
2659 {
2660 ourresults = results;
2661 Monitor.PulseAll(SyncObject);
2662 }
2663 };
2664
2665 ODERayRequest req = new ODERayRequest();
2666 req.actor = null;
2667 req.callbackMethod = retMethod;
2668 req.length = length;
2669 req.Normal = direction;
2670 req.Origin = position;
2671 req.Count = Count;
2672 req.filter = filter;
2673
2674 lock (SyncObject)
2675 {
2676 m_rayCastManager.QueueRequest(req);
2677 if (!Monitor.Wait(SyncObject, 500))
2678 return null;
2679 else
2680 return ourresults;
2681 }
2682 }
2683
2684 public override List<ContactResult> RaycastActor(PhysicsActor actor, Vector3 position, Vector3 direction, float length, int Count, RayFilterFlags flags)
2685 {
2686 if (actor == null)
2687 return new List<ContactResult>();
2688
2689 IntPtr geom;
2690 if (actor is OdePrim)
2691 geom = ((OdePrim)actor).prim_geom;
2692 else if (actor is OdeCharacter)
2693 geom = ((OdePrim)actor).prim_geom;
2694 else
2695 return new List<ContactResult>();
2696
2697 if (geom == IntPtr.Zero)
2698 return new List<ContactResult>();
2699
2700 List<ContactResult> ourResults = null;
2701 object SyncObject = new object();
2702
2703 RayCallback retMethod = delegate(List<ContactResult> results)
2704 {
2705 lock (SyncObject)
2706 {
2707 ourResults = results;
2708 Monitor.PulseAll(SyncObject);
2709 }
2710 };
2711
2712 ODERayRequest req = new ODERayRequest();
2713 req.actor = actor;
2714 req.callbackMethod = retMethod;
2715 req.length = length;
2716 req.Normal = direction;
2717 req.Origin = position;
2718 req.Count = Count;
2719 req.filter = flags;
2720
2721 lock (SyncObject)
2722 {
2723 m_rayCastManager.QueueRequest(req);
2724 if (!Monitor.Wait(SyncObject, 500))
2725 return new List<ContactResult>();
2726 }
2727
2728 if (ourResults == null)
2729 return new List<ContactResult>();
2730 return ourResults;
2731 }
2732
2733 public override List<ContactResult> BoxProbe(Vector3 position, Vector3 size, Quaternion orientation, int Count, RayFilterFlags flags)
2734 {
2735 List<ContactResult> ourResults = null;
2736 object SyncObject = new object();
2737
2738 ProbeBoxCallback retMethod = delegate(List<ContactResult> results)
2739 {
2740 lock (SyncObject)
2741 {
2742 ourResults = results;
2743 Monitor.PulseAll(SyncObject);
2744 }
2745 };
2746
2747 ODERayRequest req = new ODERayRequest();
2748 req.actor = null;
2749 req.callbackMethod = retMethod;
2750 req.Normal = size;
2751 req.Origin = position;
2752 req.orientation = orientation;
2753 req.Count = Count;
2754 req.filter = flags;
2755
2756 lock (SyncObject)
2757 {
2758 m_rayCastManager.QueueRequest(req);
2759 if (!Monitor.Wait(SyncObject, 500))
2760 return new List<ContactResult>();
2761 }
2762
2763 if (ourResults == null)
2764 return new List<ContactResult>();
2765 return ourResults;
2766 }
2767
2768 public override List<ContactResult> SphereProbe(Vector3 position, float radius, int Count, RayFilterFlags flags)
2769 {
2770 List<ContactResult> ourResults = null;
2771 object SyncObject = new object();
2772
2773 ProbeSphereCallback retMethod = delegate(List<ContactResult> results)
2774 {
2775 ourResults = results;
2776 Monitor.PulseAll(SyncObject);
2777 };
2778
2779 ODERayRequest req = new ODERayRequest();
2780 req.actor = null;
2781 req.callbackMethod = retMethod;
2782 req.length = radius;
2783 req.Origin = position;
2784 req.Count = Count;
2785 req.filter = flags;
2786
2787
2788 lock (SyncObject)
2789 {
2790 m_rayCastManager.QueueRequest(req);
2791 if (!Monitor.Wait(SyncObject, 500))
2792 return new List<ContactResult>();
2793 }
2794
2795 if (ourResults == null)
2796 return new List<ContactResult>();
2797 return ourResults;
2798 }
2799
2800 public override List<ContactResult> PlaneProbe(PhysicsActor actor, Vector4 plane, int Count, RayFilterFlags flags)
2801 {
2802 IntPtr geom = IntPtr.Zero;;
2803
2804 if (actor != null)
2805 {
2806 if (actor is OdePrim)
2807 geom = ((OdePrim)actor).prim_geom;
2808 else if (actor is OdeCharacter)
2809 geom = ((OdePrim)actor).prim_geom;
2810 }
2811
2812 List<ContactResult> ourResults = null;
2813 object SyncObject = new object();
2814
2815 ProbePlaneCallback retMethod = delegate(List<ContactResult> results)
2816 {
2817 ourResults = results;
2818 Monitor.PulseAll(SyncObject);
2819 };
2820
2821 ODERayRequest req = new ODERayRequest();
2822 req.actor = null;
2823 req.callbackMethod = retMethod;
2824 req.length = plane.W;
2825 req.Normal.X = plane.X;
2826 req.Normal.Y = plane.Y;
2827 req.Normal.Z = plane.Z;
2828 req.Count = Count;
2829 req.filter = flags;
2830
2831 lock (SyncObject)
2832 {
2833 m_rayCastManager.QueueRequest(req);
2834 if (!Monitor.Wait(SyncObject, 500))
2835 return new List<ContactResult>();
2836 }
2837
2838 if (ourResults == null)
2839 return new List<ContactResult>();
2840 return ourResults;
2841 }
2842
2843 public override int SitAvatar(PhysicsActor actor, Vector3 AbsolutePosition, Vector3 CameraPosition, Vector3 offset, Vector3 AvatarSize, SitAvatarCallback PhysicsSitResponse)
2844 {
2845 Util.FireAndForget( delegate
2846 {
2847 ODESitAvatar sitAvatar = new ODESitAvatar(this, m_rayCastManager);
2848 if(sitAvatar != null)
2849 sitAvatar.Sit(actor, AbsolutePosition, CameraPosition, offset, AvatarSize, PhysicsSitResponse);
2850 });
2851 return 1;
2852 }
2853
2854 }
2855}