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