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