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