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