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.cs2356
1 files changed, 2356 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..76d7746
--- /dev/null
+++ b/OpenSim/Region/Physics/UbitOdePlugin/OdeScene.cs
@@ -0,0 +1,2356 @@
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,0,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, 0, localID);
1163
1164 lock (_prims)
1165 _prims.Add(newPrim);
1166 }
1167 return newPrim;
1168 }
1169
1170 private PhysicsActor AddPrim(String name, Vector3 position, Vector3 size, Quaternion rotation,
1171 PrimitiveBaseShape pbs, bool isphysical, bool isPhantom, byte shapeType, uint localID)
1172 {
1173 Vector3 pos = position;
1174 Vector3 siz = size;
1175 Quaternion rot = rotation;
1176
1177 OdePrim newPrim;
1178 lock (OdeLock)
1179 {
1180 newPrim = new OdePrim(name, this, pos, siz, rot, pbs, isphysical, isPhantom, shapeType, localID);
1181
1182 lock (_prims)
1183 _prims.Add(newPrim);
1184 }
1185 return newPrim;
1186 }
1187
1188 public void addActivePrim(OdePrim activatePrim)
1189 {
1190 // adds active prim..
1191 lock (_activeprims)
1192 {
1193 if (!_activeprims.Contains(activatePrim))
1194 _activeprims.Add(activatePrim);
1195 }
1196 }
1197
1198 public void addActiveGroups(OdePrim activatePrim)
1199 {
1200 lock (_activegroups)
1201 {
1202 if (!_activegroups.Contains(activatePrim))
1203 _activegroups.Add(activatePrim);
1204 }
1205 }
1206
1207 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
1208 Vector3 size, Quaternion rotation, bool isPhysical, bool isPhantom, uint localid)
1209 {
1210 return AddPrim(primName, position, size, rotation, pbs, isPhysical, isPhantom, localid);
1211 }
1212
1213
1214 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
1215 Vector3 size, Quaternion rotation, bool isPhysical, uint localid)
1216 {
1217#if SPAM
1218 m_log.DebugFormat("[PHYSICS]: Adding physics actor to {0}", primName);
1219#endif
1220
1221 return AddPrim(primName, position, size, rotation, pbs, isPhysical, localid);
1222 }
1223
1224 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
1225 Vector3 size, Quaternion rotation, bool isPhysical, bool isPhantom, byte shapeType, uint localid)
1226 {
1227#if SPAM
1228 m_log.DebugFormat("[PHYSICS]: Adding physics actor to {0}", primName);
1229#endif
1230
1231 return AddPrim(primName, position, size, rotation, pbs, isPhysical,isPhantom, shapeType, localid);
1232 }
1233
1234 public override float TimeDilation
1235 {
1236 get { return m_timeDilation; }
1237 }
1238
1239 public override bool SupportsNINJAJoints
1240 {
1241 get { return false; }
1242 }
1243
1244
1245 public void remActivePrim(OdePrim deactivatePrim)
1246 {
1247 lock (_activeprims)
1248 {
1249 _activeprims.Remove(deactivatePrim);
1250 }
1251 }
1252 public void remActiveGroup(OdePrim deactivatePrim)
1253 {
1254 lock (_activegroups)
1255 {
1256 _activegroups.Remove(deactivatePrim);
1257 }
1258 }
1259
1260 public override void RemovePrim(PhysicsActor prim)
1261 {
1262 // As with all ODE physics operations, we don't remove the prim immediately but signal that it should be
1263 // removed in the next physics simulate pass.
1264 if (prim is OdePrim)
1265 {
1266// lock (OdeLock)
1267 {
1268 OdePrim p = (OdePrim)prim;
1269 p.setPrimForRemoval();
1270 }
1271 }
1272 }
1273 /// <summary>
1274 /// This is called from within simulate but outside the locked portion
1275 /// We need to do our own locking here
1276 /// (Note: As of 20110801 this no longer appears to be true - this is being called within lock (odeLock) in
1277 /// Simulate() -- justincc).
1278 ///
1279 /// Essentially, we need to remove the prim from our space segment, whatever segment it's in.
1280 ///
1281 /// If there are no more prim in the segment, we need to empty (spacedestroy)the segment and reclaim memory
1282 /// that the space was using.
1283 /// </summary>
1284 /// <param name="prim"></param>
1285 public void RemovePrimThreadLocked(OdePrim prim)
1286 {
1287 //Console.WriteLine("RemovePrimThreadLocked " + prim.m_primName);
1288 lock (prim)
1289 {
1290 RemoveCollisionEventReporting(prim);
1291 lock (_prims)
1292 _prims.Remove(prim);
1293 }
1294
1295 }
1296 #endregion
1297
1298 #region Space Separation Calculation
1299
1300 /// <summary>
1301 /// Called when a static prim moves or becomes static
1302 /// Places the prim in a space one the static sub-spaces grid
1303 /// </summary>
1304 /// <param name="geom">the pointer to the geom that moved</param>
1305 /// <param name="pos">the position that the geom moved to</param>
1306 /// <param name="currentspace">a pointer to the space it was in before it was moved.</param>
1307 /// <returns>a pointer to the new space it's in</returns>
1308 public IntPtr MoveGeomToStaticSpace(IntPtr geom, Vector3 pos, IntPtr currentspace)
1309 {
1310 // moves a prim into another static sub-space or from another space into a static sub-space
1311
1312 // Called ODEPrim so
1313 // it's already in locked space.
1314
1315 if (geom == IntPtr.Zero) // shouldn't happen
1316 return IntPtr.Zero;
1317
1318 // get the static sub-space for current position
1319 IntPtr newspace = calculateSpaceForGeom(pos);
1320
1321 if (newspace == currentspace) // if we are there all done
1322 return newspace;
1323
1324 // else remove it from its current space
1325 if (currentspace != IntPtr.Zero && d.SpaceQuery(currentspace, geom))
1326 {
1327 if (d.GeomIsSpace(currentspace))
1328 {
1329 waitForSpaceUnlock(currentspace);
1330 d.SpaceRemove(currentspace, geom);
1331
1332 if (d.SpaceGetSublevel(currentspace) > 2 && d.SpaceGetNumGeoms(currentspace) == 0)
1333 {
1334 d.SpaceDestroy(currentspace);
1335 }
1336 }
1337 else
1338 {
1339 m_log.Info("[Physics]: Invalid or empty Space passed to 'MoveGeomToStaticSpace':" + currentspace +
1340 " Geom:" + geom);
1341 }
1342 }
1343 else // odd currentspace is null or doesn't contain the geom? lets try the geom ideia of current space
1344 {
1345 currentspace = d.GeomGetSpace(geom);
1346 if (currentspace != IntPtr.Zero)
1347 {
1348 if (d.GeomIsSpace(currentspace))
1349 {
1350 waitForSpaceUnlock(currentspace);
1351 d.SpaceRemove(currentspace, geom);
1352
1353 if (d.SpaceGetSublevel(currentspace) > 2 && d.SpaceGetNumGeoms(currentspace) == 0)
1354 {
1355 d.SpaceDestroy(currentspace);
1356 }
1357
1358 }
1359 }
1360 }
1361
1362 // put the geom in the newspace
1363 waitForSpaceUnlock(newspace);
1364 d.SpaceAdd(newspace, geom);
1365
1366 // let caller know this newspace
1367 return newspace;
1368 }
1369
1370 /// <summary>
1371 /// Calculates the space the prim should be in by its position
1372 /// </summary>
1373 /// <param name="pos"></param>
1374 /// <returns>a pointer to the space. This could be a new space or reused space.</returns>
1375 public IntPtr calculateSpaceForGeom(Vector3 pos)
1376 {
1377 int x, y;
1378 x = (int)(pos.X * spacesPerMeter);
1379 if (x < 0)
1380 x = 0;
1381 else if (x > spaceGridMaxX)
1382 x = spaceGridMaxX;
1383
1384 y = (int)(pos.Y * spacesPerMeter);
1385 if (y < 0)
1386 y = 0;
1387 else if (y >spaceGridMaxY)
1388 y = spaceGridMaxY;
1389
1390 IntPtr tmpSpace = staticPrimspace[x, y];
1391 return tmpSpace;
1392 }
1393
1394 #endregion
1395
1396 /// <summary>
1397 /// Routine to figure out if we need to mesh this prim with our mesher
1398 /// </summary>
1399 /// <param name="pbs"></param>
1400 /// <returns></returns>
1401 public bool needsMeshing(PrimitiveBaseShape pbs)
1402 {
1403 // most of this is redundant now as the mesher will return null if it cant mesh a prim
1404 // but we still need to check for sculptie meshing being enabled so this is the most
1405 // convenient place to do it for now...
1406
1407 // //if (pbs.PathCurve == (byte)Primitive.PathCurve.Circle && pbs.ProfileCurve == (byte)Primitive.ProfileCurve.Circle && pbs.PathScaleY <= 0.75f)
1408 // //m_log.Debug("needsMeshing: " + " pathCurve: " + pbs.PathCurve.ToString() + " profileCurve: " + pbs.ProfileCurve.ToString() + " pathScaleY: " + Primitive.UnpackPathScale(pbs.PathScaleY).ToString());
1409 int iPropertiesNotSupportedDefault = 0;
1410
1411 if (pbs.SculptEntry)
1412 {
1413 if(!meshSculptedPrim)
1414 return false;
1415 }
1416
1417 // 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
1418 if (!forceSimplePrimMeshing && !pbs.SculptEntry)
1419 {
1420 if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
1421 || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1
1422 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z))
1423 {
1424
1425 if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
1426 && pbs.ProfileHollow == 0
1427 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
1428 && pbs.PathBegin == 0 && pbs.PathEnd == 0
1429 && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
1430 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
1431 && pbs.PathShearX == 0 && pbs.PathShearY == 0)
1432 {
1433#if SPAM
1434 m_log.Warn("NonMesh");
1435#endif
1436 return false;
1437 }
1438 }
1439 }
1440
1441 // following code doesn't give meshs to boxes and spheres ever
1442 // and it's odd.. so for now just return true if asked to force meshs
1443 // hopefully mesher will fail if doesn't suport so things still get basic boxes
1444
1445 if (forceSimplePrimMeshing)
1446 return true;
1447
1448 if (pbs.ProfileHollow != 0)
1449 iPropertiesNotSupportedDefault++;
1450
1451 if ((pbs.PathBegin != 0) || pbs.PathEnd != 0)
1452 iPropertiesNotSupportedDefault++;
1453
1454 if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0))
1455 iPropertiesNotSupportedDefault++;
1456
1457 if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0)
1458 iPropertiesNotSupportedDefault++;
1459
1460 if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100))
1461 iPropertiesNotSupportedDefault++;
1462
1463 if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0))
1464 iPropertiesNotSupportedDefault++;
1465
1466 if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight)
1467 iPropertiesNotSupportedDefault++;
1468
1469 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))
1470 iPropertiesNotSupportedDefault++;
1471
1472 if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1)
1473 iPropertiesNotSupportedDefault++;
1474
1475 // test for torus
1476 if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square)
1477 {
1478 if (pbs.PathCurve == (byte)Extrusion.Curve1)
1479 {
1480 iPropertiesNotSupportedDefault++;
1481 }
1482 }
1483 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
1484 {
1485 if (pbs.PathCurve == (byte)Extrusion.Straight)
1486 {
1487 iPropertiesNotSupportedDefault++;
1488 }
1489
1490 // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits
1491 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
1492 {
1493 iPropertiesNotSupportedDefault++;
1494 }
1495 }
1496 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
1497 {
1498 if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2)
1499 {
1500 iPropertiesNotSupportedDefault++;
1501 }
1502 }
1503 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
1504 {
1505 if (pbs.PathCurve == (byte)Extrusion.Straight)
1506 {
1507 iPropertiesNotSupportedDefault++;
1508 }
1509 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
1510 {
1511 iPropertiesNotSupportedDefault++;
1512 }
1513 }
1514
1515 if (pbs.SculptEntry && meshSculptedPrim)
1516 iPropertiesNotSupportedDefault++;
1517
1518 if (iPropertiesNotSupportedDefault == 0)
1519 {
1520#if SPAM
1521 m_log.Warn("NonMesh");
1522#endif
1523 return false;
1524 }
1525#if SPAM
1526 m_log.Debug("Mesh");
1527#endif
1528 return true;
1529 }
1530
1531 /// <summary>
1532 /// Called to queue a change to a actor
1533 /// to use in place of old taint mechanism so changes do have a time sequence
1534 /// </summary>
1535
1536 public void AddChange(PhysicsActor actor, changes what, Object arg)
1537 {
1538 ODEchangeitem item = new ODEchangeitem();
1539 item.actor = actor;
1540 item.what = what;
1541 item.arg = arg;
1542 ChangesQueue.Enqueue(item);
1543 }
1544
1545 /// <summary>
1546 /// Called after our prim properties are set Scale, position etc.
1547 /// We use this event queue like method to keep changes to the physical scene occuring in the threadlocked mutex
1548 /// This assures us that we have no race conditions
1549 /// </summary>
1550 /// <param name="prim"></param>
1551 public override void AddPhysicsActorTaint(PhysicsActor prim)
1552 {
1553 }
1554
1555 /// <summary>
1556 /// This is our main simulate loop
1557 /// It's thread locked by a Mutex in the scene.
1558 /// It holds Collisions, it instructs ODE to step through the physical reactions
1559 /// It moves the objects around in memory
1560 /// It calls the methods that report back to the object owners.. (scenepresence, SceneObjectGroup)
1561 /// </summary>
1562 /// <param name="timeStep"></param>
1563 /// <returns></returns>
1564 public override float Simulate(float timeStep)
1565 {
1566 int statstart;
1567 int statchanges = 0;
1568 int statchmove = 0;
1569 int statactmove = 0;
1570 int statray = 0;
1571 int statcol = 0;
1572 int statstep = 0;
1573 int statmovchar = 0;
1574 int statmovprim;
1575 int totjcontact = 0;
1576
1577 // acumulate time so we can reduce error
1578 step_time += timeStep;
1579
1580 if (step_time < ODE_STEPSIZE)
1581 return 0;
1582
1583 if (framecount >= int.MaxValue)
1584 framecount = 0;
1585
1586 framecount++;
1587
1588 int curphysiteractions = m_physicsiterations;
1589
1590 if (step_time >= m_SkipFramesAtms)
1591 {
1592 // if in trouble reduce step resolution
1593 curphysiteractions /= 2;
1594 }
1595
1596 int nodeframes = 0;
1597
1598// checkThread();
1599
1600 lock (SimulationLock)
1601 {
1602 // adjust number of iterations per step
1603 try
1604 {
1605 d.WorldSetQuickStepNumIterations(world, curphysiteractions);
1606 }
1607 catch (StackOverflowException)
1608 {
1609 m_log.Error("[PHYSICS]: The operating system wasn't able to allocate enough memory for the simulation. Restarting the sim.");
1610// ode.drelease(world);
1611 base.TriggerPhysicsBasedRestart();
1612 }
1613
1614
1615 while (step_time >= ODE_STEPSIZE && nodeframes < 10) //limit number of steps so we don't say here for ever
1616 {
1617 try
1618 {
1619 statstart = Util.EnvironmentTickCount();
1620
1621 // clear pointer/counter to contacts to pass into joints
1622 m_global_contactcount = 0;
1623
1624 ODEchangeitem item;
1625
1626 if(ChangesQueue.Count >0)
1627 {
1628 int ttmpstart = Util.EnvironmentTickCount();
1629 int ttmp;
1630 int ttmp2;
1631
1632 while(ChangesQueue.Dequeue(out item))
1633 {
1634 if (item.actor != null)
1635 {
1636 try
1637 {
1638 if (item.actor is OdeCharacter)
1639 ((OdeCharacter)item.actor).DoAChange(item.what, item.arg);
1640 else if (((OdePrim)item.actor).DoAChange(item.what, item.arg))
1641 RemovePrimThreadLocked((OdePrim)item.actor);
1642 }
1643 catch
1644 {
1645 m_log.Warn("[PHYSICS]: doChange failed for a actor");
1646 };
1647 }
1648 ttmp = Util.EnvironmentTickCountSubtract(ttmpstart);
1649 if (ttmp > 20)
1650 break;
1651 }
1652
1653 ttmp2 = Util.EnvironmentTickCountSubtract(ttmpstart);
1654 if (ttmp2 > 50)
1655 ttmp2 = 0;
1656
1657 }
1658
1659 statchanges += Util.EnvironmentTickCountSubtract(statstart);
1660
1661 statactmove += Util.EnvironmentTickCountSubtract(statstart);
1662 //if ((framecount % m_randomizeWater) == 0)
1663 // randomizeWater(waterlevel);
1664
1665 m_rayCastManager.ProcessQueuedRequests();
1666
1667
1668
1669 statray += Util.EnvironmentTickCountSubtract(statstart);
1670 collision_optimized();
1671 statcol += Util.EnvironmentTickCountSubtract(statstart);
1672
1673 lock (_collisionEventPrim)
1674 {
1675 foreach (PhysicsActor obj in _collisionEventPrim)
1676 {
1677 if (obj == null)
1678 continue;
1679
1680 switch ((ActorTypes)obj.PhysicsActorType)
1681 {
1682 case ActorTypes.Agent:
1683 OdeCharacter cobj = (OdeCharacter)obj;
1684 cobj.AddCollisionFrameTime((int)(ODE_STEPSIZE*1000.0f));
1685 cobj.SendCollisions();
1686 break;
1687
1688 case ActorTypes.Prim:
1689 OdePrim pobj = (OdePrim)obj;
1690 pobj.SendCollisions();
1691 break;
1692 }
1693 }
1694 }
1695
1696 d.WorldQuickStep(world, ODE_STEPSIZE);
1697 statstep += Util.EnvironmentTickCountSubtract(statstart);
1698
1699 // Move characters
1700 lock (_characters)
1701 {
1702 List<OdeCharacter> defects = new List<OdeCharacter>();
1703 foreach (OdeCharacter actor in _characters)
1704 {
1705 if (actor != null)
1706 actor.Move(ODE_STEPSIZE, defects);
1707 }
1708 if (defects.Count != 0)
1709 {
1710 foreach (OdeCharacter defect in defects)
1711 {
1712 RemoveCharacter(defect);
1713 }
1714 }
1715 }
1716 statchmove += Util.EnvironmentTickCountSubtract(statstart);
1717
1718 // Move other active objects
1719 lock (_activegroups)
1720 {
1721 foreach (OdePrim aprim in _activegroups)
1722 {
1723 aprim.Move();
1724 }
1725 }
1726
1727 //ode.dunlock(world);
1728 }
1729 catch (Exception e)
1730 {
1731 m_log.ErrorFormat("[PHYSICS]: {0}, {1}, {2}", e.Message, e.TargetSite, e);
1732// ode.dunlock(world);
1733 }
1734
1735 d.JointGroupEmpty(contactgroup);
1736 totjcontact += m_global_contactcount;
1737
1738 step_time -= ODE_STEPSIZE;
1739 nodeframes++;
1740 }
1741
1742 statstart = Util.EnvironmentTickCount();
1743
1744 lock (_characters)
1745 {
1746 foreach (OdeCharacter actor in _characters)
1747 {
1748 if (actor != null)
1749 {
1750 if (actor.bad)
1751 m_log.WarnFormat("[PHYSICS]: BAD Actor {0} in _characters list was not removed?", actor.m_uuid);
1752
1753 actor.UpdatePositionAndVelocity();
1754 }
1755 }
1756 }
1757
1758 lock (_badCharacter)
1759 {
1760 if (_badCharacter.Count > 0)
1761 {
1762 foreach (OdeCharacter chr in _badCharacter)
1763 {
1764 RemoveCharacter(chr);
1765 }
1766
1767 _badCharacter.Clear();
1768 }
1769 }
1770 statmovchar = Util.EnvironmentTickCountSubtract(statstart);
1771
1772 lock (_activegroups)
1773 {
1774 {
1775 foreach (OdePrim actor in _activegroups)
1776 {
1777 if (actor.IsPhysical)
1778 {
1779 actor.UpdatePositionAndVelocity((float)nodeframes * ODE_STEPSIZE);
1780 }
1781 }
1782 }
1783 }
1784
1785 statmovprim = Util.EnvironmentTickCountSubtract(statstart);
1786
1787 int nactivegeoms = d.SpaceGetNumGeoms(ActiveSpace);
1788 int nstaticgeoms = d.SpaceGetNumGeoms(StaticSpace);
1789 int ntopgeoms = d.SpaceGetNumGeoms(TopSpace);
1790 int nbodies = d.NTotalBodies;
1791 int ngeoms = d.NTotalGeoms;
1792
1793 // Finished with all sim stepping. If requested, dump world state to file for debugging.
1794 // TODO: This call to the export function is already inside lock (OdeLock) - but is an extra lock needed?
1795 // TODO: This overwrites all dump files in-place. Should this be a growing logfile, or separate snapshots?
1796 if (physics_logging && (physics_logging_interval > 0) && (framecount % physics_logging_interval == 0))
1797 {
1798 string fname = "state-" + world.ToString() + ".DIF"; // give each physics world a separate filename
1799 string prefix = "world" + world.ToString(); // prefix for variable names in exported .DIF file
1800
1801 if (physics_logging_append_existing_logfile)
1802 {
1803 string header = "-------------- START OF PHYSICS FRAME " + framecount.ToString() + " --------------";
1804 TextWriter fwriter = File.AppendText(fname);
1805 fwriter.WriteLine(header);
1806 fwriter.Close();
1807 }
1808
1809 d.WorldExportDIF(world, fname, physics_logging_append_existing_logfile, prefix);
1810 }
1811
1812 // think time dilation is not a physics issue alone.. but ok let's fake something
1813 if (step_time < ODE_STEPSIZE) // we did the required loops
1814 m_timeDilation = 1.0f;
1815 else
1816 { // we didn't forget the lost ones and let user know something
1817 m_timeDilation = 1 - step_time / timeStep;
1818 if (m_timeDilation < 0)
1819 m_timeDilation = 0;
1820 step_time = 0;
1821 }
1822 }
1823
1824// return nodeframes * ODE_STEPSIZE; // return real simulated time
1825 return 1000 * nodeframes; // return steps for now * 1000 to keep core happy
1826 }
1827
1828 /// <summary>
1829 public override void GetResults()
1830 {
1831 }
1832
1833 public override bool IsThreaded
1834 {
1835 // for now we won't be multithreaded
1836 get { return (false); }
1837 }
1838
1839 public float GetTerrainHeightAtXY(float x, float y)
1840 {
1841 // assumes 1m size grid and constante size square regions
1842 // needs to know about sims around in future
1843 // region offset in mega position
1844
1845 int offsetX = ((int)(x / (int)Constants.RegionSize)) * (int)Constants.RegionSize;
1846 int offsetY = ((int)(y / (int)Constants.RegionSize)) * (int)Constants.RegionSize;
1847
1848 IntPtr heightFieldGeom = IntPtr.Zero;
1849
1850 // get region map
1851 if (!RegionTerrain.TryGetValue(new Vector3(offsetX, offsetY, 0), out heightFieldGeom))
1852 return 0f;
1853
1854 if (heightFieldGeom == IntPtr.Zero)
1855 return 0f;
1856
1857 if (!TerrainHeightFieldHeights.ContainsKey(heightFieldGeom))
1858 return 0f;
1859
1860 // TerrainHeightField for ODE as offset 1m
1861 x += 1f - offsetX;
1862 y += 1f - offsetY;
1863
1864 // make position fit into array
1865 if (x < 0)
1866 x = 0;
1867 if (y < 0)
1868 y = 0;
1869
1870 // integer indexs
1871 int ix;
1872 int iy;
1873 // interpolators offset
1874 float dx;
1875 float dy;
1876
1877 int regsize = (int)Constants.RegionSize + 3; // map size see setterrain number of samples
1878
1879 // we still have square fixed size regions
1880 // also flip x and y because of how map is done for ODE fliped axis
1881 // so ix,iy,dx and dy are inter exchanged
1882 if (x < regsize - 1)
1883 {
1884 iy = (int)x;
1885 dy = x - (float)iy;
1886 }
1887 else // out world use external height
1888 {
1889 iy = regsize - 1;
1890 dy = 0;
1891 }
1892 if (y < regsize - 1)
1893 {
1894 ix = (int)y;
1895 dx = y - (float)ix;
1896 }
1897 else
1898 {
1899 ix = regsize - 1;
1900 dx = 0;
1901 }
1902
1903 float h0;
1904 float h1;
1905 float h2;
1906
1907 iy *= regsize;
1908 iy += ix; // all indexes have iy + ix
1909
1910 float[] heights = TerrainHeightFieldHeights[heightFieldGeom];
1911
1912 if ((dx + dy) <= 1.0f)
1913 {
1914 h0 = ((float)heights[iy]); // 0,0 vertice
1915 h1 = (((float)heights[iy + 1]) - h0) * dx; // 1,0 vertice minus 0,0
1916 h2 = (((float)heights[iy + regsize]) - h0) * dy; // 0,1 vertice minus 0,0
1917 }
1918 else
1919 {
1920 h0 = ((float)heights[iy + regsize + 1]); // 1,1 vertice
1921 h1 = (((float)heights[iy + 1]) - h0) * (1 - dy); // 1,1 vertice minus 1,0
1922 h2 = (((float)heights[iy + regsize]) - h0) * (1 - dx); // 1,1 vertice minus 0,1
1923 }
1924
1925 return h0 + h1 + h2;
1926 }
1927 public override void SetTerrain(float[] heightMap)
1928 {
1929 if (m_worldOffset != Vector3.Zero && m_parentScene != null)
1930 {
1931 if (m_parentScene is OdeScene)
1932 {
1933 ((OdeScene)m_parentScene).SetTerrain(heightMap, m_worldOffset);
1934 }
1935 }
1936 else
1937 {
1938 SetTerrain(heightMap, m_worldOffset);
1939 }
1940 }
1941
1942 public override void CombineTerrain(float[] heightMap, Vector3 pOffset)
1943 {
1944 SetTerrain(heightMap, pOffset);
1945 }
1946
1947 public void SetTerrain(float[] heightMap, Vector3 pOffset)
1948 {
1949 // assumes 1m size grid and constante size square regions
1950 // needs to know about sims around in future
1951
1952 float[] _heightmap;
1953
1954 uint heightmapWidth = Constants.RegionSize + 2;
1955 uint heightmapHeight = Constants.RegionSize + 2;
1956
1957 uint heightmapWidthSamples = heightmapWidth + 1;
1958 uint heightmapHeightSamples = heightmapHeight + 1;
1959
1960 _heightmap = new float[heightmapWidthSamples * heightmapHeightSamples];
1961
1962 const float scale = 1.0f;
1963 const float offset = 0.0f;
1964 const float thickness = 10f;
1965 const int wrap = 0;
1966
1967 uint regionsize = Constants.RegionSize;
1968
1969 float hfmin = float.MaxValue;
1970 float hfmax = float.MinValue;
1971 float val;
1972 uint xx;
1973 uint yy;
1974
1975 uint maxXXYY = regionsize - 1;
1976 // flipping map adding one margin all around so things don't fall in edges
1977
1978 uint xt = 0;
1979 xx = 0;
1980
1981 for (uint x = 0; x < heightmapWidthSamples; x++)
1982 {
1983 if (x > 1 && xx < maxXXYY)
1984 xx++;
1985 yy = 0;
1986 for (uint y = 0; y < heightmapHeightSamples; y++)
1987 {
1988 if (y > 1 && y < maxXXYY)
1989 yy += regionsize;
1990
1991 val = heightMap[yy + xx];
1992 if (val < 0.0f)
1993 val = 0.0f; // no neg terrain as in chode
1994 _heightmap[xt + y] = val;
1995
1996 if (hfmin > val)
1997 hfmin = val;
1998 if (hfmax < val)
1999 hfmax = val;
2000 }
2001 xt += heightmapHeightSamples;
2002 }
2003 lock (OdeLock)
2004 {
2005 IntPtr GroundGeom = IntPtr.Zero;
2006 if (RegionTerrain.TryGetValue(pOffset, out GroundGeom))
2007 {
2008 RegionTerrain.Remove(pOffset);
2009 if (GroundGeom != IntPtr.Zero)
2010 {
2011 if (TerrainHeightFieldHeights.ContainsKey(GroundGeom))
2012 {
2013 TerrainHeightFieldHeightsHandlers[GroundGeom].Free();
2014 TerrainHeightFieldHeightsHandlers.Remove(GroundGeom);
2015 TerrainHeightFieldHeights.Remove(GroundGeom);
2016 }
2017 d.SpaceRemove(StaticSpace, GroundGeom);
2018 d.GeomDestroy(GroundGeom);
2019 }
2020 }
2021 IntPtr HeightmapData = d.GeomHeightfieldDataCreate();
2022
2023 GCHandle _heightmaphandler = GCHandle.Alloc(_heightmap, GCHandleType.Pinned);
2024
2025 d.GeomHeightfieldDataBuildSingle(HeightmapData, _heightmaphandler.AddrOfPinnedObject(), 0, heightmapWidth , heightmapHeight,
2026 (int)heightmapWidthSamples, (int)heightmapHeightSamples, scale,
2027 offset, thickness, wrap);
2028
2029 d.GeomHeightfieldDataSetBounds(HeightmapData, hfmin - 1, hfmax + 1);
2030 GroundGeom = d.CreateHeightfield(StaticSpace, HeightmapData, 1);
2031 if (GroundGeom != IntPtr.Zero)
2032 {
2033 d.GeomSetCategoryBits(GroundGeom, (int)(CollisionCategories.Land));
2034 d.GeomSetCollideBits(GroundGeom, (int)(CollisionCategories.Space));
2035
2036 }
2037 geom_name_map[GroundGeom] = "Terrain";
2038
2039 d.Matrix3 R = new d.Matrix3();
2040
2041 Quaternion q1 = Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), 1.5707f);
2042 Quaternion q2 = Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), 1.5707f);
2043
2044
2045 q1 = q1 * q2;
2046
2047 Vector3 v3;
2048 float angle;
2049 q1.GetAxisAngle(out v3, out angle);
2050
2051 d.RFromAxisAndAngle(out R, v3.X, v3.Y, v3.Z, angle);
2052 d.GeomSetRotation(GroundGeom, ref R);
2053 d.GeomSetPosition(GroundGeom, pOffset.X + (float)Constants.RegionSize * 0.5f, pOffset.Y + (float)Constants.RegionSize * 0.5f, 0);
2054 RegionTerrain.Add(pOffset, GroundGeom, GroundGeom);
2055// TerrainHeightFieldHeights.Add(GroundGeom, ODElandMap);
2056 TerrainHeightFieldHeights.Add(GroundGeom, _heightmap);
2057 TerrainHeightFieldHeightsHandlers.Add(GroundGeom, _heightmaphandler);
2058
2059 }
2060 }
2061
2062 public override void DeleteTerrain()
2063 {
2064 }
2065
2066 public float GetWaterLevel()
2067 {
2068 return waterlevel;
2069 }
2070
2071 public override bool SupportsCombining()
2072 {
2073 return true;
2074 }
2075/*
2076 public override void UnCombine(PhysicsScene pScene)
2077 {
2078 IntPtr localGround = IntPtr.Zero;
2079// float[] localHeightfield;
2080 bool proceed = false;
2081 List<IntPtr> geomDestroyList = new List<IntPtr>();
2082
2083 lock (OdeLock)
2084 {
2085 if (RegionTerrain.TryGetValue(Vector3.Zero, out localGround))
2086 {
2087 foreach (IntPtr geom in TerrainHeightFieldHeights.Keys)
2088 {
2089 if (geom == localGround)
2090 {
2091// localHeightfield = TerrainHeightFieldHeights[geom];
2092 proceed = true;
2093 }
2094 else
2095 {
2096 geomDestroyList.Add(geom);
2097 }
2098 }
2099
2100 if (proceed)
2101 {
2102 m_worldOffset = Vector3.Zero;
2103 WorldExtents = new Vector2((int)Constants.RegionSize, (int)Constants.RegionSize);
2104 m_parentScene = null;
2105
2106 foreach (IntPtr g in geomDestroyList)
2107 {
2108 // removingHeightField needs to be done or the garbage collector will
2109 // collect the terrain data before we tell ODE to destroy it causing
2110 // memory corruption
2111 if (TerrainHeightFieldHeights.ContainsKey(g))
2112 {
2113// float[] removingHeightField = TerrainHeightFieldHeights[g];
2114 TerrainHeightFieldHeights.Remove(g);
2115
2116 if (RegionTerrain.ContainsKey(g))
2117 {
2118 RegionTerrain.Remove(g);
2119 }
2120
2121 d.GeomDestroy(g);
2122 //removingHeightField = new float[0];
2123 }
2124 }
2125
2126 }
2127 else
2128 {
2129 m_log.Warn("[PHYSICS]: Couldn't proceed with UnCombine. Region has inconsistant data.");
2130 }
2131 }
2132 }
2133 }
2134*/
2135 public override void SetWaterLevel(float baseheight)
2136 {
2137 waterlevel = baseheight;
2138 randomizeWater(waterlevel);
2139 }
2140
2141 public void randomizeWater(float baseheight)
2142 {
2143 const uint heightmapWidth = m_regionWidth + 2;
2144 const uint heightmapHeight = m_regionHeight + 2;
2145 const uint heightmapWidthSamples = m_regionWidth + 2;
2146 const uint heightmapHeightSamples = m_regionHeight + 2;
2147 const float scale = 1.0f;
2148 const float offset = 0.0f;
2149 const float thickness = 2.9f;
2150 const int wrap = 0;
2151
2152 for (int i = 0; i < (258 * 258); i++)
2153 {
2154 _watermap[i] = (baseheight-0.1f) + ((float)fluidRandomizer.Next(1,9) / 10f);
2155 // m_log.Info((baseheight - 0.1f) + ((float)fluidRandomizer.Next(1, 9) / 10f));
2156 }
2157
2158 lock (OdeLock)
2159 {
2160 if (WaterGeom != IntPtr.Zero)
2161 {
2162 d.SpaceRemove(StaticSpace, WaterGeom);
2163 }
2164 IntPtr HeightmapData = d.GeomHeightfieldDataCreate();
2165 d.GeomHeightfieldDataBuildSingle(HeightmapData, _watermap, 0, heightmapWidth, heightmapHeight,
2166 (int)heightmapWidthSamples, (int)heightmapHeightSamples, scale,
2167 offset, thickness, wrap);
2168 d.GeomHeightfieldDataSetBounds(HeightmapData, m_regionWidth, m_regionHeight);
2169 WaterGeom = d.CreateHeightfield(StaticSpace, HeightmapData, 1);
2170 if (WaterGeom != IntPtr.Zero)
2171 {
2172 d.GeomSetCategoryBits(WaterGeom, (int)(CollisionCategories.Water));
2173 d.GeomSetCollideBits(WaterGeom, (int)(CollisionCategories.Space));
2174
2175 }
2176 geom_name_map[WaterGeom] = "Water";
2177
2178 d.Matrix3 R = new d.Matrix3();
2179
2180 Quaternion q1 = Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), 1.5707f);
2181 Quaternion q2 = Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), 1.5707f);
2182
2183 q1 = q1 * q2;
2184 Vector3 v3;
2185 float angle;
2186 q1.GetAxisAngle(out v3, out angle);
2187
2188 d.RFromAxisAndAngle(out R, v3.X, v3.Y, v3.Z, angle);
2189 d.GeomSetRotation(WaterGeom, ref R);
2190 d.GeomSetPosition(WaterGeom, 128, 128, 0);
2191
2192 }
2193
2194 }
2195
2196 public override void Dispose()
2197 {
2198 m_rayCastManager.Dispose();
2199 m_rayCastManager = null;
2200
2201 lock (OdeLock)
2202 {
2203 lock (_prims)
2204 {
2205 foreach (OdePrim prm in _prims)
2206 {
2207 RemovePrim(prm);
2208 }
2209 }
2210
2211 if (ContactgeomsArray != IntPtr.Zero)
2212 Marshal.FreeHGlobal(ContactgeomsArray);
2213 if (GlobalContactsArray != IntPtr.Zero)
2214 Marshal.FreeHGlobal(GlobalContactsArray);
2215
2216 d.WorldDestroy(world);
2217 //d.CloseODE();
2218 }
2219 }
2220
2221 public override Dictionary<uint, float> GetTopColliders()
2222 {
2223 Dictionary<uint, float> returncolliders = new Dictionary<uint, float>();
2224 int cnt = 0;
2225 lock (_prims)
2226 {
2227 foreach (OdePrim prm in _prims)
2228 {
2229 if (prm.CollisionScore > 0)
2230 {
2231 returncolliders.Add(prm.m_localID, prm.CollisionScore);
2232 cnt++;
2233 prm.CollisionScore = 0f;
2234 if (cnt > 25)
2235 {
2236 break;
2237 }
2238 }
2239 }
2240 }
2241 return returncolliders;
2242 }
2243
2244 public override bool SupportsRayCast()
2245 {
2246 return true;
2247 }
2248
2249 public override void RaycastWorld(Vector3 position, Vector3 direction, float length, RaycastCallback retMethod)
2250 {
2251 if (retMethod != null)
2252 {
2253 m_rayCastManager.QueueRequest(position, direction, length, retMethod);
2254 }
2255 }
2256
2257 public override void RaycastWorld(Vector3 position, Vector3 direction, float length, int Count, RayCallback retMethod)
2258 {
2259 if (retMethod != null)
2260 {
2261 m_rayCastManager.QueueRequest(position, direction, length, Count, retMethod);
2262 }
2263 }
2264
2265 // don't like this
2266 public override List<ContactResult> RaycastWorld(Vector3 position, Vector3 direction, float length, int Count)
2267 {
2268 ContactResult[] ourResults = null;
2269 RayCallback retMethod = delegate(List<ContactResult> results)
2270 {
2271 ourResults = new ContactResult[results.Count];
2272 results.CopyTo(ourResults, 0);
2273 };
2274 int waitTime = 0;
2275 m_rayCastManager.QueueRequest(position, direction, length, Count, retMethod);
2276 while (ourResults == null && waitTime < 1000)
2277 {
2278 Thread.Sleep(1);
2279 waitTime++;
2280 }
2281 if (ourResults == null)
2282 return new List<ContactResult>();
2283 return new List<ContactResult>(ourResults);
2284 }
2285
2286 public override void RaycastActor(PhysicsActor actor, Vector3 position, Vector3 direction, float length, RaycastCallback retMethod)
2287 {
2288 if (retMethod != null && actor !=null)
2289 {
2290 IntPtr geom;
2291 if (actor is OdePrim)
2292 geom = ((OdePrim)actor).prim_geom;
2293 else if (actor is OdeCharacter)
2294 geom = ((OdePrim)actor).prim_geom;
2295 else
2296 return;
2297 if (geom == IntPtr.Zero)
2298 return;
2299 m_rayCastManager.QueueRequest(geom, position, direction, length, retMethod);
2300 }
2301 }
2302
2303 public override void RaycastActor(PhysicsActor actor, Vector3 position, Vector3 direction, float length, int Count, RayCallback retMethod)
2304 {
2305 if (retMethod != null && actor != null)
2306 {
2307 IntPtr geom;
2308 if (actor is OdePrim)
2309 geom = ((OdePrim)actor).prim_geom;
2310 else if (actor is OdeCharacter)
2311 geom = ((OdePrim)actor).prim_geom;
2312 else
2313 return;
2314 if (geom == IntPtr.Zero)
2315 return;
2316
2317 m_rayCastManager.QueueRequest(geom,position, direction, length, Count, retMethod);
2318 }
2319 }
2320
2321 // don't like this
2322 public override List<ContactResult> RaycastActor(PhysicsActor actor, Vector3 position, Vector3 direction, float length, int Count)
2323 {
2324 if (actor != null)
2325 {
2326 IntPtr geom;
2327 if (actor is OdePrim)
2328 geom = ((OdePrim)actor).prim_geom;
2329 else if (actor is OdeCharacter)
2330 geom = ((OdePrim)actor).prim_geom;
2331 else
2332 return new List<ContactResult>();
2333 if (geom == IntPtr.Zero)
2334 return new List<ContactResult>();
2335
2336 ContactResult[] ourResults = null;
2337 RayCallback retMethod = delegate(List<ContactResult> results)
2338 {
2339 ourResults = new ContactResult[results.Count];
2340 results.CopyTo(ourResults, 0);
2341 };
2342 int waitTime = 0;
2343 m_rayCastManager.QueueRequest(geom,position, direction, length, Count, retMethod);
2344 while (ourResults == null && waitTime < 1000)
2345 {
2346 Thread.Sleep(1);
2347 waitTime++;
2348 }
2349 if (ourResults == null)
2350 return new List<ContactResult>();
2351 return new List<ContactResult>(ourResults);
2352 }
2353 return new List<ContactResult>();
2354 }
2355 }
2356}