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