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.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..fa3d33e
--- /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.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 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 private 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 is OdePrim) && (((OdePrim)p1).m_isVolumeDetect) ||
750 (p2 is OdePrim) && (((OdePrim)p2).m_isVolumeDetect))
751 {
752 collision_accounting_events(p1, p2, maxDepthContact);
753 return;
754 }
755
756 // big messy collision analises
757 float mu = 0;
758 float bounce = 0;
759 float cfm = 0.0001f;
760 float erp = 0.1f;
761 float erpscale = 1.0f;
762 float dscale = 1.0f;
763 bool IgnoreNegSides = false;
764
765
766 ContactData contactdata1 = new ContactData(0, 0, false);
767 ContactData contactdata2 = new ContactData(0, 0, false);
768
769 String name = null;
770 bool dop1foot = false;
771 bool dop2foot = false;
772 bool ignore = false;
773
774 switch (p1.PhysicsActorType)
775 {
776 case (int)ActorTypes.Agent:
777 {
778 bounce = 0;
779 mu = 0;
780 cfm = 0.0001f;
781
782 switch (p2.PhysicsActorType)
783 {
784 case (int)ActorTypes.Agent:
785/*
786 p1.getContactData(ref contactdata1);
787 p2.getContactData(ref contactdata2);
788
789 mu = (float)Math.Sqrt(contactdata1.mu * contactdata2.mu);
790
791 if ((Math.Abs(p2.Velocity.X - p1.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y - p1.Velocity.Y) > 0.1f))
792 mu *= frictionMovementMult;
793*/
794 p1.CollidingObj = true;
795 p2.CollidingObj = true;
796 break;
797 case (int)ActorTypes.Prim:
798/*
799 p1.getContactData(ref contactdata1);
800 p2.getContactData(ref contactdata2);
801
802
803 mu = (float)Math.Sqrt(contactdata1.mu * contactdata2.mu);
804
805 if ((Math.Abs(p2.Velocity.X - p1.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y - p1.Velocity.Y) > 0.1f))
806 mu *= frictionMovementMult;
807 */
808 if (p2.Velocity.LengthSquared() > 0.0f)
809 p2.CollidingObj = true;
810
811 dop1foot = true;
812 break;
813 default:
814 ignore = true; // avatar to terrain and water ignored
815 break;
816 }
817 break;
818 }
819
820 case (int)ActorTypes.Prim:
821 switch (p2.PhysicsActorType)
822 {
823 case (int)ActorTypes.Agent:
824// p1.getContactData(ref contactdata1);
825// p2.getContactData(ref contactdata2);
826
827 bounce = 0;
828 mu = 0;
829 cfm = 0.0001f;
830/*
831 mu = (float)Math.Sqrt(contactdata1.mu * contactdata2.mu);
832
833 if ((Math.Abs(p2.Velocity.X - p1.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y - p1.Velocity.Y) > 0.1f))
834 mu *= frictionMovementMult;
835*/
836 dop2foot = true;
837 if (p1.Velocity.LengthSquared() > 0.0f)
838 p1.CollidingObj = true;
839 break;
840 case (int)ActorTypes.Prim:
841 if ((p1.Velocity - p2.Velocity).LengthSquared() > 0.0f)
842 {
843 p1.CollidingObj = true;
844 p2.CollidingObj = true;
845 }
846 p1.getContactData(ref contactdata1);
847 p2.getContactData(ref contactdata2);
848 bounce = contactdata1.bounce * contactdata2.bounce;
849 mu = (float)Math.Sqrt(contactdata1.mu * contactdata2.mu);
850
851 cfm = p1.Mass;
852 if (cfm > p2.Mass)
853 cfm = p2.Mass;
854 dscale = 10 / cfm;
855 dscale = (float)Math.Sqrt(dscale);
856 if (dscale > 1.0f)
857 dscale = 1.0f;
858 erpscale = cfm * 0.01f;
859 cfm = 0.0001f / cfm;
860 if (cfm > 0.01f)
861 cfm = 0.01f;
862
863 if ((Math.Abs(p2.Velocity.X - p1.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y - p1.Velocity.Y) > 0.1f))
864 mu *= frictionMovementMult;
865
866 break;
867 default:
868 if (geom_name_map.TryGetValue(g2, out name))
869 {
870 if (name == "Terrain")
871 {
872 p1.getContactData(ref contactdata1);
873 bounce = contactdata1.bounce * TerrainBounce;
874 mu = (float)Math.Sqrt(contactdata1.mu * TerrainFriction);
875 if (Math.Abs(p1.Velocity.X) > 0.1f || Math.Abs(p1.Velocity.Y) > 0.1f)
876 mu *= frictionMovementMult;
877 p1.CollidingGround = true;
878
879 cfm = p1.Mass;
880 dscale = 10 / cfm;
881 dscale = (float)Math.Sqrt(dscale);
882 if (dscale > 1.0f)
883 dscale = 1.0f;
884 erpscale = cfm * 0.01f;
885 cfm = 0.0001f / cfm;
886 if (cfm > 0.01f)
887 cfm = 0.01f;
888
889 if (d.GeomGetClass(g1) == d.GeomClassID.TriMeshClass)
890 {
891 if (curContact.side1 > 0)
892 IgnoreNegSides = true;
893 }
894
895 }
896 else if (name == "Water")
897 {
898 ignore = true;
899 }
900 }
901 else
902 ignore=true;
903 break;
904 }
905 break;
906
907 default:
908 if (geom_name_map.TryGetValue(g1, out name))
909 {
910 if (name == "Terrain")
911 {
912 if (p2.PhysicsActorType == (int)ActorTypes.Prim)
913 {
914 p2.CollidingGround = true;
915 p2.getContactData(ref contactdata2);
916 bounce = contactdata2.bounce * TerrainBounce;
917 mu = (float)Math.Sqrt(contactdata2.mu * TerrainFriction);
918
919 cfm = p2.Mass;
920 dscale = 10 / cfm;
921 dscale = (float)Math.Sqrt(dscale);
922
923 if (dscale > 1.0f)
924 dscale = 1.0f;
925
926 erpscale = cfm * 0.01f;
927 cfm = 0.0001f / cfm;
928 if (cfm > 0.01f)
929 cfm = 0.01f;
930
931 if (curContact.side1 > 0) // should be 2 ?
932 IgnoreNegSides = true;
933
934 if (Math.Abs(p2.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y) > 0.1f)
935 mu *= frictionMovementMult;
936 }
937 else
938 ignore = true;
939
940 }
941 else if (name == "Water" &&
942 (p2.PhysicsActorType == (int)ActorTypes.Prim || p2.PhysicsActorType == (int)ActorTypes.Agent))
943 {
944 ignore = true;
945 }
946 }
947 else
948 ignore = true;
949 break;
950 }
951
952 if (ignore)
953 return;
954
955 IntPtr Joint;
956
957 int i = 0;
958 while(true)
959 {
960
961 if (IgnoreNegSides && curContact.side1 < 0)
962 {
963 if (++i >= count)
964 break;
965
966 if (!GetCurContactGeom(i, ref curContact))
967 break;
968 }
969 else
970
971 {
972 if (dop1foot && (p1.Position.Z - curContact.pos.Z) > (p1.Size.Z - avCapRadius) * 0.5f)
973 p1.IsColliding = true;
974 if (dop2foot && (p2.Position.Z - curContact.pos.Z) > (p2.Size.Z - avCapRadius) * 0.5f)
975 p2.IsColliding = true;
976
977 Joint = CreateContacJoint(ref curContact, mu, bounce, cfm, erpscale, dscale);
978 d.JointAttach(Joint, b1, b2);
979
980 if (++m_global_contactcount >= maxContactsbeforedeath)
981 break;
982
983 if (++i >= count)
984 break;
985
986 if (!GetCurContactGeom(i, ref curContact))
987 break;
988
989 if (curContact.depth > maxDepthContact.PenetrationDepth)
990 {
991 maxDepthContact.Position.X = curContact.pos.X;
992 maxDepthContact.Position.Y = curContact.pos.Y;
993 maxDepthContact.Position.Z = curContact.pos.Z;
994 maxDepthContact.SurfaceNormal.X = curContact.normal.X;
995 maxDepthContact.SurfaceNormal.Y = curContact.normal.Y;
996 maxDepthContact.SurfaceNormal.Z = curContact.normal.Z;
997 maxDepthContact.PenetrationDepth = curContact.depth;
998 }
999 }
1000 }
1001
1002 collision_accounting_events(p1, p2, maxDepthContact);
1003
1004/*
1005 if (notskipedcount > geomContactPointsStartthrottle)
1006 {
1007 // If there are more then 3 contact points, it's likely
1008 // that we've got a pile of objects, so ...
1009 // We don't want to send out hundreds of terse updates over and over again
1010 // so lets throttle them and send them again after it's somewhat sorted out.
1011 this needs checking so out for now
1012 if (b1 != IntPtr.Zero)
1013 p1.ThrottleUpdates = true;
1014 if (b2 != IntPtr.Zero)
1015 p2.ThrottleUpdates = true;
1016
1017 }
1018 */
1019 }
1020
1021 private void collision_accounting_events(PhysicsActor p1, PhysicsActor p2, ContactPoint contact)
1022 {
1023 obj2LocalID = 0;
1024 bool p1events = p1.SubscribedEvents();
1025 bool p2events = p2.SubscribedEvents();
1026
1027 if (p1 is OdePrim && p1.IsVolumeDtc)
1028 p2events = false;
1029 if (p2 is OdePrim && p2.IsVolumeDtc)
1030 p1events = false;
1031
1032 if (!(p2events || p1events))
1033 return;
1034
1035 switch ((ActorTypes)p1.PhysicsActorType)
1036 {
1037 case ActorTypes.Agent:
1038 cc1 = (OdeCharacter)p1;
1039 switch ((ActorTypes)p2.PhysicsActorType)
1040 {
1041 case ActorTypes.Agent:
1042 cc2 = (OdeCharacter)p2;
1043 obj2LocalID = cc2.m_localID;
1044 if (p2events)
1045 cc2.AddCollisionEvent(cc1.m_localID, contact);
1046 break;
1047
1048 case ActorTypes.Prim:
1049 if (p2 is OdePrim)
1050 {
1051 cp2 = (OdePrim)p2;
1052 obj2LocalID = cp2.m_localID;
1053 if (p2events)
1054 cp2.AddCollisionEvent(cc1.m_localID, contact);
1055 }
1056 break;
1057
1058 case ActorTypes.Ground:
1059 case ActorTypes.Unknown:
1060 default:
1061 obj2LocalID = 0;
1062 break;
1063 }
1064 if (p1events)
1065 {
1066 contact.SurfaceNormal = -contact.SurfaceNormal;
1067 cc1.AddCollisionEvent(obj2LocalID, contact);
1068 }
1069 break;
1070
1071 case ActorTypes.Prim:
1072
1073 if (p1 is OdePrim)
1074 {
1075 cp1 = (OdePrim)p1;
1076
1077 // obj1LocalID = cp2.m_localID;
1078 switch ((ActorTypes)p2.PhysicsActorType)
1079 {
1080 case ActorTypes.Agent:
1081 if (p2 is OdeCharacter)
1082 {
1083 cc2 = (OdeCharacter)p2;
1084 obj2LocalID = cc2.m_localID;
1085 if (p2events)
1086 cc2.AddCollisionEvent(cp1.m_localID, contact);
1087 }
1088 break;
1089 case ActorTypes.Prim:
1090
1091 if (p2 is OdePrim)
1092 {
1093 cp2 = (OdePrim)p2;
1094 obj2LocalID = cp2.m_localID;
1095 if (p2events)
1096 cp2.AddCollisionEvent(cp1.m_localID, contact);
1097 }
1098 break;
1099
1100 case ActorTypes.Ground:
1101 case ActorTypes.Unknown:
1102 default:
1103 obj2LocalID = 0;
1104 break;
1105 }
1106 if (p1events)
1107 {
1108 contact.SurfaceNormal = -contact.SurfaceNormal;
1109 cp1.AddCollisionEvent(obj2LocalID, contact);
1110 }
1111 }
1112 break;
1113 }
1114 }
1115
1116 /// <summary>
1117 /// This is our collision testing routine in ODE
1118 /// </summary>
1119 /// <param name="timeStep"></param>
1120 private void collision_optimized()
1121 {
1122 lock (_characters)
1123 {
1124 try
1125 {
1126 foreach (OdeCharacter chr in _characters)
1127 {
1128 if (chr == null || chr.Shell == IntPtr.Zero || chr.Body == IntPtr.Zero)
1129 continue;
1130
1131 chr.IsColliding = false;
1132 // chr.CollidingGround = false; not done here
1133 chr.CollidingObj = false;
1134 // do colisions with static space
1135 d.SpaceCollide2(StaticSpace, chr.Shell, IntPtr.Zero, nearCallback);
1136 }
1137 }
1138 catch (AccessViolationException)
1139 {
1140 m_log.Warn("[PHYSICS]: Unable to collide Character to static space");
1141 }
1142
1143 }
1144
1145 lock (_activeprims)
1146 {
1147 foreach (OdePrim aprim in _activeprims)
1148 {
1149 aprim.CollisionScore = 0;
1150 aprim.IsColliding = false;
1151 }
1152 }
1153
1154 // collide active prims with static enviroment
1155 lock (_activegroups)
1156 {
1157 try
1158 {
1159 foreach (OdePrim prm in _activegroups)
1160 {
1161 if (d.BodyIsEnabled(prm.Body) && !prm.m_outbounds)
1162 d.SpaceCollide2(StaticSpace, prm.collide_geom, IntPtr.Zero, nearCallback);
1163 }
1164 }
1165 catch (AccessViolationException)
1166 {
1167 m_log.Warn("[PHYSICS]: Unable to collide Active prim to static space");
1168 }
1169 }
1170 // finally colide active things amoung them
1171 try
1172 {
1173 d.SpaceCollide(ActiveSpace, IntPtr.Zero, nearCallback);
1174 }
1175 catch (AccessViolationException)
1176 {
1177 m_log.Warn("[PHYSICS]: Unable to collide in Active space");
1178 }
1179// _perloopContact.Clear();
1180 }
1181
1182 #endregion
1183
1184
1185
1186 /// <summary>
1187 /// Add actor to the list that should receive collision events in the simulate loop.
1188 /// </summary>
1189 /// <param name="obj"></param>
1190 public void AddCollisionEventReporting(PhysicsActor obj)
1191 {
1192 lock (_collisionEventPrim)
1193 {
1194 if (!_collisionEventPrim.Contains(obj))
1195 _collisionEventPrim.Add(obj);
1196 }
1197 }
1198
1199 /// <summary>
1200 /// Remove actor from the list that should receive collision events in the simulate loop.
1201 /// </summary>
1202 /// <param name="obj"></param>
1203 public void RemoveCollisionEventReporting(PhysicsActor obj)
1204 {
1205 lock (_collisionEventPrim)
1206 {
1207 if (_collisionEventPrim.Contains(obj))
1208 _collisionEventPrim.Remove(obj);
1209 }
1210 }
1211
1212 #region Add/Remove Entities
1213
1214 public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
1215 {
1216 Vector3 pos;
1217 pos.X = position.X;
1218 pos.Y = position.Y;
1219 pos.Z = position.Z;
1220 OdeCharacter newAv = new OdeCharacter(avName, this, pos, size, avPIDD, avPIDP, avCapRadius, avDensity, avMovementDivisorWalk, avMovementDivisorRun);
1221 newAv.Flying = isFlying;
1222 newAv.MinimumGroundFlightOffset = minimumGroundFlightOffset;
1223
1224 return newAv;
1225 }
1226
1227 public void AddCharacter(OdeCharacter chr)
1228 {
1229 lock (_characters)
1230 {
1231 if (!_characters.Contains(chr))
1232 {
1233 _characters.Add(chr);
1234 if (chr.bad)
1235 m_log.DebugFormat("[PHYSICS] Added BAD actor {0} to characters list", chr.m_uuid);
1236 }
1237 }
1238 }
1239
1240 public void RemoveCharacter(OdeCharacter chr)
1241 {
1242 lock (_characters)
1243 {
1244 if (_characters.Contains(chr))
1245 {
1246 _characters.Remove(chr);
1247 }
1248 }
1249 }
1250
1251 public void BadCharacter(OdeCharacter chr)
1252 {
1253 lock (_badCharacter)
1254 {
1255 if (!_badCharacter.Contains(chr))
1256 _badCharacter.Add(chr);
1257 }
1258 }
1259
1260 public override void RemoveAvatar(PhysicsActor actor)
1261 {
1262 //m_log.Debug("[PHYSICS]:ODELOCK");
1263 ((OdeCharacter) actor).Destroy();
1264 }
1265
1266 private PhysicsActor AddPrim(String name, Vector3 position, Vector3 size, Quaternion rotation,
1267 PrimitiveBaseShape pbs, bool isphysical, uint localID)
1268 {
1269 Vector3 pos = position;
1270 Vector3 siz = size;
1271 Quaternion rot = rotation;
1272
1273 OdePrim newPrim;
1274 lock (OdeLock)
1275 {
1276 newPrim = new OdePrim(name, this, pos, siz, rot, pbs, isphysical,false,0,localID);
1277
1278 lock (_prims)
1279 _prims.Add(newPrim);
1280 }
1281 return newPrim;
1282 }
1283
1284 private PhysicsActor AddPrim(String name, Vector3 position, Vector3 size, Quaternion rotation,
1285 PrimitiveBaseShape pbs, bool isphysical, bool isPhantom, uint localID)
1286 {
1287 Vector3 pos = position;
1288 Vector3 siz = size;
1289 Quaternion rot = rotation;
1290
1291 OdePrim newPrim;
1292 lock (OdeLock)
1293 {
1294 newPrim = new OdePrim(name, this, pos, siz, rot, pbs, isphysical, isPhantom, 0, localID);
1295
1296 lock (_prims)
1297 _prims.Add(newPrim);
1298 }
1299 return newPrim;
1300 }
1301
1302 private PhysicsActor AddPrim(String name, Vector3 position, Vector3 size, Quaternion rotation,
1303 PrimitiveBaseShape pbs, bool isphysical, bool isPhantom, byte shapeType, uint localID)
1304 {
1305 Vector3 pos = position;
1306 Vector3 siz = size;
1307 Quaternion rot = rotation;
1308
1309 OdePrim newPrim;
1310 lock (OdeLock)
1311 {
1312 newPrim = new OdePrim(name, this, pos, siz, rot, pbs, isphysical, isPhantom, shapeType, localID);
1313
1314 lock (_prims)
1315 _prims.Add(newPrim);
1316 }
1317 return newPrim;
1318 }
1319
1320 public void addActivePrim(OdePrim activatePrim)
1321 {
1322 // adds active prim..
1323 lock (_activeprims)
1324 {
1325 if (!_activeprims.Contains(activatePrim))
1326 _activeprims.Add(activatePrim);
1327 }
1328 }
1329
1330 public void addActiveGroups(OdePrim activatePrim)
1331 {
1332 lock (_activegroups)
1333 {
1334 if (!_activegroups.Contains(activatePrim))
1335 _activegroups.Add(activatePrim);
1336 }
1337 }
1338
1339 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
1340 Vector3 size, Quaternion rotation, bool isPhysical, bool isPhantom, uint localid)
1341 {
1342 return AddPrim(primName, position, size, rotation, pbs, isPhysical, isPhantom, localid);
1343 }
1344
1345
1346 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
1347 Vector3 size, Quaternion rotation, bool isPhysical, 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, localid);
1354 }
1355
1356 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
1357 Vector3 size, Quaternion rotation, bool isPhysical, bool isPhantom, byte shapeType, uint localid)
1358 {
1359#if SPAM
1360 m_log.DebugFormat("[PHYSICS]: Adding physics actor to {0}", primName);
1361#endif
1362
1363 return AddPrim(primName, position, size, rotation, pbs, isPhysical,isPhantom, shapeType, localid);
1364 }
1365
1366 public override float TimeDilation
1367 {
1368 get { return m_timeDilation; }
1369 }
1370
1371 public override bool SupportsNINJAJoints
1372 {
1373 get { return false; }
1374 }
1375
1376
1377 public void remActivePrim(OdePrim deactivatePrim)
1378 {
1379 lock (_activeprims)
1380 {
1381 _activeprims.Remove(deactivatePrim);
1382 }
1383 }
1384 public void remActiveGroup(OdePrim deactivatePrim)
1385 {
1386 lock (_activegroups)
1387 {
1388 _activegroups.Remove(deactivatePrim);
1389 }
1390 }
1391
1392 public override void RemovePrim(PhysicsActor prim)
1393 {
1394 // As with all ODE physics operations, we don't remove the prim immediately but signal that it should be
1395 // removed in the next physics simulate pass.
1396 if (prim is OdePrim)
1397 {
1398// lock (OdeLock)
1399 {
1400 OdePrim p = (OdePrim)prim;
1401 p.setPrimForRemoval();
1402 }
1403 }
1404 }
1405 /// <summary>
1406 /// This is called from within simulate but outside the locked portion
1407 /// We need to do our own locking here
1408 /// (Note: As of 20110801 this no longer appears to be true - this is being called within lock (odeLock) in
1409 /// Simulate() -- justincc).
1410 ///
1411 /// Essentially, we need to remove the prim from our space segment, whatever segment it's in.
1412 ///
1413 /// If there are no more prim in the segment, we need to empty (spacedestroy)the segment and reclaim memory
1414 /// that the space was using.
1415 /// </summary>
1416 /// <param name="prim"></param>
1417 public void RemovePrimThreadLocked(OdePrim prim)
1418 {
1419 //Console.WriteLine("RemovePrimThreadLocked " + prim.m_primName);
1420 lock (prim)
1421 {
1422 RemoveCollisionEventReporting(prim);
1423 lock (_prims)
1424 _prims.Remove(prim);
1425 }
1426
1427 }
1428 #endregion
1429
1430 #region Space Separation Calculation
1431
1432 /// <summary>
1433 /// Called when a static prim moves or becomes static
1434 /// Places the prim in a space one the static sub-spaces grid
1435 /// </summary>
1436 /// <param name="geom">the pointer to the geom that moved</param>
1437 /// <param name="pos">the position that the geom moved to</param>
1438 /// <param name="currentspace">a pointer to the space it was in before it was moved.</param>
1439 /// <returns>a pointer to the new space it's in</returns>
1440 public IntPtr MoveGeomToStaticSpace(IntPtr geom, Vector3 pos, IntPtr currentspace)
1441 {
1442 // moves a prim into another static sub-space or from another space into a static sub-space
1443
1444 // Called ODEPrim so
1445 // it's already in locked space.
1446
1447 if (geom == IntPtr.Zero) // shouldn't happen
1448 return IntPtr.Zero;
1449
1450 // get the static sub-space for current position
1451 IntPtr newspace = calculateSpaceForGeom(pos);
1452
1453 if (newspace == currentspace) // if we are there all done
1454 return newspace;
1455
1456 // else remove it from its current space
1457 if (currentspace != IntPtr.Zero && d.SpaceQuery(currentspace, geom))
1458 {
1459 if (d.GeomIsSpace(currentspace))
1460 {
1461 waitForSpaceUnlock(currentspace);
1462 d.SpaceRemove(currentspace, geom);
1463
1464 if (d.SpaceGetSublevel(currentspace) > 2 && d.SpaceGetNumGeoms(currentspace) == 0)
1465 {
1466 d.SpaceDestroy(currentspace);
1467 }
1468 }
1469 else
1470 {
1471 m_log.Info("[Physics]: Invalid or empty Space passed to 'MoveGeomToStaticSpace':" + currentspace +
1472 " Geom:" + geom);
1473 }
1474 }
1475 else // odd currentspace is null or doesn't contain the geom? lets try the geom ideia of current space
1476 {
1477 currentspace = d.GeomGetSpace(geom);
1478 if (currentspace != IntPtr.Zero)
1479 {
1480 if (d.GeomIsSpace(currentspace))
1481 {
1482 waitForSpaceUnlock(currentspace);
1483 d.SpaceRemove(currentspace, geom);
1484
1485 if (d.SpaceGetSublevel(currentspace) > 2 && d.SpaceGetNumGeoms(currentspace) == 0)
1486 {
1487 d.SpaceDestroy(currentspace);
1488 }
1489
1490 }
1491 }
1492 }
1493
1494 // put the geom in the newspace
1495 waitForSpaceUnlock(newspace);
1496 d.SpaceAdd(newspace, geom);
1497
1498 // let caller know this newspace
1499 return newspace;
1500 }
1501
1502 /// <summary>
1503 /// Calculates the space the prim should be in by its position
1504 /// </summary>
1505 /// <param name="pos"></param>
1506 /// <returns>a pointer to the space. This could be a new space or reused space.</returns>
1507 public IntPtr calculateSpaceForGeom(Vector3 pos)
1508 {
1509 int x, y;
1510 x = (int)(pos.X * spacesPerMeter);
1511 if (x < 0)
1512 x = 0;
1513 else if (x > spaceGridMaxX)
1514 x = spaceGridMaxX;
1515
1516 y = (int)(pos.Y * spacesPerMeter);
1517 if (y < 0)
1518 y = 0;
1519 else if (y >spaceGridMaxY)
1520 y = spaceGridMaxY;
1521
1522 IntPtr tmpSpace = staticPrimspace[x, y];
1523 return tmpSpace;
1524 }
1525
1526 #endregion
1527
1528 /// <summary>
1529 /// Routine to figure out if we need to mesh this prim with our mesher
1530 /// </summary>
1531 /// <param name="pbs"></param>
1532 /// <returns></returns>
1533 public bool needsMeshing(PrimitiveBaseShape pbs)
1534 {
1535 // most of this is redundant now as the mesher will return null if it cant mesh a prim
1536 // but we still need to check for sculptie meshing being enabled so this is the most
1537 // convenient place to do it for now...
1538
1539 // //if (pbs.PathCurve == (byte)Primitive.PathCurve.Circle && pbs.ProfileCurve == (byte)Primitive.ProfileCurve.Circle && pbs.PathScaleY <= 0.75f)
1540 // //m_log.Debug("needsMeshing: " + " pathCurve: " + pbs.PathCurve.ToString() + " profileCurve: " + pbs.ProfileCurve.ToString() + " pathScaleY: " + Primitive.UnpackPathScale(pbs.PathScaleY).ToString());
1541 int iPropertiesNotSupportedDefault = 0;
1542
1543 if (pbs.SculptEntry)
1544 {
1545 if(!meshSculptedPrim)
1546 return false;
1547 }
1548
1549 // 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
1550 if (!forceSimplePrimMeshing && !pbs.SculptEntry)
1551 {
1552 if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
1553 || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1
1554 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z))
1555 {
1556
1557 if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
1558 && pbs.ProfileHollow == 0
1559 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
1560 && pbs.PathBegin == 0 && pbs.PathEnd == 0
1561 && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
1562 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
1563 && pbs.PathShearX == 0 && pbs.PathShearY == 0)
1564 {
1565#if SPAM
1566 m_log.Warn("NonMesh");
1567#endif
1568 return false;
1569 }
1570 }
1571 }
1572
1573 // following code doesn't give meshs to boxes and spheres ever
1574 // and it's odd.. so for now just return true if asked to force meshs
1575 // hopefully mesher will fail if doesn't suport so things still get basic boxes
1576
1577 if (forceSimplePrimMeshing)
1578 return true;
1579
1580 if (pbs.ProfileHollow != 0)
1581 iPropertiesNotSupportedDefault++;
1582
1583 if ((pbs.PathBegin != 0) || pbs.PathEnd != 0)
1584 iPropertiesNotSupportedDefault++;
1585
1586 if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0))
1587 iPropertiesNotSupportedDefault++;
1588
1589 if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0)
1590 iPropertiesNotSupportedDefault++;
1591
1592 if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100))
1593 iPropertiesNotSupportedDefault++;
1594
1595 if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0))
1596 iPropertiesNotSupportedDefault++;
1597
1598 if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight)
1599 iPropertiesNotSupportedDefault++;
1600
1601 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))
1602 iPropertiesNotSupportedDefault++;
1603
1604 if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1)
1605 iPropertiesNotSupportedDefault++;
1606
1607 // test for torus
1608 if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square)
1609 {
1610 if (pbs.PathCurve == (byte)Extrusion.Curve1)
1611 {
1612 iPropertiesNotSupportedDefault++;
1613 }
1614 }
1615 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
1616 {
1617 if (pbs.PathCurve == (byte)Extrusion.Straight)
1618 {
1619 iPropertiesNotSupportedDefault++;
1620 }
1621
1622 // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits
1623 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
1624 {
1625 iPropertiesNotSupportedDefault++;
1626 }
1627 }
1628 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
1629 {
1630 if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2)
1631 {
1632 iPropertiesNotSupportedDefault++;
1633 }
1634 }
1635 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
1636 {
1637 if (pbs.PathCurve == (byte)Extrusion.Straight)
1638 {
1639 iPropertiesNotSupportedDefault++;
1640 }
1641 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
1642 {
1643 iPropertiesNotSupportedDefault++;
1644 }
1645 }
1646
1647 if (pbs.SculptEntry && meshSculptedPrim)
1648 iPropertiesNotSupportedDefault++;
1649
1650 if (iPropertiesNotSupportedDefault == 0)
1651 {
1652#if SPAM
1653 m_log.Warn("NonMesh");
1654#endif
1655 return false;
1656 }
1657#if SPAM
1658 m_log.Debug("Mesh");
1659#endif
1660 return true;
1661 }
1662
1663 /// <summary>
1664 /// Called to queue a change to a actor
1665 /// to use in place of old taint mechanism so changes do have a time sequence
1666 /// </summary>
1667
1668 public void AddChange(PhysicsActor actor, changes what, Object arg)
1669 {
1670 ODEchangeitem item = new ODEchangeitem();
1671 item.actor = actor;
1672 item.what = what;
1673 item.arg = arg;
1674 ChangesQueue.Enqueue(item);
1675 }
1676
1677 /// <summary>
1678 /// Called after our prim properties are set Scale, position etc.
1679 /// We use this event queue like method to keep changes to the physical scene occuring in the threadlocked mutex
1680 /// This assures us that we have no race conditions
1681 /// </summary>
1682 /// <param name="prim"></param>
1683 public override void AddPhysicsActorTaint(PhysicsActor prim)
1684 {
1685 }
1686
1687 /// <summary>
1688 /// This is our main simulate loop
1689 /// It's thread locked by a Mutex in the scene.
1690 /// It holds Collisions, it instructs ODE to step through the physical reactions
1691 /// It moves the objects around in memory
1692 /// It calls the methods that report back to the object owners.. (scenepresence, SceneObjectGroup)
1693 /// </summary>
1694 /// <param name="timeStep"></param>
1695 /// <returns></returns>
1696 public override float Simulate(float timeStep)
1697 {
1698
1699 DateTime now = DateTime.UtcNow;
1700 TimeSpan SinceLastFrame = now - m_lastframe;
1701 m_lastframe = now;
1702 timeStep = (float)SinceLastFrame.TotalSeconds;
1703
1704 // acumulate time so we can reduce error
1705 step_time += timeStep;
1706
1707 if (step_time < HalfOdeStep)
1708 return 0;
1709
1710 if (framecount < 0)
1711 framecount = 0;
1712
1713 framecount++;
1714
1715 int curphysiteractions;
1716
1717 // if in trouble reduce step resolution
1718 if (step_time >= m_SkipFramesAtms)
1719 curphysiteractions = m_physicsiterations / 2;
1720 else
1721 curphysiteractions = m_physicsiterations;
1722
1723 int nodeframes = 0;
1724
1725// checkThread();
1726
1727 lock (SimulationLock)
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}