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