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