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