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