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