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.cs2383
1 files changed, 2383 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..1a6907d
--- /dev/null
+++ b/OpenSim/Region/Physics/UbitOdePlugin/OdeScene.cs
@@ -0,0 +1,2383 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28//#define SPAM
29
30using System;
31using System.Collections.Generic;
32using System.Reflection;
33using System.Runtime.InteropServices;
34using System.Threading;
35using System.IO;
36using System.Diagnostics;
37using log4net;
38using Nini.Config;
39using OdeAPI;
40using OpenSim.Framework;
41using OpenSim.Region.Physics.Manager;
42using OpenMetaverse;
43
44namespace OpenSim.Region.Physics.OdePlugin
45{
46 public enum StatusIndicators : int
47 {
48 Generic = 0,
49 Start = 1,
50 End = 2
51 }
52
53 public struct sCollisionData
54 {
55 public uint ColliderLocalId;
56 public uint CollidedWithLocalId;
57 public int NumberOfCollisions;
58 public int CollisionType;
59 public int StatusIndicator;
60 public int lastframe;
61 }
62
63 [Flags]
64 public enum CollisionCategories : int
65 {
66 Disabled = 0,
67 Geom = 0x00000001,
68 Body = 0x00000002,
69 Space = 0x00000004,
70 Character = 0x00000008,
71 Land = 0x00000010,
72 Water = 0x00000020,
73 Wind = 0x00000040,
74 Sensor = 0x00000080,
75 Selected = 0x00000100
76 }
77
78 /// <summary>
79 /// Material type for a primitive
80 /// </summary>
81 public enum Material : int
82 {
83 /// <summary></summary>
84 Stone = 0,
85 /// <summary></summary>
86 Metal = 1,
87 /// <summary></summary>
88 Glass = 2,
89 /// <summary></summary>
90 Wood = 3,
91 /// <summary></summary>
92 Flesh = 4,
93 /// <summary></summary>
94 Plastic = 5,
95 /// <summary></summary>
96 Rubber = 6,
97
98 light = 7 // compatibility with old viewers
99 }
100
101 public enum changes : int
102 {
103 Add = 0, // arg null. finishs the prim creation. should be used internally only ( to remove later ?)
104 Remove,
105 Link, // arg AuroraODEPrim new parent prim or null to delink. Makes the prim part of a object with prim parent as root
106 // or removes from a object if arg is null
107 DeLink,
108 Position, // arg Vector3 new position in world coords. Changes prim position. Prim must know if it is root or child
109 Orientation, // arg Quaternion new orientation in world coords. Changes prim position. Prim must know it it is root or child
110 PosOffset, // not in use
111 // arg Vector3 new position in local coords. Changes prim position in object
112 OriOffset, // not in use
113 // arg Vector3 new position in local coords. Changes prim position in object
114 Velocity,
115 AngVelocity,
116 Acceleration,
117 Force,
118 Torque,
119
120 AddForce,
121 AddAngForce,
122 AngLock,
123
124 Size,
125 Shape,
126
127 CollidesWater,
128 VolumeDtc,
129
130 Physical,
131 Phantom,
132 Selected,
133 disabled,
134 building,
135
136 VehicleType,
137 VehicleFloatParam,
138 VehicleVectorParam,
139 VehicleRotationParam,
140 VehicleFlags,
141 SetVehicle,
142
143 Null //keep this last used do dim the methods array. does nothing but pulsing the prim
144 }
145
146 public struct ODEchangeitem
147 {
148 public PhysicsActor actor;
149 public OdeCharacter character;
150 public changes what;
151 public Object arg;
152 }
153
154 public class OdeScene : PhysicsScene
155 {
156 private readonly ILog m_log;
157 // private Dictionary<string, sCollisionData> m_storedCollisions = new Dictionary<string, sCollisionData>();
158
159// private int threadid = 0;
160 private Random fluidRandomizer = new Random(Environment.TickCount);
161
162 const d.ContactFlags comumContactFlags = d.ContactFlags.SoftERP | d.ContactFlags.SoftCFM |d.ContactFlags.Approx1 | d.ContactFlags.Bounce;
163 const float MaxERP = 0.8f;
164 const float minERP = 0.1f;
165 const float comumContactCFM = 0.0001f;
166
167 float frictionMovementMult = 0.3f;
168
169 float TerrainBounce = 0.1f;
170 float TerrainFriction = 0.3f;
171
172 public float 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,float cfm,float erp)
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 = cfm;
549 newcontact.surface.soft_erp = erp;
550
551 IntPtr contact = new IntPtr(GlobalContactsArray.ToInt64() + (Int64)(m_global_contactcount * d.Contact.unmanagedSizeOf));
552 Marshal.StructureToPtr(newcontact, contact, true);
553 return d.JointCreateContactPtr(world, contactgroup, contact);
554 }
555
556
557
558 private bool GetCurContactGeom(int index, ref d.ContactGeom newcontactgeom)
559 {
560 if (ContactgeomsArray == IntPtr.Zero || index >= contactsPerCollision)
561 return false;
562
563 IntPtr contactptr = new IntPtr(ContactgeomsArray.ToInt64() + (Int64)(index * d.ContactGeom.unmanagedSizeOf));
564 newcontactgeom = (d.ContactGeom)Marshal.PtrToStructure(contactptr, typeof(d.ContactGeom));
565 return true;
566 }
567
568 /// <summary>
569 /// This is our near callback. A geometry is near a body
570 /// </summary>
571 /// <param name="space">The space that contains the geoms. Remember, spaces are also geoms</param>
572 /// <param name="g1">a geometry or space</param>
573 /// <param name="g2">another geometry or space</param>
574 ///
575
576 private void near(IntPtr space, IntPtr g1, IntPtr g2)
577 {
578 // no lock here! It's invoked from within Simulate(), which is thread-locked
579
580 if (m_global_contactcount >= maxContactsbeforedeath)
581 return;
582
583 // Test if we're colliding a geom with a space.
584 // If so we have to drill down into the space recursively
585
586 if (g1 == IntPtr.Zero || g2 == IntPtr.Zero)
587 return;
588
589 if (d.GeomIsSpace(g1) || d.GeomIsSpace(g2))
590 {
591 // We'll be calling near recursivly if one
592 // of them is a space to find all of the
593 // contact points in the space
594 try
595 {
596 d.SpaceCollide2(g1, g2, IntPtr.Zero, nearCallback);
597 }
598 catch (AccessViolationException)
599 {
600 m_log.Warn("[PHYSICS]: Unable to collide test a space");
601 return;
602 }
603 //here one should check collisions of geoms inside a space
604 // but on each space we only should have geoms that not colide amoung each other
605 // so we don't dig inside spaces
606 return;
607 }
608
609 // get geom bodies to check if we already a joint contact
610 // guess this shouldn't happen now
611 IntPtr b1 = d.GeomGetBody(g1);
612 IntPtr b2 = d.GeomGetBody(g2);
613
614 // d.GeomClassID id = d.GeomGetClass(g1);
615
616 // Figure out how many contact points we have
617 int count = 0;
618 try
619 {
620 // Colliding Geom To Geom
621 // This portion of the function 'was' blatantly ripped off from BoxStack.cs
622
623 if (g1 == g2)
624 return; // Can't collide with yourself
625
626 if (b1 != IntPtr.Zero && b2 != IntPtr.Zero && d.AreConnectedExcluding(b1, b2, d.JointType.Contact))
627 return;
628
629 count = d.CollidePtr(g1, g2, (contactsPerCollision & 0xffff), ContactgeomsArray, d.ContactGeom.unmanagedSizeOf);
630 }
631 catch (SEHException)
632 {
633 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.");
634// ode.drelease(world);
635 base.TriggerPhysicsBasedRestart();
636 }
637 catch (Exception e)
638 {
639 m_log.WarnFormat("[PHYSICS]: Unable to collide test an object: {0}", e.Message);
640 return;
641 }
642
643 // id contacts done
644 if (count == 0)
645 return;
646
647 // try get physical actors
648 PhysicsActor p1;
649 PhysicsActor p2;
650
651 if (!actor_name_map.TryGetValue(g1, out p1))
652 {
653 p1 = PANull;
654 }
655
656 if (!actor_name_map.TryGetValue(g2, out p2))
657 {
658 p2 = PANull;
659 }
660
661 // update actors collision score
662 if (p1.CollisionScore >= float.MaxValue - count)
663 p1.CollisionScore = 0;
664 p1.CollisionScore += count;
665
666 if (p2.CollisionScore >= float.MaxValue - count)
667 p2.CollisionScore = 0;
668 p2.CollisionScore += count;
669
670
671 // get first contact
672 d.ContactGeom curContact = new d.ContactGeom();
673 if (!GetCurContactGeom(0, ref curContact))
674 return;
675 // for now it's the one with max depth
676 ContactPoint maxDepthContact = new ContactPoint(
677 new Vector3(curContact.pos.X, curContact.pos.Y, curContact.pos.Z),
678 new Vector3(curContact.normal.X, curContact.normal.Y, curContact.normal.Z),
679 curContact.depth
680 );
681 // do volume detection case
682 if (
683 (p1 is OdePrim) && (((OdePrim)p1).m_isVolumeDetect) ||
684 (p2 is OdePrim) && (((OdePrim)p2).m_isVolumeDetect))
685 {
686 collision_accounting_events(p1, p2, maxDepthContact);
687 return;
688 }
689
690 // big messy collision analises
691 float mu = 0;
692 float bounce = 0;
693 float cfm = 0.0001f;
694 float erp = 0.1f;
695
696 ContactData contactdata1 = new ContactData(0, 0, false);
697 ContactData contactdata2 = new ContactData(0, 0, false);
698
699 String name = null;
700 bool dop1foot = false;
701 bool dop2foot = false;
702 bool ignore = false;
703
704 switch (p1.PhysicsActorType)
705 {
706 case (int)ActorTypes.Agent:
707 {
708 bounce = 0;
709 mu = 0;
710 cfm = 0.0001f;
711
712 switch (p2.PhysicsActorType)
713 {
714 case (int)ActorTypes.Agent:
715/*
716 p1.getContactData(ref contactdata1);
717 p2.getContactData(ref contactdata2);
718
719 mu = (float)Math.Sqrt(contactdata1.mu * contactdata2.mu);
720
721 if ((Math.Abs(p2.Velocity.X - p1.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y - p1.Velocity.Y) > 0.1f))
722 mu *= frictionMovementMult;
723*/
724 p1.CollidingObj = true;
725 p2.CollidingObj = true;
726 break;
727 case (int)ActorTypes.Prim:
728/*
729 p1.getContactData(ref contactdata1);
730 p2.getContactData(ref contactdata2);
731
732
733 mu = (float)Math.Sqrt(contactdata1.mu * contactdata2.mu);
734
735 if ((Math.Abs(p2.Velocity.X - p1.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y - p1.Velocity.Y) > 0.1f))
736 mu *= frictionMovementMult;
737 */
738 if (p2.Velocity.LengthSquared() > 0.0f)
739 p2.CollidingObj = true;
740
741 dop1foot = true;
742 break;
743 default:
744 ignore = true; // avatar to terrain and water ignored
745 break;
746 }
747 break;
748 }
749
750 case (int)ActorTypes.Prim:
751 switch (p2.PhysicsActorType)
752 {
753 case (int)ActorTypes.Agent:
754// p1.getContactData(ref contactdata1);
755// p2.getContactData(ref contactdata2);
756
757 bounce = 0;
758 mu = 0;
759 cfm = 0.0001f;
760/*
761 mu = (float)Math.Sqrt(contactdata1.mu * contactdata2.mu);
762
763 if ((Math.Abs(p2.Velocity.X - p1.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y - p1.Velocity.Y) > 0.1f))
764 mu *= frictionMovementMult;
765*/
766 dop2foot = true;
767 if (p1.Velocity.LengthSquared() > 0.0f)
768 p1.CollidingObj = true;
769 break;
770 case (int)ActorTypes.Prim:
771 if ((p1.Velocity - p2.Velocity).LengthSquared() > 0.0f)
772 {
773 p1.CollidingObj = true;
774 p2.CollidingObj = true;
775 }
776 p1.getContactData(ref contactdata1);
777 p2.getContactData(ref contactdata2);
778 bounce = contactdata1.bounce * contactdata2.bounce;
779 mu = (float)Math.Sqrt(contactdata1.mu * contactdata2.mu);
780
781 cfm = p1.Mass;
782 if (cfm > p2.Mass)
783 cfm = p2.Mass;
784 cfm = (float)Math.Sqrt(cfm);
785 cfm *= 0.0001f;
786 if (cfm > 0.8f)
787 cfm = 0.8f;
788
789 if ((Math.Abs(p2.Velocity.X - p1.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y - p1.Velocity.Y) > 0.1f))
790 mu *= frictionMovementMult;
791
792 break;
793 default:
794 if (geom_name_map.TryGetValue(g2, out name))
795 {
796 if (name == "Terrain")
797 {
798 p1.getContactData(ref contactdata1);
799 bounce = contactdata1.bounce * TerrainBounce;
800 mu = (float)Math.Sqrt(contactdata1.mu * TerrainFriction);
801 if (Math.Abs(p1.Velocity.X) > 0.1f || Math.Abs(p1.Velocity.Y) > 0.1f)
802 mu *= frictionMovementMult;
803 p1.CollidingGround = true;
804 cfm = p1.Mass;
805 cfm = (float)Math.Sqrt(cfm);
806 cfm *= 0.0001f;
807 if (cfm > 0.8f)
808 cfm = 0.8f;
809
810 }
811 else if (name == "Water")
812 {
813 ignore = true;
814 }
815 }
816 else
817 ignore=true;
818 break;
819 }
820 break;
821
822 default:
823 if (geom_name_map.TryGetValue(g1, out name))
824 {
825 if (name == "Terrain")
826 {
827 if (p2.PhysicsActorType == (int)ActorTypes.Prim)
828 {
829 p2.CollidingGround = true;
830 p2.getContactData(ref contactdata2);
831 bounce = contactdata2.bounce * TerrainBounce;
832 mu = (float)Math.Sqrt(contactdata2.mu * TerrainFriction);
833 cfm = p2.Mass;
834 cfm = (float)Math.Sqrt(cfm);
835 cfm *= 0.0001f;
836 if (cfm > 0.8f)
837 cfm = 0.8f;
838
839 if (Math.Abs(p2.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y) > 0.1f)
840 mu *= frictionMovementMult;
841 }
842 else
843 ignore = true;
844
845 }
846 else if (name == "Water" &&
847 (p2.PhysicsActorType == (int)ActorTypes.Prim || p2.PhysicsActorType == (int)ActorTypes.Agent))
848 {
849 ignore = true;
850 }
851 }
852 else
853 ignore = true;
854 break;
855 }
856
857 if (ignore)
858 return;
859
860 IntPtr Joint;
861
862 int i = 0;
863 while(true)
864 {
865 if (dop1foot && (p1.Position.Z - curContact.pos.Z) > (p1.Size.Z - avCapRadius) * 0.5f)
866 p1.IsColliding = true;
867 if (dop2foot && (p2.Position.Z - curContact.pos.Z) > (p2.Size.Z - avCapRadius) * 0.5f)
868 p2.IsColliding = true;
869
870
871 erp = curContact.depth;
872 if (erp < minERP)
873 erp = minERP;
874 else if (erp > MaxERP)
875 erp = MaxERP;
876
877 Joint = CreateContacJoint(ref curContact, mu, bounce,cfm,erp);
878 d.JointAttach(Joint, b1, b2);
879
880 if (++m_global_contactcount >= maxContactsbeforedeath)
881 break;
882
883 if(++i >= count)
884 break;
885
886 if (!GetCurContactGeom(i, ref curContact))
887 break;
888
889 if (curContact.depth > maxDepthContact.PenetrationDepth)
890 {
891 maxDepthContact.Position.X = curContact.pos.X;
892 maxDepthContact.Position.Y = curContact.pos.Y;
893 maxDepthContact.Position.Z = curContact.pos.Z;
894 maxDepthContact.SurfaceNormal.X = curContact.normal.X;
895 maxDepthContact.SurfaceNormal.Y = curContact.normal.Y;
896 maxDepthContact.SurfaceNormal.Z = curContact.normal.Z;
897 maxDepthContact.PenetrationDepth = curContact.depth;
898 }
899 }
900
901 collision_accounting_events(p1, p2, maxDepthContact);
902
903/*
904 if (notskipedcount > geomContactPointsStartthrottle)
905 {
906 // If there are more then 3 contact points, it's likely
907 // that we've got a pile of objects, so ...
908 // We don't want to send out hundreds of terse updates over and over again
909 // so lets throttle them and send them again after it's somewhat sorted out.
910 this needs checking so out for now
911 if (b1 != IntPtr.Zero)
912 p1.ThrottleUpdates = true;
913 if (b2 != IntPtr.Zero)
914 p2.ThrottleUpdates = true;
915
916 }
917 */
918 }
919
920 private void collision_accounting_events(PhysicsActor p1, PhysicsActor p2, ContactPoint contact)
921 {
922 // obj1LocalID = 0;
923 //returncollisions = false;
924 obj2LocalID = 0;
925 //ctype = 0;
926 //cStartStop = 0;
927 if (!(p2.SubscribedEvents() || p1.SubscribedEvents()))
928 return;
929
930 switch ((ActorTypes)p1.PhysicsActorType)
931 {
932 case ActorTypes.Agent:
933 cc1 = (OdeCharacter)p1;
934 switch ((ActorTypes)p2.PhysicsActorType)
935 {
936 case ActorTypes.Agent:
937 cc2 = (OdeCharacter)p2;
938 obj2LocalID = cc2.m_localID;
939 if (p2.SubscribedEvents())
940 cc2.AddCollisionEvent(cc1.m_localID, contact);
941 break;
942
943 case ActorTypes.Prim:
944 if (p2 is OdePrim)
945 {
946 cp2 = (OdePrim)p2;
947 obj2LocalID = cp2.m_localID;
948 if (p2.SubscribedEvents())
949 cp2.AddCollisionEvent(cc1.m_localID, contact);
950 }
951 break;
952
953 case ActorTypes.Ground:
954 case ActorTypes.Unknown:
955 default:
956 obj2LocalID = 0;
957 break;
958 }
959 if (p1.SubscribedEvents())
960 {
961 contact.SurfaceNormal = -contact.SurfaceNormal;
962 cc1.AddCollisionEvent(obj2LocalID, contact);
963 }
964 break;
965
966 case ActorTypes.Prim:
967
968 if (p1 is OdePrim)
969 {
970 cp1 = (OdePrim)p1;
971
972 // obj1LocalID = cp2.m_localID;
973 switch ((ActorTypes)p2.PhysicsActorType)
974 {
975 case ActorTypes.Agent:
976 if (p2 is OdeCharacter)
977 {
978 cc2 = (OdeCharacter)p2;
979 obj2LocalID = cc2.m_localID;
980 if (p2.SubscribedEvents())
981 cc2.AddCollisionEvent(cp1.m_localID, contact);
982 }
983 break;
984 case ActorTypes.Prim:
985
986 if (p2 is OdePrim)
987 {
988 cp2 = (OdePrim)p2;
989 obj2LocalID = cp2.m_localID;
990 if (p2.SubscribedEvents())
991 cp2.AddCollisionEvent(cp1.m_localID, contact);
992 }
993 break;
994
995 case ActorTypes.Ground:
996 case ActorTypes.Unknown:
997 default:
998 obj2LocalID = 0;
999 break;
1000 }
1001 if (p1.SubscribedEvents())
1002 {
1003 contact.SurfaceNormal = -contact.SurfaceNormal;
1004 cp1.AddCollisionEvent(obj2LocalID, contact);
1005 }
1006 }
1007 break;
1008 }
1009 }
1010
1011 /// <summary>
1012 /// This is our collision testing routine in ODE
1013 /// </summary>
1014 /// <param name="timeStep"></param>
1015 private void collision_optimized()
1016 {
1017 lock (_characters)
1018 {
1019 try
1020 {
1021 foreach (OdeCharacter chr in _characters)
1022 {
1023 if (chr == null || chr.Shell == IntPtr.Zero || chr.Body == IntPtr.Zero)
1024 continue;
1025
1026 chr.IsColliding = false;
1027 // chr.CollidingGround = false; not done here
1028 chr.CollidingObj = false;
1029 // do colisions with static space
1030 d.SpaceCollide2(StaticSpace, chr.Shell, IntPtr.Zero, nearCallback);
1031 }
1032 }
1033 catch (AccessViolationException)
1034 {
1035 m_log.Warn("[PHYSICS]: Unable to collide Character to static space");
1036 }
1037
1038 }
1039
1040 lock (_activeprims)
1041 {
1042 foreach (OdePrim aprim in _activeprims)
1043 {
1044 aprim.CollisionScore = 0;
1045 aprim.IsColliding = false;
1046 }
1047 }
1048
1049 // collide active prims with static enviroment
1050 lock (_activegroups)
1051 {
1052 try
1053 {
1054 foreach (OdePrim prm in _activegroups)
1055 {
1056 if (d.BodyIsEnabled(prm.Body) && !prm.m_outbounds)
1057 d.SpaceCollide2(StaticSpace, prm.collide_geom, IntPtr.Zero, nearCallback);
1058 }
1059 }
1060 catch (AccessViolationException)
1061 {
1062 m_log.Warn("[PHYSICS]: Unable to collide Active prim to static space");
1063 }
1064 }
1065 // finally colide active things amoung them
1066 try
1067 {
1068 d.SpaceCollide(ActiveSpace, IntPtr.Zero, nearCallback);
1069 }
1070 catch (AccessViolationException)
1071 {
1072 m_log.Warn("[PHYSICS]: Unable to collide in Active space");
1073 }
1074// _perloopContact.Clear();
1075 }
1076
1077 #endregion
1078
1079
1080
1081 /// <summary>
1082 /// Add actor to the list that should receive collision events in the simulate loop.
1083 /// </summary>
1084 /// <param name="obj"></param>
1085 public void AddCollisionEventReporting(PhysicsActor obj)
1086 {
1087 lock (_collisionEventPrim)
1088 {
1089 if (!_collisionEventPrim.Contains(obj))
1090 _collisionEventPrim.Add(obj);
1091 }
1092 }
1093
1094 /// <summary>
1095 /// Remove actor from the list that should receive collision events in the simulate loop.
1096 /// </summary>
1097 /// <param name="obj"></param>
1098 public void RemoveCollisionEventReporting(PhysicsActor obj)
1099 {
1100 lock (_collisionEventPrim)
1101 {
1102 if (_collisionEventPrim.Contains(obj))
1103 _collisionEventPrim.Remove(obj);
1104 }
1105 }
1106
1107 #region Add/Remove Entities
1108
1109 public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
1110 {
1111 Vector3 pos;
1112 pos.X = position.X;
1113 pos.Y = position.Y;
1114 pos.Z = position.Z;
1115 OdeCharacter newAv = new OdeCharacter(avName, this, pos, size, avPIDD, avPIDP, avCapRadius, avDensity, avMovementDivisorWalk, avMovementDivisorRun);
1116 newAv.Flying = isFlying;
1117 newAv.MinimumGroundFlightOffset = minimumGroundFlightOffset;
1118
1119 return newAv;
1120 }
1121
1122 public void AddCharacter(OdeCharacter chr)
1123 {
1124 lock (_characters)
1125 {
1126 if (!_characters.Contains(chr))
1127 {
1128 _characters.Add(chr);
1129 if (chr.bad)
1130 m_log.DebugFormat("[PHYSICS] Added BAD actor {0} to characters list", chr.m_uuid);
1131 }
1132 }
1133 }
1134
1135 public void RemoveCharacter(OdeCharacter chr)
1136 {
1137 lock (_characters)
1138 {
1139 if (_characters.Contains(chr))
1140 {
1141 _characters.Remove(chr);
1142 }
1143 }
1144 }
1145
1146 public void BadCharacter(OdeCharacter chr)
1147 {
1148 lock (_badCharacter)
1149 {
1150 if (!_badCharacter.Contains(chr))
1151 _badCharacter.Add(chr);
1152 }
1153 }
1154
1155 public override void RemoveAvatar(PhysicsActor actor)
1156 {
1157 //m_log.Debug("[PHYSICS]:ODELOCK");
1158 ((OdeCharacter) actor).Destroy();
1159 }
1160
1161 private PhysicsActor AddPrim(String name, Vector3 position, Vector3 size, Quaternion rotation,
1162 PrimitiveBaseShape pbs, bool isphysical, uint localID)
1163 {
1164 Vector3 pos = position;
1165 Vector3 siz = size;
1166 Quaternion rot = rotation;
1167
1168 OdePrim newPrim;
1169 lock (OdeLock)
1170 {
1171 newPrim = new OdePrim(name, this, pos, siz, rot, pbs, isphysical,false,0,localID);
1172
1173 lock (_prims)
1174 _prims.Add(newPrim);
1175 }
1176 return newPrim;
1177 }
1178
1179 private PhysicsActor AddPrim(String name, Vector3 position, Vector3 size, Quaternion rotation,
1180 PrimitiveBaseShape pbs, bool isphysical, bool isPhantom, uint localID)
1181 {
1182 Vector3 pos = position;
1183 Vector3 siz = size;
1184 Quaternion rot = rotation;
1185
1186 OdePrim newPrim;
1187 lock (OdeLock)
1188 {
1189 newPrim = new OdePrim(name, this, pos, siz, rot, pbs, isphysical, isPhantom, 0, localID);
1190
1191 lock (_prims)
1192 _prims.Add(newPrim);
1193 }
1194 return newPrim;
1195 }
1196
1197 private PhysicsActor AddPrim(String name, Vector3 position, Vector3 size, Quaternion rotation,
1198 PrimitiveBaseShape pbs, bool isphysical, bool isPhantom, byte shapeType, uint localID)
1199 {
1200 Vector3 pos = position;
1201 Vector3 siz = size;
1202 Quaternion rot = rotation;
1203
1204 OdePrim newPrim;
1205 lock (OdeLock)
1206 {
1207 newPrim = new OdePrim(name, this, pos, siz, rot, pbs, isphysical, isPhantom, shapeType, localID);
1208
1209 lock (_prims)
1210 _prims.Add(newPrim);
1211 }
1212 return newPrim;
1213 }
1214
1215 public void addActivePrim(OdePrim activatePrim)
1216 {
1217 // adds active prim..
1218 lock (_activeprims)
1219 {
1220 if (!_activeprims.Contains(activatePrim))
1221 _activeprims.Add(activatePrim);
1222 }
1223 }
1224
1225 public void addActiveGroups(OdePrim activatePrim)
1226 {
1227 lock (_activegroups)
1228 {
1229 if (!_activegroups.Contains(activatePrim))
1230 _activegroups.Add(activatePrim);
1231 }
1232 }
1233
1234 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
1235 Vector3 size, Quaternion rotation, bool isPhysical, bool isPhantom, uint localid)
1236 {
1237 return AddPrim(primName, position, size, rotation, pbs, isPhysical, isPhantom, localid);
1238 }
1239
1240
1241 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
1242 Vector3 size, Quaternion rotation, bool isPhysical, uint localid)
1243 {
1244#if SPAM
1245 m_log.DebugFormat("[PHYSICS]: Adding physics actor to {0}", primName);
1246#endif
1247
1248 return AddPrim(primName, position, size, rotation, pbs, isPhysical, localid);
1249 }
1250
1251 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
1252 Vector3 size, Quaternion rotation, bool isPhysical, bool isPhantom, byte shapeType, uint localid)
1253 {
1254#if SPAM
1255 m_log.DebugFormat("[PHYSICS]: Adding physics actor to {0}", primName);
1256#endif
1257
1258 return AddPrim(primName, position, size, rotation, pbs, isPhysical,isPhantom, shapeType, localid);
1259 }
1260
1261 public override float TimeDilation
1262 {
1263 get { return m_timeDilation; }
1264 }
1265
1266 public override bool SupportsNINJAJoints
1267 {
1268 get { return false; }
1269 }
1270
1271
1272 public void remActivePrim(OdePrim deactivatePrim)
1273 {
1274 lock (_activeprims)
1275 {
1276 _activeprims.Remove(deactivatePrim);
1277 }
1278 }
1279 public void remActiveGroup(OdePrim deactivatePrim)
1280 {
1281 lock (_activegroups)
1282 {
1283 _activegroups.Remove(deactivatePrim);
1284 }
1285 }
1286
1287 public override void RemovePrim(PhysicsActor prim)
1288 {
1289 // As with all ODE physics operations, we don't remove the prim immediately but signal that it should be
1290 // removed in the next physics simulate pass.
1291 if (prim is OdePrim)
1292 {
1293// lock (OdeLock)
1294 {
1295 OdePrim p = (OdePrim)prim;
1296 p.setPrimForRemoval();
1297 }
1298 }
1299 }
1300 /// <summary>
1301 /// This is called from within simulate but outside the locked portion
1302 /// We need to do our own locking here
1303 /// (Note: As of 20110801 this no longer appears to be true - this is being called within lock (odeLock) in
1304 /// Simulate() -- justincc).
1305 ///
1306 /// Essentially, we need to remove the prim from our space segment, whatever segment it's in.
1307 ///
1308 /// If there are no more prim in the segment, we need to empty (spacedestroy)the segment and reclaim memory
1309 /// that the space was using.
1310 /// </summary>
1311 /// <param name="prim"></param>
1312 public void RemovePrimThreadLocked(OdePrim prim)
1313 {
1314 //Console.WriteLine("RemovePrimThreadLocked " + prim.m_primName);
1315 lock (prim)
1316 {
1317 RemoveCollisionEventReporting(prim);
1318 lock (_prims)
1319 _prims.Remove(prim);
1320 }
1321
1322 }
1323 #endregion
1324
1325 #region Space Separation Calculation
1326
1327 /// <summary>
1328 /// Called when a static prim moves or becomes static
1329 /// Places the prim in a space one the static sub-spaces grid
1330 /// </summary>
1331 /// <param name="geom">the pointer to the geom that moved</param>
1332 /// <param name="pos">the position that the geom moved to</param>
1333 /// <param name="currentspace">a pointer to the space it was in before it was moved.</param>
1334 /// <returns>a pointer to the new space it's in</returns>
1335 public IntPtr MoveGeomToStaticSpace(IntPtr geom, Vector3 pos, IntPtr currentspace)
1336 {
1337 // moves a prim into another static sub-space or from another space into a static sub-space
1338
1339 // Called ODEPrim so
1340 // it's already in locked space.
1341
1342 if (geom == IntPtr.Zero) // shouldn't happen
1343 return IntPtr.Zero;
1344
1345 // get the static sub-space for current position
1346 IntPtr newspace = calculateSpaceForGeom(pos);
1347
1348 if (newspace == currentspace) // if we are there all done
1349 return newspace;
1350
1351 // else remove it from its current space
1352 if (currentspace != IntPtr.Zero && d.SpaceQuery(currentspace, geom))
1353 {
1354 if (d.GeomIsSpace(currentspace))
1355 {
1356 waitForSpaceUnlock(currentspace);
1357 d.SpaceRemove(currentspace, geom);
1358
1359 if (d.SpaceGetSublevel(currentspace) > 2 && d.SpaceGetNumGeoms(currentspace) == 0)
1360 {
1361 d.SpaceDestroy(currentspace);
1362 }
1363 }
1364 else
1365 {
1366 m_log.Info("[Physics]: Invalid or empty Space passed to 'MoveGeomToStaticSpace':" + currentspace +
1367 " Geom:" + geom);
1368 }
1369 }
1370 else // odd currentspace is null or doesn't contain the geom? lets try the geom ideia of current space
1371 {
1372 currentspace = d.GeomGetSpace(geom);
1373 if (currentspace != IntPtr.Zero)
1374 {
1375 if (d.GeomIsSpace(currentspace))
1376 {
1377 waitForSpaceUnlock(currentspace);
1378 d.SpaceRemove(currentspace, geom);
1379
1380 if (d.SpaceGetSublevel(currentspace) > 2 && d.SpaceGetNumGeoms(currentspace) == 0)
1381 {
1382 d.SpaceDestroy(currentspace);
1383 }
1384
1385 }
1386 }
1387 }
1388
1389 // put the geom in the newspace
1390 waitForSpaceUnlock(newspace);
1391 d.SpaceAdd(newspace, geom);
1392
1393 // let caller know this newspace
1394 return newspace;
1395 }
1396
1397 /// <summary>
1398 /// Calculates the space the prim should be in by its position
1399 /// </summary>
1400 /// <param name="pos"></param>
1401 /// <returns>a pointer to the space. This could be a new space or reused space.</returns>
1402 public IntPtr calculateSpaceForGeom(Vector3 pos)
1403 {
1404 int x, y;
1405 x = (int)(pos.X * spacesPerMeter);
1406 if (x < 0)
1407 x = 0;
1408 else if (x > spaceGridMaxX)
1409 x = spaceGridMaxX;
1410
1411 y = (int)(pos.Y * spacesPerMeter);
1412 if (y < 0)
1413 y = 0;
1414 else if (y >spaceGridMaxY)
1415 y = spaceGridMaxY;
1416
1417 IntPtr tmpSpace = staticPrimspace[x, y];
1418 return tmpSpace;
1419 }
1420
1421 #endregion
1422
1423 /// <summary>
1424 /// Routine to figure out if we need to mesh this prim with our mesher
1425 /// </summary>
1426 /// <param name="pbs"></param>
1427 /// <returns></returns>
1428 public bool needsMeshing(PrimitiveBaseShape pbs)
1429 {
1430 // most of this is redundant now as the mesher will return null if it cant mesh a prim
1431 // but we still need to check for sculptie meshing being enabled so this is the most
1432 // convenient place to do it for now...
1433
1434 // //if (pbs.PathCurve == (byte)Primitive.PathCurve.Circle && pbs.ProfileCurve == (byte)Primitive.ProfileCurve.Circle && pbs.PathScaleY <= 0.75f)
1435 // //m_log.Debug("needsMeshing: " + " pathCurve: " + pbs.PathCurve.ToString() + " profileCurve: " + pbs.ProfileCurve.ToString() + " pathScaleY: " + Primitive.UnpackPathScale(pbs.PathScaleY).ToString());
1436 int iPropertiesNotSupportedDefault = 0;
1437
1438 if (pbs.SculptEntry)
1439 {
1440 if(!meshSculptedPrim)
1441 return false;
1442 }
1443
1444 // 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
1445 if (!forceSimplePrimMeshing && !pbs.SculptEntry)
1446 {
1447 if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
1448 || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1
1449 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z))
1450 {
1451
1452 if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
1453 && pbs.ProfileHollow == 0
1454 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
1455 && pbs.PathBegin == 0 && pbs.PathEnd == 0
1456 && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
1457 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
1458 && pbs.PathShearX == 0 && pbs.PathShearY == 0)
1459 {
1460#if SPAM
1461 m_log.Warn("NonMesh");
1462#endif
1463 return false;
1464 }
1465 }
1466 }
1467
1468 // following code doesn't give meshs to boxes and spheres ever
1469 // and it's odd.. so for now just return true if asked to force meshs
1470 // hopefully mesher will fail if doesn't suport so things still get basic boxes
1471
1472 if (forceSimplePrimMeshing)
1473 return true;
1474
1475 if (pbs.ProfileHollow != 0)
1476 iPropertiesNotSupportedDefault++;
1477
1478 if ((pbs.PathBegin != 0) || pbs.PathEnd != 0)
1479 iPropertiesNotSupportedDefault++;
1480
1481 if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0))
1482 iPropertiesNotSupportedDefault++;
1483
1484 if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0)
1485 iPropertiesNotSupportedDefault++;
1486
1487 if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100))
1488 iPropertiesNotSupportedDefault++;
1489
1490 if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0))
1491 iPropertiesNotSupportedDefault++;
1492
1493 if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight)
1494 iPropertiesNotSupportedDefault++;
1495
1496 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))
1497 iPropertiesNotSupportedDefault++;
1498
1499 if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1)
1500 iPropertiesNotSupportedDefault++;
1501
1502 // test for torus
1503 if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square)
1504 {
1505 if (pbs.PathCurve == (byte)Extrusion.Curve1)
1506 {
1507 iPropertiesNotSupportedDefault++;
1508 }
1509 }
1510 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
1511 {
1512 if (pbs.PathCurve == (byte)Extrusion.Straight)
1513 {
1514 iPropertiesNotSupportedDefault++;
1515 }
1516
1517 // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits
1518 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
1519 {
1520 iPropertiesNotSupportedDefault++;
1521 }
1522 }
1523 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
1524 {
1525 if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2)
1526 {
1527 iPropertiesNotSupportedDefault++;
1528 }
1529 }
1530 else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
1531 {
1532 if (pbs.PathCurve == (byte)Extrusion.Straight)
1533 {
1534 iPropertiesNotSupportedDefault++;
1535 }
1536 else if (pbs.PathCurve == (byte)Extrusion.Curve1)
1537 {
1538 iPropertiesNotSupportedDefault++;
1539 }
1540 }
1541
1542 if (pbs.SculptEntry && meshSculptedPrim)
1543 iPropertiesNotSupportedDefault++;
1544
1545 if (iPropertiesNotSupportedDefault == 0)
1546 {
1547#if SPAM
1548 m_log.Warn("NonMesh");
1549#endif
1550 return false;
1551 }
1552#if SPAM
1553 m_log.Debug("Mesh");
1554#endif
1555 return true;
1556 }
1557
1558 /// <summary>
1559 /// Called to queue a change to a actor
1560 /// to use in place of old taint mechanism so changes do have a time sequence
1561 /// </summary>
1562
1563 public void AddChange(PhysicsActor actor, changes what, Object arg)
1564 {
1565 ODEchangeitem item = new ODEchangeitem();
1566 item.actor = actor;
1567 item.what = what;
1568 item.arg = arg;
1569 ChangesQueue.Enqueue(item);
1570 }
1571
1572 /// <summary>
1573 /// Called after our prim properties are set Scale, position etc.
1574 /// We use this event queue like method to keep changes to the physical scene occuring in the threadlocked mutex
1575 /// This assures us that we have no race conditions
1576 /// </summary>
1577 /// <param name="prim"></param>
1578 public override void AddPhysicsActorTaint(PhysicsActor prim)
1579 {
1580 }
1581
1582 /// <summary>
1583 /// This is our main simulate loop
1584 /// It's thread locked by a Mutex in the scene.
1585 /// It holds Collisions, it instructs ODE to step through the physical reactions
1586 /// It moves the objects around in memory
1587 /// It calls the methods that report back to the object owners.. (scenepresence, SceneObjectGroup)
1588 /// </summary>
1589 /// <param name="timeStep"></param>
1590 /// <returns></returns>
1591 public override float Simulate(float timeStep)
1592 {
1593 int statstart;
1594 int statchanges = 0;
1595 int statchmove = 0;
1596 int statactmove = 0;
1597 int statray = 0;
1598 int statcol = 0;
1599 int statstep = 0;
1600 int statmovchar = 0;
1601 int statmovprim;
1602 int totjcontact = 0;
1603
1604 // acumulate time so we can reduce error
1605 step_time += timeStep;
1606
1607 if (step_time < ODE_STEPSIZE)
1608 return 0;
1609
1610 if (framecount >= int.MaxValue)
1611 framecount = 0;
1612
1613 framecount++;
1614
1615 int curphysiteractions = m_physicsiterations;
1616
1617 if (step_time >= m_SkipFramesAtms)
1618 {
1619 // if in trouble reduce step resolution
1620 curphysiteractions /= 2;
1621 }
1622
1623 int nodeframes = 0;
1624
1625// checkThread();
1626
1627 lock (SimulationLock)
1628 {
1629 // adjust number of iterations per step
1630 try
1631 {
1632 d.WorldSetQuickStepNumIterations(world, curphysiteractions);
1633 }
1634 catch (StackOverflowException)
1635 {
1636 m_log.Error("[PHYSICS]: The operating system wasn't able to allocate enough memory for the simulation. Restarting the sim.");
1637// ode.drelease(world);
1638 base.TriggerPhysicsBasedRestart();
1639 }
1640
1641
1642 while (step_time >= ODE_STEPSIZE && nodeframes < 10) //limit number of steps so we don't say here for ever
1643 {
1644 try
1645 {
1646 statstart = Util.EnvironmentTickCount();
1647
1648 // clear pointer/counter to contacts to pass into joints
1649 m_global_contactcount = 0;
1650
1651 ODEchangeitem item;
1652
1653 if(ChangesQueue.Count >0)
1654 {
1655 int ttmpstart = Util.EnvironmentTickCount();
1656 int ttmp;
1657 int ttmp2;
1658
1659 while(ChangesQueue.Dequeue(out item))
1660 {
1661 if (item.actor != null)
1662 {
1663 try
1664 {
1665 if (item.actor is OdeCharacter)
1666 ((OdeCharacter)item.actor).DoAChange(item.what, item.arg);
1667 else if (((OdePrim)item.actor).DoAChange(item.what, item.arg))
1668 RemovePrimThreadLocked((OdePrim)item.actor);
1669 }
1670 catch
1671 {
1672 m_log.Warn("[PHYSICS]: doChange failed for a actor");
1673 };
1674 }
1675 ttmp = Util.EnvironmentTickCountSubtract(ttmpstart);
1676 if (ttmp > 20)
1677 break;
1678 }
1679
1680 ttmp2 = Util.EnvironmentTickCountSubtract(ttmpstart);
1681 if (ttmp2 > 50)
1682 ttmp2 = 0;
1683
1684 }
1685
1686 statchanges += Util.EnvironmentTickCountSubtract(statstart);
1687
1688 statactmove += Util.EnvironmentTickCountSubtract(statstart);
1689 //if ((framecount % m_randomizeWater) == 0)
1690 // randomizeWater(waterlevel);
1691
1692 m_rayCastManager.ProcessQueuedRequests();
1693
1694
1695
1696 statray += Util.EnvironmentTickCountSubtract(statstart);
1697 collision_optimized();
1698 statcol += Util.EnvironmentTickCountSubtract(statstart);
1699
1700 lock (_collisionEventPrim)
1701 {
1702 foreach (PhysicsActor obj in _collisionEventPrim)
1703 {
1704 if (obj == null)
1705 continue;
1706
1707 switch ((ActorTypes)obj.PhysicsActorType)
1708 {
1709 case ActorTypes.Agent:
1710 OdeCharacter cobj = (OdeCharacter)obj;
1711 cobj.AddCollisionFrameTime((int)(ODE_STEPSIZE*1000.0f));
1712 cobj.SendCollisions();
1713 break;
1714
1715 case ActorTypes.Prim:
1716 OdePrim pobj = (OdePrim)obj;
1717 pobj.SendCollisions();
1718 break;
1719 }
1720 }
1721 }
1722
1723 d.WorldQuickStep(world, ODE_STEPSIZE);
1724 statstep += Util.EnvironmentTickCountSubtract(statstart);
1725
1726 // Move characters
1727 lock (_characters)
1728 {
1729 List<OdeCharacter> defects = new List<OdeCharacter>();
1730 foreach (OdeCharacter actor in _characters)
1731 {
1732 if (actor != null)
1733 actor.Move(ODE_STEPSIZE, defects);
1734 }
1735 if (defects.Count != 0)
1736 {
1737 foreach (OdeCharacter defect in defects)
1738 {
1739 RemoveCharacter(defect);
1740 }
1741 }
1742 }
1743 statchmove += Util.EnvironmentTickCountSubtract(statstart);
1744
1745 // Move other active objects
1746 lock (_activegroups)
1747 {
1748 foreach (OdePrim aprim in _activegroups)
1749 {
1750 aprim.Move();
1751 }
1752 }
1753
1754 //ode.dunlock(world);
1755 }
1756 catch (Exception e)
1757 {
1758 m_log.ErrorFormat("[PHYSICS]: {0}, {1}, {2}", e.Message, e.TargetSite, e);
1759// ode.dunlock(world);
1760 }
1761
1762 d.JointGroupEmpty(contactgroup);
1763 totjcontact += m_global_contactcount;
1764
1765 step_time -= ODE_STEPSIZE;
1766 nodeframes++;
1767 }
1768
1769 statstart = Util.EnvironmentTickCount();
1770
1771 lock (_characters)
1772 {
1773 foreach (OdeCharacter actor in _characters)
1774 {
1775 if (actor != null)
1776 {
1777 if (actor.bad)
1778 m_log.WarnFormat("[PHYSICS]: BAD Actor {0} in _characters list was not removed?", actor.m_uuid);
1779
1780 actor.UpdatePositionAndVelocity();
1781 }
1782 }
1783 }
1784
1785 lock (_badCharacter)
1786 {
1787 if (_badCharacter.Count > 0)
1788 {
1789 foreach (OdeCharacter chr in _badCharacter)
1790 {
1791 RemoveCharacter(chr);
1792 }
1793
1794 _badCharacter.Clear();
1795 }
1796 }
1797 statmovchar = Util.EnvironmentTickCountSubtract(statstart);
1798
1799 lock (_activegroups)
1800 {
1801 {
1802 foreach (OdePrim actor in _activegroups)
1803 {
1804 if (actor.IsPhysical)
1805 {
1806 actor.UpdatePositionAndVelocity((float)nodeframes * ODE_STEPSIZE);
1807 }
1808 }
1809 }
1810 }
1811
1812 statmovprim = Util.EnvironmentTickCountSubtract(statstart);
1813
1814 int nactivegeoms = d.SpaceGetNumGeoms(ActiveSpace);
1815 int nstaticgeoms = d.SpaceGetNumGeoms(StaticSpace);
1816 int ntopgeoms = d.SpaceGetNumGeoms(TopSpace);
1817 int nbodies = d.NTotalBodies;
1818 int ngeoms = d.NTotalGeoms;
1819
1820 // Finished with all sim stepping. If requested, dump world state to file for debugging.
1821 // TODO: This call to the export function is already inside lock (OdeLock) - but is an extra lock needed?
1822 // TODO: This overwrites all dump files in-place. Should this be a growing logfile, or separate snapshots?
1823 if (physics_logging && (physics_logging_interval > 0) && (framecount % physics_logging_interval == 0))
1824 {
1825 string fname = "state-" + world.ToString() + ".DIF"; // give each physics world a separate filename
1826 string prefix = "world" + world.ToString(); // prefix for variable names in exported .DIF file
1827
1828 if (physics_logging_append_existing_logfile)
1829 {
1830 string header = "-------------- START OF PHYSICS FRAME " + framecount.ToString() + " --------------";
1831 TextWriter fwriter = File.AppendText(fname);
1832 fwriter.WriteLine(header);
1833 fwriter.Close();
1834 }
1835
1836 d.WorldExportDIF(world, fname, physics_logging_append_existing_logfile, prefix);
1837 }
1838
1839 // think time dilation is not a physics issue alone.. but ok let's fake something
1840 if (step_time < ODE_STEPSIZE) // we did the required loops
1841 m_timeDilation = 1.0f;
1842 else
1843 { // we didn't forget the lost ones and let user know something
1844 m_timeDilation = 1 - step_time / timeStep;
1845 if (m_timeDilation < 0)
1846 m_timeDilation = 0;
1847 step_time = 0;
1848 }
1849 }
1850
1851// return nodeframes * ODE_STEPSIZE; // return real simulated time
1852 return 1000 * nodeframes; // return steps for now * 1000 to keep core happy
1853 }
1854
1855 /// <summary>
1856 public override void GetResults()
1857 {
1858 }
1859
1860 public override bool IsThreaded
1861 {
1862 // for now we won't be multithreaded
1863 get { return (false); }
1864 }
1865
1866 public float GetTerrainHeightAtXY(float x, float y)
1867 {
1868 // assumes 1m size grid and constante size square regions
1869 // needs to know about sims around in future
1870 // region offset in mega position
1871
1872 int offsetX = ((int)(x / (int)Constants.RegionSize)) * (int)Constants.RegionSize;
1873 int offsetY = ((int)(y / (int)Constants.RegionSize)) * (int)Constants.RegionSize;
1874
1875 IntPtr heightFieldGeom = IntPtr.Zero;
1876
1877 // get region map
1878 if (!RegionTerrain.TryGetValue(new Vector3(offsetX, offsetY, 0), out heightFieldGeom))
1879 return 0f;
1880
1881 if (heightFieldGeom == IntPtr.Zero)
1882 return 0f;
1883
1884 if (!TerrainHeightFieldHeights.ContainsKey(heightFieldGeom))
1885 return 0f;
1886
1887 // TerrainHeightField for ODE as offset 1m
1888 x += 1f - offsetX;
1889 y += 1f - offsetY;
1890
1891 // make position fit into array
1892 if (x < 0)
1893 x = 0;
1894 if (y < 0)
1895 y = 0;
1896
1897 // integer indexs
1898 int ix;
1899 int iy;
1900 // interpolators offset
1901 float dx;
1902 float dy;
1903
1904 int regsize = (int)Constants.RegionSize + 3; // map size see setterrain number of samples
1905
1906 // we still have square fixed size regions
1907 // also flip x and y because of how map is done for ODE fliped axis
1908 // so ix,iy,dx and dy are inter exchanged
1909 if (x < regsize - 1)
1910 {
1911 iy = (int)x;
1912 dy = x - (float)iy;
1913 }
1914 else // out world use external height
1915 {
1916 iy = regsize - 1;
1917 dy = 0;
1918 }
1919 if (y < regsize - 1)
1920 {
1921 ix = (int)y;
1922 dx = y - (float)ix;
1923 }
1924 else
1925 {
1926 ix = regsize - 1;
1927 dx = 0;
1928 }
1929
1930 float h0;
1931 float h1;
1932 float h2;
1933
1934 iy *= regsize;
1935 iy += ix; // all indexes have iy + ix
1936
1937 float[] heights = TerrainHeightFieldHeights[heightFieldGeom];
1938
1939 if ((dx + dy) <= 1.0f)
1940 {
1941 h0 = ((float)heights[iy]); // 0,0 vertice
1942 h1 = (((float)heights[iy + 1]) - h0) * dx; // 1,0 vertice minus 0,0
1943 h2 = (((float)heights[iy + regsize]) - h0) * dy; // 0,1 vertice minus 0,0
1944 }
1945 else
1946 {
1947 h0 = ((float)heights[iy + regsize + 1]); // 1,1 vertice
1948 h1 = (((float)heights[iy + 1]) - h0) * (1 - dy); // 1,1 vertice minus 1,0
1949 h2 = (((float)heights[iy + regsize]) - h0) * (1 - dx); // 1,1 vertice minus 0,1
1950 }
1951
1952 return h0 + h1 + h2;
1953 }
1954 public override void SetTerrain(float[] heightMap)
1955 {
1956 if (m_worldOffset != Vector3.Zero && m_parentScene != null)
1957 {
1958 if (m_parentScene is OdeScene)
1959 {
1960 ((OdeScene)m_parentScene).SetTerrain(heightMap, m_worldOffset);
1961 }
1962 }
1963 else
1964 {
1965 SetTerrain(heightMap, m_worldOffset);
1966 }
1967 }
1968
1969 public override void CombineTerrain(float[] heightMap, Vector3 pOffset)
1970 {
1971 SetTerrain(heightMap, pOffset);
1972 }
1973
1974 public void SetTerrain(float[] heightMap, Vector3 pOffset)
1975 {
1976 // assumes 1m size grid and constante size square regions
1977 // needs to know about sims around in future
1978
1979 float[] _heightmap;
1980
1981 uint heightmapWidth = Constants.RegionSize + 2;
1982 uint heightmapHeight = Constants.RegionSize + 2;
1983
1984 uint heightmapWidthSamples = heightmapWidth + 1;
1985 uint heightmapHeightSamples = heightmapHeight + 1;
1986
1987 _heightmap = new float[heightmapWidthSamples * heightmapHeightSamples];
1988
1989 const float scale = 1.0f;
1990 const float offset = 0.0f;
1991 const float thickness = 10f;
1992 const int wrap = 0;
1993
1994 uint regionsize = Constants.RegionSize;
1995
1996 float hfmin = float.MaxValue;
1997 float hfmax = float.MinValue;
1998 float val;
1999 uint xx;
2000 uint yy;
2001
2002 uint maxXXYY = regionsize - 1;
2003 // flipping map adding one margin all around so things don't fall in edges
2004
2005 uint xt = 0;
2006 xx = 0;
2007
2008 for (uint x = 0; x < heightmapWidthSamples; x++)
2009 {
2010 if (x > 1 && xx < maxXXYY)
2011 xx++;
2012 yy = 0;
2013 for (uint y = 0; y < heightmapHeightSamples; y++)
2014 {
2015 if (y > 1 && y < maxXXYY)
2016 yy += regionsize;
2017
2018 val = heightMap[yy + xx];
2019 if (val < 0.0f)
2020 val = 0.0f; // no neg terrain as in chode
2021 _heightmap[xt + y] = val;
2022
2023 if (hfmin > val)
2024 hfmin = val;
2025 if (hfmax < val)
2026 hfmax = val;
2027 }
2028 xt += heightmapHeightSamples;
2029 }
2030 lock (OdeLock)
2031 {
2032 IntPtr GroundGeom = IntPtr.Zero;
2033 if (RegionTerrain.TryGetValue(pOffset, out GroundGeom))
2034 {
2035 RegionTerrain.Remove(pOffset);
2036 if (GroundGeom != IntPtr.Zero)
2037 {
2038 if (TerrainHeightFieldHeights.ContainsKey(GroundGeom))
2039 {
2040 TerrainHeightFieldHeightsHandlers[GroundGeom].Free();
2041 TerrainHeightFieldHeightsHandlers.Remove(GroundGeom);
2042 TerrainHeightFieldHeights.Remove(GroundGeom);
2043 }
2044 d.SpaceRemove(StaticSpace, GroundGeom);
2045 d.GeomDestroy(GroundGeom);
2046 }
2047 }
2048 IntPtr HeightmapData = d.GeomHeightfieldDataCreate();
2049
2050 GCHandle _heightmaphandler = GCHandle.Alloc(_heightmap, GCHandleType.Pinned);
2051
2052 d.GeomHeightfieldDataBuildSingle(HeightmapData, _heightmaphandler.AddrOfPinnedObject(), 0, heightmapWidth , heightmapHeight,
2053 (int)heightmapWidthSamples, (int)heightmapHeightSamples, scale,
2054 offset, thickness, wrap);
2055
2056 d.GeomHeightfieldDataSetBounds(HeightmapData, hfmin - 1, hfmax + 1);
2057 GroundGeom = d.CreateHeightfield(StaticSpace, HeightmapData, 1);
2058 if (GroundGeom != IntPtr.Zero)
2059 {
2060 d.GeomSetCategoryBits(GroundGeom, (int)(CollisionCategories.Land));
2061 d.GeomSetCollideBits(GroundGeom, (int)(CollisionCategories.Space));
2062
2063 }
2064 geom_name_map[GroundGeom] = "Terrain";
2065
2066 d.Matrix3 R = new d.Matrix3();
2067
2068 Quaternion q1 = Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), 1.5707f);
2069 Quaternion q2 = Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), 1.5707f);
2070
2071
2072 q1 = q1 * q2;
2073
2074 Vector3 v3;
2075 float angle;
2076 q1.GetAxisAngle(out v3, out angle);
2077
2078 d.RFromAxisAndAngle(out R, v3.X, v3.Y, v3.Z, angle);
2079 d.GeomSetRotation(GroundGeom, ref R);
2080 d.GeomSetPosition(GroundGeom, pOffset.X + (float)Constants.RegionSize * 0.5f, pOffset.Y + (float)Constants.RegionSize * 0.5f, 0);
2081 RegionTerrain.Add(pOffset, GroundGeom, GroundGeom);
2082// TerrainHeightFieldHeights.Add(GroundGeom, ODElandMap);
2083 TerrainHeightFieldHeights.Add(GroundGeom, _heightmap);
2084 TerrainHeightFieldHeightsHandlers.Add(GroundGeom, _heightmaphandler);
2085
2086 }
2087 }
2088
2089 public override void DeleteTerrain()
2090 {
2091 }
2092
2093 public float GetWaterLevel()
2094 {
2095 return waterlevel;
2096 }
2097
2098 public override bool SupportsCombining()
2099 {
2100 return true;
2101 }
2102/*
2103 public override void UnCombine(PhysicsScene pScene)
2104 {
2105 IntPtr localGround = IntPtr.Zero;
2106// float[] localHeightfield;
2107 bool proceed = false;
2108 List<IntPtr> geomDestroyList = new List<IntPtr>();
2109
2110 lock (OdeLock)
2111 {
2112 if (RegionTerrain.TryGetValue(Vector3.Zero, out localGround))
2113 {
2114 foreach (IntPtr geom in TerrainHeightFieldHeights.Keys)
2115 {
2116 if (geom == localGround)
2117 {
2118// localHeightfield = TerrainHeightFieldHeights[geom];
2119 proceed = true;
2120 }
2121 else
2122 {
2123 geomDestroyList.Add(geom);
2124 }
2125 }
2126
2127 if (proceed)
2128 {
2129 m_worldOffset = Vector3.Zero;
2130 WorldExtents = new Vector2((int)Constants.RegionSize, (int)Constants.RegionSize);
2131 m_parentScene = null;
2132
2133 foreach (IntPtr g in geomDestroyList)
2134 {
2135 // removingHeightField needs to be done or the garbage collector will
2136 // collect the terrain data before we tell ODE to destroy it causing
2137 // memory corruption
2138 if (TerrainHeightFieldHeights.ContainsKey(g))
2139 {
2140// float[] removingHeightField = TerrainHeightFieldHeights[g];
2141 TerrainHeightFieldHeights.Remove(g);
2142
2143 if (RegionTerrain.ContainsKey(g))
2144 {
2145 RegionTerrain.Remove(g);
2146 }
2147
2148 d.GeomDestroy(g);
2149 //removingHeightField = new float[0];
2150 }
2151 }
2152
2153 }
2154 else
2155 {
2156 m_log.Warn("[PHYSICS]: Couldn't proceed with UnCombine. Region has inconsistant data.");
2157 }
2158 }
2159 }
2160 }
2161*/
2162 public override void SetWaterLevel(float baseheight)
2163 {
2164 waterlevel = baseheight;
2165 randomizeWater(waterlevel);
2166 }
2167
2168 public void randomizeWater(float baseheight)
2169 {
2170 const uint heightmapWidth = m_regionWidth + 2;
2171 const uint heightmapHeight = m_regionHeight + 2;
2172 const uint heightmapWidthSamples = m_regionWidth + 2;
2173 const uint heightmapHeightSamples = m_regionHeight + 2;
2174 const float scale = 1.0f;
2175 const float offset = 0.0f;
2176 const float thickness = 2.9f;
2177 const int wrap = 0;
2178
2179 for (int i = 0; i < (258 * 258); i++)
2180 {
2181 _watermap[i] = (baseheight-0.1f) + ((float)fluidRandomizer.Next(1,9) / 10f);
2182 // m_log.Info((baseheight - 0.1f) + ((float)fluidRandomizer.Next(1, 9) / 10f));
2183 }
2184
2185 lock (OdeLock)
2186 {
2187 if (WaterGeom != IntPtr.Zero)
2188 {
2189 d.SpaceRemove(StaticSpace, WaterGeom);
2190 }
2191 IntPtr HeightmapData = d.GeomHeightfieldDataCreate();
2192 d.GeomHeightfieldDataBuildSingle(HeightmapData, _watermap, 0, heightmapWidth, heightmapHeight,
2193 (int)heightmapWidthSamples, (int)heightmapHeightSamples, scale,
2194 offset, thickness, wrap);
2195 d.GeomHeightfieldDataSetBounds(HeightmapData, m_regionWidth, m_regionHeight);
2196 WaterGeom = d.CreateHeightfield(StaticSpace, HeightmapData, 1);
2197 if (WaterGeom != IntPtr.Zero)
2198 {
2199 d.GeomSetCategoryBits(WaterGeom, (int)(CollisionCategories.Water));
2200 d.GeomSetCollideBits(WaterGeom, (int)(CollisionCategories.Space));
2201
2202 }
2203 geom_name_map[WaterGeom] = "Water";
2204
2205 d.Matrix3 R = new d.Matrix3();
2206
2207 Quaternion q1 = Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), 1.5707f);
2208 Quaternion q2 = Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), 1.5707f);
2209
2210 q1 = q1 * q2;
2211 Vector3 v3;
2212 float angle;
2213 q1.GetAxisAngle(out v3, out angle);
2214
2215 d.RFromAxisAndAngle(out R, v3.X, v3.Y, v3.Z, angle);
2216 d.GeomSetRotation(WaterGeom, ref R);
2217 d.GeomSetPosition(WaterGeom, 128, 128, 0);
2218
2219 }
2220
2221 }
2222
2223 public override void Dispose()
2224 {
2225 m_rayCastManager.Dispose();
2226 m_rayCastManager = null;
2227
2228 lock (OdeLock)
2229 {
2230 lock (_prims)
2231 {
2232 foreach (OdePrim prm in _prims)
2233 {
2234 RemovePrim(prm);
2235 }
2236 }
2237
2238 if (ContactgeomsArray != IntPtr.Zero)
2239 Marshal.FreeHGlobal(ContactgeomsArray);
2240 if (GlobalContactsArray != IntPtr.Zero)
2241 Marshal.FreeHGlobal(GlobalContactsArray);
2242
2243 d.WorldDestroy(world);
2244 //d.CloseODE();
2245 }
2246 }
2247
2248 public override Dictionary<uint, float> GetTopColliders()
2249 {
2250 Dictionary<uint, float> returncolliders = new Dictionary<uint, float>();
2251 int cnt = 0;
2252 lock (_prims)
2253 {
2254 foreach (OdePrim prm in _prims)
2255 {
2256 if (prm.CollisionScore > 0)
2257 {
2258 returncolliders.Add(prm.m_localID, prm.CollisionScore);
2259 cnt++;
2260 prm.CollisionScore = 0f;
2261 if (cnt > 25)
2262 {
2263 break;
2264 }
2265 }
2266 }
2267 }
2268 return returncolliders;
2269 }
2270
2271 public override bool SupportsRayCast()
2272 {
2273 return true;
2274 }
2275
2276 public override void RaycastWorld(Vector3 position, Vector3 direction, float length, RaycastCallback retMethod)
2277 {
2278 if (retMethod != null)
2279 {
2280 m_rayCastManager.QueueRequest(position, direction, length, retMethod);
2281 }
2282 }
2283
2284 public override void RaycastWorld(Vector3 position, Vector3 direction, float length, int Count, RayCallback retMethod)
2285 {
2286 if (retMethod != null)
2287 {
2288 m_rayCastManager.QueueRequest(position, direction, length, Count, retMethod);
2289 }
2290 }
2291
2292 // don't like this
2293 public override List<ContactResult> RaycastWorld(Vector3 position, Vector3 direction, float length, int Count)
2294 {
2295 ContactResult[] ourResults = null;
2296 RayCallback retMethod = delegate(List<ContactResult> results)
2297 {
2298 ourResults = new ContactResult[results.Count];
2299 results.CopyTo(ourResults, 0);
2300 };
2301 int waitTime = 0;
2302 m_rayCastManager.QueueRequest(position, direction, length, Count, retMethod);
2303 while (ourResults == null && waitTime < 1000)
2304 {
2305 Thread.Sleep(1);
2306 waitTime++;
2307 }
2308 if (ourResults == null)
2309 return new List<ContactResult>();
2310 return new List<ContactResult>(ourResults);
2311 }
2312
2313 public override void RaycastActor(PhysicsActor actor, Vector3 position, Vector3 direction, float length, RaycastCallback retMethod)
2314 {
2315 if (retMethod != null && actor !=null)
2316 {
2317 IntPtr geom;
2318 if (actor is OdePrim)
2319 geom = ((OdePrim)actor).prim_geom;
2320 else if (actor is OdeCharacter)
2321 geom = ((OdePrim)actor).prim_geom;
2322 else
2323 return;
2324 if (geom == IntPtr.Zero)
2325 return;
2326 m_rayCastManager.QueueRequest(geom, position, direction, length, retMethod);
2327 }
2328 }
2329
2330 public override void RaycastActor(PhysicsActor actor, Vector3 position, Vector3 direction, float length, int Count, RayCallback retMethod)
2331 {
2332 if (retMethod != null && actor != null)
2333 {
2334 IntPtr geom;
2335 if (actor is OdePrim)
2336 geom = ((OdePrim)actor).prim_geom;
2337 else if (actor is OdeCharacter)
2338 geom = ((OdePrim)actor).prim_geom;
2339 else
2340 return;
2341 if (geom == IntPtr.Zero)
2342 return;
2343
2344 m_rayCastManager.QueueRequest(geom,position, direction, length, Count, retMethod);
2345 }
2346 }
2347
2348 // don't like this
2349 public override List<ContactResult> RaycastActor(PhysicsActor actor, Vector3 position, Vector3 direction, float length, int Count)
2350 {
2351 if (actor != null)
2352 {
2353 IntPtr geom;
2354 if (actor is OdePrim)
2355 geom = ((OdePrim)actor).prim_geom;
2356 else if (actor is OdeCharacter)
2357 geom = ((OdePrim)actor).prim_geom;
2358 else
2359 return new List<ContactResult>();
2360 if (geom == IntPtr.Zero)
2361 return new List<ContactResult>();
2362
2363 ContactResult[] ourResults = null;
2364 RayCallback retMethod = delegate(List<ContactResult> results)
2365 {
2366 ourResults = new ContactResult[results.Count];
2367 results.CopyTo(ourResults, 0);
2368 };
2369 int waitTime = 0;
2370 m_rayCastManager.QueueRequest(geom,position, direction, length, Count, retMethod);
2371 while (ourResults == null && waitTime < 1000)
2372 {
2373 Thread.Sleep(1);
2374 waitTime++;
2375 }
2376 if (ourResults == null)
2377 return new List<ContactResult>();
2378 return new List<ContactResult>(ourResults);
2379 }
2380 return new List<ContactResult>();
2381 }
2382 }
2383}