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