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