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