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