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