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