aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSCharacter.cs814
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSConstraint.cs135
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSConstraint6Dof.cs153
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSConstraintCollection.cs180
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSConstraintHinge.cs57
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSDynamics.cs1374
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSLinkset.cs333
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs396
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSLinksetConstraints.cs314
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSMaterials.cs200
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSMotors.cs347
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSParam.cs559
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSPhysObject.cs345
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSPlugin.cs81
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs1467
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSScene.cs954
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSShapeCollection.cs1015
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSShapes.cs208
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSTerrainHeightmap.cs175
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSTerrainManager.cs460
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSTerrainMesh.cs267
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BulletSimAPI.cs1604
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BulletSimData.cs280
23 files changed, 11718 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSCharacter.cs
new file mode 100644
index 0000000..4c4e950
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSCharacter.cs
@@ -0,0 +1,814 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Reflection;
30using log4net;
31using OMV = OpenMetaverse;
32using OpenSim.Framework;
33using OpenSim.Region.Physics.Manager;
34
35namespace OpenSim.Region.Physics.BulletSNPlugin
36{
37public sealed class BSCharacter : BSPhysObject
38{
39 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
40 private static readonly string LogHeader = "[BULLETS CHAR]";
41
42 // private bool _stopped;
43 private OMV.Vector3 _size;
44 private bool _grabbed;
45 private bool _selected;
46 private OMV.Vector3 _position;
47 private float _mass;
48 private float _avatarDensity;
49 private float _avatarVolume;
50 private OMV.Vector3 _force;
51 private OMV.Vector3 _velocity;
52 private OMV.Vector3 _torque;
53 private float _collisionScore;
54 private OMV.Vector3 _acceleration;
55 private OMV.Quaternion _orientation;
56 private int _physicsActorType;
57 private bool _isPhysical;
58 private bool _flying;
59 private bool _setAlwaysRun;
60 private bool _throttleUpdates;
61 private bool _isColliding;
62 private bool _collidingObj;
63 private bool _floatOnWater;
64 private OMV.Vector3 _rotationalVelocity;
65 private bool _kinematic;
66 private float _buoyancy;
67
68 // The friction and velocity of the avatar is modified depending on whether walking or not.
69 private OMV.Vector3 _appliedVelocity; // the last velocity applied to the avatar
70 private float _currentFriction; // the friction currently being used (changed by setVelocity).
71
72 private BSVMotor _velocityMotor;
73
74 private OMV.Vector3 _PIDTarget;
75 private bool _usePID;
76 private float _PIDTau;
77 private bool _useHoverPID;
78 private float _PIDHoverHeight;
79 private PIDHoverType _PIDHoverType;
80 private float _PIDHoverTao;
81
82 public BSCharacter(uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, bool isFlying)
83 : base(parent_scene, localID, avName, "BSCharacter")
84 {
85 _physicsActorType = (int)ActorTypes.Agent;
86 _position = pos;
87
88 // Old versions of ScenePresence passed only the height. If width and/or depth are zero,
89 // replace with the default values.
90 _size = size;
91 if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth;
92 if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth;
93
94 // A motor to control the acceleration and deceleration of the avatar movement.
95 // _velocityMotor = new BSVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f);
96 // _velocityMotor = new BSPIDVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f);
97 // Infinite decay and timescale values so motor only changes current to target values.
98 _velocityMotor = new BSVMotor("BSCharacter.Velocity",
99 0.2f, // time scale
100 BSMotor.Infinite, // decay time scale
101 BSMotor.InfiniteVector, // friction timescale
102 1f // efficiency
103 );
104 _velocityMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages.
105
106 _flying = isFlying;
107 _orientation = OMV.Quaternion.Identity;
108 _velocity = OMV.Vector3.Zero;
109 _appliedVelocity = OMV.Vector3.Zero;
110 _buoyancy = ComputeBuoyancyFromFlying(isFlying);
111 _currentFriction = BSParam.AvatarStandingFriction;
112 _avatarDensity = BSParam.AvatarDensity;
113
114 // The dimensions of the avatar capsule are kept in the scale.
115 // Physics creates a unit capsule which is scaled by the physics engine.
116 ComputeAvatarScale(_size);
117 // set _avatarVolume and _mass based on capsule size, _density and Scale
118 ComputeAvatarVolumeAndMass();
119 DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}",
120 LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
121
122 // do actual creation in taint time
123 PhysicsScene.TaintedObject("BSCharacter.create", delegate()
124 {
125 DetailLog("{0},BSCharacter.create,taint", LocalID);
126 // New body and shape into PhysBody and PhysShape
127 PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this);
128
129 SetPhysicalProperties();
130 });
131 return;
132 }
133
134 // called when this character is being destroyed and the resources should be released
135 public override void Destroy()
136 {
137 base.Destroy();
138
139 DetailLog("{0},BSCharacter.Destroy", LocalID);
140 PhysicsScene.TaintedObject("BSCharacter.destroy", delegate()
141 {
142 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
143 PhysBody.Clear();
144 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
145 PhysShape.Clear();
146 });
147 }
148
149 private void SetPhysicalProperties()
150 {
151 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
152
153 ZeroMotion(true);
154 ForcePosition = _position;
155 // Set the velocity and compute the proper friction
156 ForceVelocity = _velocity;
157 // Setting the current and target in the motor will cause it to start computing any deceleration.
158 _velocityMotor.Reset();
159 _velocityMotor.SetCurrent(_velocity);
160 _velocityMotor.SetTarget(_velocity);
161 _velocityMotor.Enabled = false;
162
163 // This will enable or disable the flying buoyancy of the avatar.
164 // Needs to be reset especially when an avatar is recreated after crossing a region boundry.
165 Flying = _flying;
166
167 BulletSimAPI.SetRestitution2(PhysBody.ptr, BSParam.AvatarRestitution);
168 BulletSimAPI.SetMargin2(PhysShape.ptr, PhysicsScene.Params.collisionMargin);
169 BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
170 BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, BSParam.ContactProcessingThreshold);
171 if (BSParam.CcdMotionThreshold > 0f)
172 {
173 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold);
174 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius);
175 }
176
177 UpdatePhysicalMassProperties(RawMass);
178
179 // Make so capsule does not fall over
180 BulletSimAPI.SetAngularFactorV2(PhysBody.ptr, OMV.Vector3.Zero);
181
182 BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_CHARACTER_OBJECT);
183
184 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr, _position, _orientation);
185
186 // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG);
187 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_DEACTIVATION);
188 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
189
190 // Do this after the object has been added to the world
191 PhysBody.collisionType = CollisionType.Avatar;
192 PhysBody.ApplyCollisionMask();
193 }
194
195 public override void RequestPhysicsterseUpdate()
196 {
197 base.RequestPhysicsterseUpdate();
198 }
199 // No one calls this method so I don't know what it could possibly mean
200 public override bool Stopped { get { return false; } }
201
202 public override OMV.Vector3 Size {
203 get
204 {
205 // Avatar capsule size is kept in the scale parameter.
206 return _size;
207 }
208
209 set {
210 // When an avatar's size is set, only the height is changed.
211 _size = value;
212 // Old versions of ScenePresence passed only the height. If width and/or depth are zero,
213 // replace with the default values.
214 if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth;
215 if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth;
216
217 ComputeAvatarScale(_size);
218 ComputeAvatarVolumeAndMass();
219 DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}",
220 LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
221
222 PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
223 {
224 if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape)
225 {
226 BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
227 UpdatePhysicalMassProperties(RawMass);
228 // Make sure this change appears as a property update event
229 BulletSimAPI.PushUpdate2(PhysBody.ptr);
230 }
231 });
232
233 }
234 }
235
236 public override PrimitiveBaseShape Shape
237 {
238 set { BaseShape = value; }
239 }
240 // I want the physics engine to make an avatar capsule
241 public override BSPhysicsShapeType PreferredPhysicalShape
242 {
243 get {return BSPhysicsShapeType.SHAPE_CAPSULE; }
244 }
245
246 public override bool Grabbed {
247 set { _grabbed = value; }
248 }
249 public override bool Selected {
250 set { _selected = value; }
251 }
252 public override void CrossingFailure() { return; }
253 public override void link(PhysicsActor obj) { return; }
254 public override void delink() { return; }
255
256 // Set motion values to zero.
257 // Do it to the properties so the values get set in the physics engine.
258 // Push the setting of the values to the viewer.
259 // Called at taint time!
260 public override void ZeroMotion(bool inTaintTime)
261 {
262 _velocity = OMV.Vector3.Zero;
263 _velocityMotor.Zero();
264 _acceleration = OMV.Vector3.Zero;
265 _rotationalVelocity = OMV.Vector3.Zero;
266
267 // Zero some other properties directly into the physics engine
268 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
269 {
270 if (PhysBody.HasPhysicalBody)
271 BulletSimAPI.ClearAllForces2(PhysBody.ptr);
272 });
273 }
274 public override void ZeroAngularMotion(bool inTaintTime)
275 {
276 _rotationalVelocity = OMV.Vector3.Zero;
277
278 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
279 {
280 if (PhysBody.HasPhysicalBody)
281 {
282 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
283 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
284 // The next also get rid of applied linear force but the linear velocity is untouched.
285 BulletSimAPI.ClearForces2(PhysBody.ptr);
286 }
287 });
288 }
289
290
291 public override void LockAngularMotion(OMV.Vector3 axis) { return; }
292
293 public override OMV.Vector3 RawPosition
294 {
295 get { return _position; }
296 set { _position = value; }
297 }
298 public override OMV.Vector3 Position {
299 get {
300 // Don't refetch the position because this function is called a zillion times
301 // _position = BulletSimAPI.GetObjectPosition2(Scene.World.ptr, LocalID);
302 return _position;
303 }
304 set {
305 _position = value;
306 PositionSanityCheck();
307
308 PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
309 {
310 DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
311 if (PhysBody.HasPhysicalBody)
312 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
313 });
314 }
315 }
316 public override OMV.Vector3 ForcePosition {
317 get {
318 _position = BulletSimAPI.GetPosition2(PhysBody.ptr);
319 return _position;
320 }
321 set {
322 _position = value;
323 PositionSanityCheck();
324 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
325 }
326 }
327
328
329 // Check that the current position is sane and, if not, modify the position to make it so.
330 // Check for being below terrain or on water.
331 // Returns 'true' of the position was made sane by some action.
332 private bool PositionSanityCheck()
333 {
334 bool ret = false;
335
336 // TODO: check for out of bounds
337 if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position))
338 {
339 // The character is out of the known/simulated area.
340 // Upper levels of code will handle the transition to other areas so, for
341 // the time, we just ignore the position.
342 return ret;
343 }
344
345 // If below the ground, move the avatar up
346 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
347 if (Position.Z < terrainHeight)
348 {
349 DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
350 _position.Z = terrainHeight + 2.0f;
351 ret = true;
352 }
353 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
354 {
355 float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
356 if (Position.Z < waterHeight)
357 {
358 _position.Z = waterHeight;
359 ret = true;
360 }
361 }
362
363 return ret;
364 }
365
366 // A version of the sanity check that also makes sure a new position value is
367 // pushed back to the physics engine. This routine would be used by anyone
368 // who is not already pushing the value.
369 private bool PositionSanityCheck(bool inTaintTime)
370 {
371 bool ret = false;
372 if (PositionSanityCheck())
373 {
374 // The new position value must be pushed into the physics engine but we can't
375 // just assign to "Position" because of potential call loops.
376 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate()
377 {
378 DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
379 if (PhysBody.HasPhysicalBody)
380 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
381 });
382 ret = true;
383 }
384 return ret;
385 }
386
387 public override float Mass { get { return _mass; } }
388
389 // used when we only want this prim's mass and not the linkset thing
390 public override float RawMass {
391 get {return _mass; }
392 }
393 public override void UpdatePhysicalMassProperties(float physMass)
394 {
395 OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass);
396 BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, localInertia);
397 }
398
399 public override OMV.Vector3 Force {
400 get { return _force; }
401 set {
402 _force = value;
403 // m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force);
404 PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
405 {
406 DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
407 if (PhysBody.HasPhysicalBody)
408 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
409 });
410 }
411 }
412
413 public bool TouchingGround()
414 {
415 bool ret = BulletSimAPI.RayCastGround(PhysicsScene.World.ptr,_position,_size.Z * 0.55f, PhysBody.ptr);
416 return ret;
417 }
418 // Avatars don't do vehicles
419 public override int VehicleType { get { return (int)Vehicle.TYPE_NONE; } set { return; } }
420 public override void VehicleFloatParam(int param, float value) { }
421 public override void VehicleVectorParam(int param, OMV.Vector3 value) {}
422 public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { }
423 public override void VehicleFlags(int param, bool remove) { }
424
425 // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more
426 public override void SetVolumeDetect(int param) { return; }
427
428 public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } }
429 public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } }
430
431 // Sets the target in the motor. This starts the changing of the avatar's velocity.
432 public override OMV.Vector3 TargetVelocity
433 {
434 get
435 {
436 return _velocityMotor.TargetValue;
437 }
438 set
439 {
440 DetailLog("{0},BSCharacter.setTargetVelocity,call,vel={1}", LocalID, value);
441
442 if (!_flying)
443 if ((value.Z >= 0.0001f) || (value.Z <= -0.0001f) || _velocity.Z < -0.0001f)
444 if (!TouchingGround())
445 value.Z = _velocity.Z;
446 if (_setAlwaysRun)
447 value *= 1.3f;
448
449 OMV.Vector3 targetVel = value;
450
451 PhysicsScene.TaintedObject("BSCharacter.setTargetVelocity", delegate()
452 {
453
454 _velocityMotor.Reset();
455 _velocityMotor.SetTarget(targetVel);
456 _velocityMotor.SetCurrent(_velocity);
457 _velocityMotor.Enabled = true;
458
459 // Make sure a property update happens next step so the motor gets incorporated.
460 BulletSimAPI.PushUpdate2(PhysBody.ptr);
461 });
462 }
463 }
464 // Directly setting velocity means this is what the user really wants now.
465 public override OMV.Vector3 Velocity {
466 get { return _velocity; }
467 set {
468 _velocity = value;
469 // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity);
470 PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate()
471 {
472 _velocityMotor.Reset();
473 _velocityMotor.SetCurrent(_velocity);
474 _velocityMotor.SetTarget(_velocity);
475 // Even though the motor is initialized, it's not used and the velocity goes straight into the avatar.
476 _velocityMotor.Enabled = false;
477
478 DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity);
479 ForceVelocity = _velocity;
480 });
481 }
482 }
483 public override OMV.Vector3 ForceVelocity {
484 get { return _velocity; }
485 set {
486 PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity");
487
488 _velocity = value;
489 // Depending on whether the avatar is moving or not, change the friction
490 // to keep the avatar from slipping around
491 if (_velocity.Length() == 0)
492 {
493 if (_currentFriction != BSParam.AvatarStandingFriction)
494 {
495 _currentFriction = BSParam.AvatarStandingFriction;
496 if (PhysBody.HasPhysicalBody)
497 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
498 }
499 }
500 else
501 {
502 if (_currentFriction != BSParam.AvatarFriction)
503 {
504 _currentFriction = BSParam.AvatarFriction;
505 if (PhysBody.HasPhysicalBody)
506 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
507 }
508 }
509 // Remember the set velocity so we can suppress the reduction by friction, ...
510 _appliedVelocity = value;
511
512 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
513 BulletSimAPI.Activate2(PhysBody.ptr, true);
514 }
515 }
516 public override OMV.Vector3 Torque {
517 get { return _torque; }
518 set { _torque = value;
519 }
520 }
521 public override float CollisionScore {
522 get { return _collisionScore; }
523 set { _collisionScore = value;
524 }
525 }
526 public override OMV.Vector3 Acceleration {
527 get { return _acceleration; }
528 set { _acceleration = value; }
529 }
530 public override OMV.Quaternion RawOrientation
531 {
532 get { return _orientation; }
533 set { _orientation = value; }
534 }
535 public override OMV.Quaternion Orientation {
536 get { return _orientation; }
537 set {
538 _orientation = value;
539 // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation);
540 PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
541 {
542 if (PhysBody.HasPhysicalBody)
543 {
544 // _position = BulletSimAPI.GetPosition2(BSBody.ptr);
545 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
546 }
547 });
548 }
549 }
550 // Go directly to Bullet to get/set the value.
551 public override OMV.Quaternion ForceOrientation
552 {
553 get
554 {
555 _orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr);
556 return _orientation;
557 }
558 set
559 {
560 _orientation = value;
561 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
562 }
563 }
564 public override int PhysicsActorType {
565 get { return _physicsActorType; }
566 set { _physicsActorType = value;
567 }
568 }
569 public override bool IsPhysical {
570 get { return _isPhysical; }
571 set { _isPhysical = value;
572 }
573 }
574 public override bool IsSolid {
575 get { return true; }
576 }
577 public override bool IsStatic {
578 get { return false; }
579 }
580 public override bool Flying {
581 get { return _flying; }
582 set {
583 _flying = value;
584
585 // simulate flying by changing the effect of gravity
586 Buoyancy = ComputeBuoyancyFromFlying(_flying);
587 }
588 }
589 // Flying is implimented by changing the avatar's buoyancy.
590 // Would this be done better with a vehicle type?
591 private float ComputeBuoyancyFromFlying(bool ifFlying) {
592 return ifFlying ? 1f : 0f;
593 }
594 public override bool
595 SetAlwaysRun {
596 get { return _setAlwaysRun; }
597 set { _setAlwaysRun = value; }
598 }
599 public override bool ThrottleUpdates {
600 get { return _throttleUpdates; }
601 set { _throttleUpdates = value; }
602 }
603 public override bool IsColliding {
604 get { return (CollidingStep == PhysicsScene.SimulationStep); }
605 set { _isColliding = value; }
606 }
607 public override bool CollidingGround {
608 get { return (CollidingGroundStep == PhysicsScene.SimulationStep); }
609 set { CollidingGround = value; }
610 }
611 public override bool CollidingObj {
612 get { return _collidingObj; }
613 set { _collidingObj = value; }
614 }
615 public override bool FloatOnWater {
616 set {
617 _floatOnWater = value;
618 PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate()
619 {
620 if (PhysBody.HasPhysicalBody)
621 {
622 if (_floatOnWater)
623 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
624 else
625 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
626 }
627 });
628 }
629 }
630 public override OMV.Vector3 RotationalVelocity {
631 get { return _rotationalVelocity; }
632 set { _rotationalVelocity = value; }
633 }
634 public override OMV.Vector3 ForceRotationalVelocity {
635 get { return _rotationalVelocity; }
636 set { _rotationalVelocity = value; }
637 }
638 public override bool Kinematic {
639 get { return _kinematic; }
640 set { _kinematic = value; }
641 }
642 // neg=fall quickly, 0=1g, 1=0g, pos=float up
643 public override float Buoyancy {
644 get { return _buoyancy; }
645 set { _buoyancy = value;
646 PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate()
647 {
648 DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
649 ForceBuoyancy = _buoyancy;
650 });
651 }
652 }
653 public override float ForceBuoyancy {
654 get { return _buoyancy; }
655 set {
656 PhysicsScene.AssertInTaintTime("BSCharacter.ForceBuoyancy");
657
658 _buoyancy = value;
659 DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
660 // Buoyancy is faked by changing the gravity applied to the object
661 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
662 if (PhysBody.HasPhysicalBody)
663 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
664 }
665 }
666
667 // Used for MoveTo
668 public override OMV.Vector3 PIDTarget {
669 set { _PIDTarget = value; }
670 }
671 public override bool PIDActive {
672 set { _usePID = value; }
673 }
674 public override float PIDTau {
675 set { _PIDTau = value; }
676 }
677
678 // Used for llSetHoverHeight and maybe vehicle height
679 // Hover Height will override MoveTo target's Z
680 public override bool PIDHoverActive {
681 set { _useHoverPID = value; }
682 }
683 public override float PIDHoverHeight {
684 set { _PIDHoverHeight = value; }
685 }
686 public override PIDHoverType PIDHoverType {
687 set { _PIDHoverType = value; }
688 }
689 public override float PIDHoverTau {
690 set { _PIDHoverTao = value; }
691 }
692
693 // For RotLookAt
694 public override OMV.Quaternion APIDTarget { set { return; } }
695 public override bool APIDActive { set { return; } }
696 public override float APIDStrength { set { return; } }
697 public override float APIDDamping { set { return; } }
698
699 public override void AddForce(OMV.Vector3 force, bool pushforce) {
700 if (force.IsFinite())
701 {
702 _force.X += force.X;
703 _force.Y += force.Y;
704 _force.Z += force.Z;
705 // m_log.DebugFormat("{0}: AddForce. adding={1}, newForce={2}", LogHeader, force, _force);
706 PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate()
707 {
708 DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force);
709 if (PhysBody.HasPhysicalBody)
710 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
711 });
712 }
713 else
714 {
715 m_log.ErrorFormat("{0}: Got a NaN force applied to a Character", LogHeader);
716 }
717 //m_lastUpdateSent = false;
718 }
719
720 public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
721 }
722 public override void SetMomentum(OMV.Vector3 momentum) {
723 }
724
725 private void ComputeAvatarScale(OMV.Vector3 size)
726 {
727 OMV.Vector3 newScale = size;
728 // newScale.X = PhysicsScene.Params.avatarCapsuleWidth;
729 // newScale.Y = PhysicsScene.Params.avatarCapsuleDepth;
730
731 // From the total height, remove the capsule half spheres that are at each end
732 // The 1.15f came from ODE. Not sure what this factors in.
733 // newScale.Z = (size.Z * 1.15f) - (newScale.X + newScale.Y);
734
735 // The total scale height is the central cylindar plus the caps on the two ends.
736 newScale.Z = size.Z + (Math.Min(size.X, size.Y) * 2f);
737
738 // Convert diameters to radii and height to half height -- the way Bullet expects it.
739 Scale = newScale / 2f;
740 }
741
742 // set _avatarVolume and _mass based on capsule size, _density and Scale
743 private void ComputeAvatarVolumeAndMass()
744 {
745 _avatarVolume = (float)(
746 Math.PI
747 * Scale.X
748 * Scale.Y // the area of capsule cylinder
749 * Scale.Z // times height of capsule cylinder
750 + 1.33333333f
751 * Math.PI
752 * Scale.X
753 * Math.Min(Scale.X, Scale.Y)
754 * Scale.Y // plus the volume of the capsule end caps
755 );
756 _mass = _avatarDensity * _avatarVolume;
757 }
758
759 // The physics engine says that properties have updated. Update same and inform
760 // the world that things have changed.
761 public override void UpdateProperties(EntityProperties entprop)
762 {
763 _position = entprop.Position;
764 _orientation = entprop.Rotation;
765 _velocity = entprop.Velocity;
766 _acceleration = entprop.Acceleration;
767 _rotationalVelocity = entprop.RotationalVelocity;
768
769 // Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
770 PositionSanityCheck(true);
771
772 if (_velocityMotor.Enabled)
773 {
774 // TODO: Decide if the step parameters should be changed depending on the avatar's
775 // state (flying, colliding, ...).
776
777 OMV.Vector3 stepVelocity = _velocityMotor.Step(PhysicsScene.LastTimeStep);
778
779 // If falling, we keep the world's downward vector no matter what the other axis specify.
780 if (!Flying && !IsColliding)
781 {
782 stepVelocity.Z = entprop.Velocity.Z;
783 DetailLog("{0},BSCharacter.UpdateProperties,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity);
784 }
785
786 // If the user has said stop and we've stopped applying velocity correction,
787 // the motor can be turned off. Set the velocity to zero so the zero motion is sent to the viewer.
788 if (_velocityMotor.TargetValue.ApproxEquals(OMV.Vector3.Zero, 0.01f) && _velocityMotor.ErrorIsZero)
789 {
790 stepVelocity = OMV.Vector3.Zero;
791 _velocityMotor.Enabled = false;
792 DetailLog("{0},BSCharacter.UpdateProperties,taint,disableVelocityMotor,m={1}", LocalID, _velocityMotor);
793 }
794
795 _velocity = stepVelocity;
796 entprop.Velocity = _velocity;
797 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
798 }
799
800 // remember the current and last set values
801 LastEntityProperties = CurrentEntityProperties;
802 CurrentEntityProperties = entprop;
803
804 // Tell the linkset about value changes
805 Linkset.UpdateProperties(this, true);
806
807 // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
808 // base.RequestPhysicsterseUpdate();
809
810 DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
811 LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity);
812 }
813}
814}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSConstraint.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraint.cs
new file mode 100644
index 0000000..426bdc2
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraint.cs
@@ -0,0 +1,135 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using OpenMetaverse;
31
32namespace OpenSim.Region.Physics.BulletSNPlugin
33{
34
35public abstract class BSConstraint : IDisposable
36{
37 private static string LogHeader = "[BULLETSIM CONSTRAINT]";
38
39 protected BulletSim m_world;
40 protected BulletBody m_body1;
41 protected BulletBody m_body2;
42 protected BulletConstraint m_constraint;
43 protected bool m_enabled = false;
44
45 public BulletBody Body1 { get { return m_body1; } }
46 public BulletBody Body2 { get { return m_body2; } }
47 public BulletConstraint Constraint { get { return m_constraint; } }
48 public abstract ConstraintType Type { get; }
49 public bool IsEnabled { get { return m_enabled; } }
50
51 public BSConstraint()
52 {
53 }
54
55 public virtual void Dispose()
56 {
57 if (m_enabled)
58 {
59 m_enabled = false;
60 if (m_constraint.HasPhysicalConstraint)
61 {
62 bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr);
63 m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}",
64 BSScene.DetailLogZero,
65 m_body1.ID, m_body1.ptr.ToString(),
66 m_body2.ID, m_body2.ptr.ToString(),
67 success);
68 m_constraint.Clear();
69 }
70 }
71 }
72
73 public virtual bool SetLinearLimits(Vector3 low, Vector3 high)
74 {
75 bool ret = false;
76 if (m_enabled)
77 ret = BulletSimAPI.SetLinearLimits2(m_constraint.ptr, low, high);
78 return ret;
79 }
80
81 public virtual bool SetAngularLimits(Vector3 low, Vector3 high)
82 {
83 bool ret = false;
84 if (m_enabled)
85 ret = BulletSimAPI.SetAngularLimits2(m_constraint.ptr, low, high);
86 return ret;
87 }
88
89 public virtual bool SetSolverIterations(float cnt)
90 {
91 bool ret = false;
92 if (m_enabled)
93 {
94 BulletSimAPI.SetConstraintNumSolverIterations2(m_constraint.ptr, cnt);
95 ret = true;
96 }
97 return ret;
98 }
99
100 public virtual bool CalculateTransforms()
101 {
102 bool ret = false;
103 if (m_enabled)
104 {
105 // Recompute the internal transforms
106 BulletSimAPI.CalculateTransforms2(m_constraint.ptr);
107 ret = true;
108 }
109 return ret;
110 }
111
112 // Reset this constraint making sure it has all its internal structures
113 // recomputed and is enabled and ready to go.
114 public virtual bool RecomputeConstraintVariables(float mass)
115 {
116 bool ret = false;
117 if (m_enabled)
118 {
119 ret = CalculateTransforms();
120 if (ret)
121 {
122 // Setting an object's mass to zero (making it static like when it's selected)
123 // automatically disables the constraints.
124 // If the link is enabled, be sure to set the constraint itself to enabled.
125 BulletSimAPI.SetConstraintEnable2(m_constraint.ptr, BSParam.NumericBool(true));
126 }
127 else
128 {
129 m_world.physicsScene.Logger.ErrorFormat("{0} CalculateTransforms failed. A={1}, B={2}", LogHeader, Body1.ID, Body2.ID);
130 }
131 }
132 return ret;
133 }
134}
135}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSConstraint6Dof.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraint6Dof.cs
new file mode 100644
index 0000000..0181d9d
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraint6Dof.cs
@@ -0,0 +1,153 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using OpenMetaverse;
31
32namespace OpenSim.Region.Physics.BulletSNPlugin
33{
34
35public sealed class BSConstraint6Dof : BSConstraint
36{
37 private static string LogHeader = "[BULLETSIM 6DOF CONSTRAINT]";
38
39 public override ConstraintType Type { get { return ConstraintType.D6_CONSTRAINT_TYPE; } }
40
41 // Create a btGeneric6DofConstraint
42 public BSConstraint6Dof(BulletSim world, BulletBody obj1, BulletBody obj2,
43 Vector3 frame1, Quaternion frame1rot,
44 Vector3 frame2, Quaternion frame2rot,
45 bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
46 {
47 m_world = world;
48 m_body1 = obj1;
49 m_body2 = obj2;
50 m_constraint = new BulletConstraint(
51 BulletSimAPI.Create6DofConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
52 frame1, frame1rot,
53 frame2, frame2rot,
54 useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
55 m_enabled = true;
56 world.physicsScene.DetailLog("{0},BS6DofConstraint,createFrame,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
57 BSScene.DetailLogZero, world.worldID,
58 obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
59 }
60
61 public BSConstraint6Dof(BulletSim world, BulletBody obj1, BulletBody obj2,
62 Vector3 joinPoint,
63 bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
64 {
65 m_world = world;
66 m_body1 = obj1;
67 m_body2 = obj2;
68 if (!obj1.HasPhysicalBody || !obj2.HasPhysicalBody)
69 {
70 world.physicsScene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
71 BSScene.DetailLogZero, world.worldID,
72 obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
73 world.physicsScene.Logger.ErrorFormat("{0} Attempt to build 6DOF constraint with missing bodies: wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
74 LogHeader, world.worldID, obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
75 m_enabled = false;
76 }
77 else
78 {
79 m_constraint = new BulletConstraint(
80 BulletSimAPI.Create6DofConstraintToPoint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
81 joinPoint,
82 useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
83 world.physicsScene.DetailLog("{0},BS6DofConstraint,createMidPoint,wID={1}, csrt={2}, rID={3}, rBody={4}, cID={5}, cBody={6}",
84 BSScene.DetailLogZero, world.worldID, m_constraint.ptr.ToString(),
85 obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
86 if (!m_constraint.HasPhysicalConstraint)
87 {
88 world.physicsScene.Logger.ErrorFormat("{0} Failed creation of 6Dof constraint. rootID={1}, childID={2}",
89 LogHeader, obj1.ID, obj2.ID);
90 m_enabled = false;
91 }
92 else
93 {
94 m_enabled = true;
95 }
96 }
97 }
98
99 public bool SetFrames(Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot)
100 {
101 bool ret = false;
102 if (m_enabled)
103 {
104 BulletSimAPI.SetFrames2(m_constraint.ptr, frameA, frameArot, frameB, frameBrot);
105 ret = true;
106 }
107 return ret;
108 }
109
110 public bool SetCFMAndERP(float cfm, float erp)
111 {
112 bool ret = false;
113 if (m_enabled)
114 {
115 BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
116 BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL);
117 BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
118 ret = true;
119 }
120 return ret;
121 }
122
123 public bool UseFrameOffset(bool useOffset)
124 {
125 bool ret = false;
126 float onOff = useOffset ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
127 if (m_enabled)
128 ret = BulletSimAPI.UseFrameOffset2(m_constraint.ptr, onOff);
129 return ret;
130 }
131
132 public bool TranslationalLimitMotor(bool enable, float targetVelocity, float maxMotorForce)
133 {
134 bool ret = false;
135 float onOff = enable ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
136 if (m_enabled)
137 {
138 ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.ptr, onOff, targetVelocity, maxMotorForce);
139 m_world.physicsScene.DetailLog("{0},BS6DOFConstraint,TransLimitMotor,enable={1},vel={2},maxForce={3}",
140 BSScene.DetailLogZero, enable, targetVelocity, maxMotorForce);
141 }
142 return ret;
143 }
144
145 public bool SetBreakingImpulseThreshold(float threshold)
146 {
147 bool ret = false;
148 if (m_enabled)
149 ret = BulletSimAPI.SetBreakingImpulseThreshold2(m_constraint.ptr, threshold);
150 return ret;
151 }
152}
153}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSConstraintCollection.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraintCollection.cs
new file mode 100644
index 0000000..5c00b1a
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraintCollection.cs
@@ -0,0 +1,180 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using log4net;
31using OpenMetaverse;
32
33namespace OpenSim.Region.Physics.BulletSNPlugin
34{
35
36public sealed class BSConstraintCollection : IDisposable
37{
38 // private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
39 // private static readonly string LogHeader = "[CONSTRAINT COLLECTION]";
40
41 delegate bool ConstraintAction(BSConstraint constrain);
42
43 private List<BSConstraint> m_constraints;
44 private BulletSim m_world;
45
46 public BSConstraintCollection(BulletSim world)
47 {
48 m_world = world;
49 m_constraints = new List<BSConstraint>();
50 }
51
52 public void Dispose()
53 {
54 this.Clear();
55 }
56
57 public void Clear()
58 {
59 lock (m_constraints)
60 {
61 foreach (BSConstraint cons in m_constraints)
62 {
63 cons.Dispose();
64 }
65 m_constraints.Clear();
66 }
67 }
68
69 public bool AddConstraint(BSConstraint cons)
70 {
71 lock (m_constraints)
72 {
73 // There is only one constraint between any bodies. Remove any old just to make sure.
74 RemoveAndDestroyConstraint(cons.Body1, cons.Body2);
75
76 m_constraints.Add(cons);
77 }
78
79 return true;
80 }
81
82 // Get the constraint between two bodies. There can be only one.
83 // Return 'true' if a constraint was found.
84 public bool TryGetConstraint(BulletBody body1, BulletBody body2, out BSConstraint returnConstraint)
85 {
86 bool found = false;
87 BSConstraint foundConstraint = null;
88
89 uint lookingID1 = body1.ID;
90 uint lookingID2 = body2.ID;
91 lock (m_constraints)
92 {
93 foreach (BSConstraint constrain in m_constraints)
94 {
95 if ((constrain.Body1.ID == lookingID1 && constrain.Body2.ID == lookingID2)
96 || (constrain.Body1.ID == lookingID2 && constrain.Body2.ID == lookingID1))
97 {
98 foundConstraint = constrain;
99 found = true;
100 break;
101 }
102 }
103 }
104 returnConstraint = foundConstraint;
105 return found;
106 }
107
108 // Remove any constraint between the passed bodies.
109 // Presumed there is only one such constraint possible.
110 // Return 'true' if a constraint was found and destroyed.
111 public bool RemoveAndDestroyConstraint(BulletBody body1, BulletBody body2)
112 {
113 bool ret = false;
114 lock (m_constraints)
115 {
116 BSConstraint constrain;
117 if (this.TryGetConstraint(body1, body2, out constrain))
118 {
119 // remove the constraint from our collection
120 RemoveAndDestroyConstraint(constrain);
121 ret = true;
122 }
123 }
124
125 return ret;
126 }
127
128 // The constraint MUST exist in the collection
129 public bool RemoveAndDestroyConstraint(BSConstraint constrain)
130 {
131 lock (m_constraints)
132 {
133 // remove the constraint from our collection
134 m_constraints.Remove(constrain);
135 }
136 // tell the engine that all its structures need to be freed
137 constrain.Dispose();
138 // we destroyed something
139 return true;
140 }
141
142 // Remove all constraints that reference the passed body.
143 // Return 'true' if any constraints were destroyed.
144 public bool RemoveAndDestroyConstraint(BulletBody body1)
145 {
146 List<BSConstraint> toRemove = new List<BSConstraint>();
147 uint lookingID = body1.ID;
148 lock (m_constraints)
149 {
150 foreach (BSConstraint constrain in m_constraints)
151 {
152 if (constrain.Body1.ID == lookingID || constrain.Body2.ID == lookingID)
153 {
154 toRemove.Add(constrain);
155 }
156 }
157 foreach (BSConstraint constrain in toRemove)
158 {
159 m_constraints.Remove(constrain);
160 constrain.Dispose();
161 }
162 }
163 return (toRemove.Count > 0);
164 }
165
166 public bool RecalculateAllConstraints()
167 {
168 bool ret = false;
169 lock (m_constraints)
170 {
171 foreach (BSConstraint constrain in m_constraints)
172 {
173 constrain.CalculateTransforms();
174 ret = true;
175 }
176 }
177 return ret;
178 }
179}
180}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSConstraintHinge.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraintHinge.cs
new file mode 100644
index 0000000..7951f06
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraintHinge.cs
@@ -0,0 +1,57 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using OpenMetaverse;
31
32namespace OpenSim.Region.Physics.BulletSNPlugin
33{
34
35public sealed class BSConstraintHinge : BSConstraint
36{
37 public override ConstraintType Type { get { return ConstraintType.HINGE_CONSTRAINT_TYPE; } }
38
39 public BSConstraintHinge(BulletSim world, BulletBody obj1, BulletBody obj2,
40 Vector3 pivotInA, Vector3 pivotInB,
41 Vector3 axisInA, Vector3 axisInB,
42 bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
43 {
44 m_world = world;
45 m_body1 = obj1;
46 m_body2 = obj2;
47 m_constraint = new BulletConstraint(
48 BulletSimAPI.CreateHingeConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
49 pivotInA, pivotInB,
50 axisInA, axisInB,
51 useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
52 m_enabled = true;
53 }
54
55}
56
57}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSDynamics.cs
new file mode 100644
index 0000000..72afacc
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSDynamics.cs
@@ -0,0 +1,1374 @@
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 * The quotations from http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial
28 * are Copyright (c) 2009 Linden Research, Inc and are used under their license
29 * of Creative Commons Attribution-Share Alike 3.0
30 * (http://creativecommons.org/licenses/by-sa/3.0/).
31 */
32
33using System;
34using System.Collections.Generic;
35using System.Reflection;
36using System.Runtime.InteropServices;
37using OpenMetaverse;
38using OpenSim.Region.Physics.Manager;
39
40namespace OpenSim.Region.Physics.BulletSNPlugin
41{
42 public sealed class BSDynamics
43 {
44 private static string LogHeader = "[BULLETSIM VEHICLE]";
45
46 private BSScene PhysicsScene { get; set; }
47 // the prim this dynamic controller belongs to
48 private BSPrim Prim { get; set; }
49
50 // mass of the vehicle fetched each time we're calles
51 private float m_vehicleMass;
52
53 // Vehicle properties
54 public Vehicle Type { get; set; }
55
56 // private Quaternion m_referenceFrame = Quaternion.Identity; // Axis modifier
57 private VehicleFlag m_flags = (VehicleFlag) 0; // Boolean settings:
58 // HOVER_TERRAIN_ONLY
59 // HOVER_GLOBAL_HEIGHT
60 // NO_DEFLECTION_UP
61 // HOVER_WATER_ONLY
62 // HOVER_UP_ONLY
63 // LIMIT_MOTOR_UP
64 // LIMIT_ROLL_ONLY
65 private Vector3 m_BlockingEndPoint = Vector3.Zero;
66 private Quaternion m_RollreferenceFrame = Quaternion.Identity;
67 private Quaternion m_referenceFrame = Quaternion.Identity;
68
69 // Linear properties
70 private BSVMotor m_linearMotor = new BSVMotor("LinearMotor");
71 private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time
72 private Vector3 m_linearMotorOffset = Vector3.Zero; // the point of force can be offset from the center
73 private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL
74 private Vector3 m_linearFrictionTimescale = Vector3.Zero;
75 private float m_linearMotorDecayTimescale = 0;
76 private float m_linearMotorTimescale = 0;
77 private Vector3 m_lastLinearVelocityVector = Vector3.Zero;
78 private Vector3 m_lastPositionVector = Vector3.Zero;
79 // private bool m_LinearMotorSetLastFrame = false;
80 // private Vector3 m_linearMotorOffset = Vector3.Zero;
81
82 //Angular properties
83 private BSVMotor m_angularMotor = new BSVMotor("AngularMotor");
84 private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor
85 // private int m_angularMotorApply = 0; // application frame counter
86 private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity
87 private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate
88 private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate
89 private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate
90 private Vector3 m_lastAngularVelocity = Vector3.Zero;
91 private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body
92
93 //Deflection properties
94 private BSVMotor m_angularDeflectionMotor = new BSVMotor("AngularDeflection");
95 private float m_angularDeflectionEfficiency = 0;
96 private float m_angularDeflectionTimescale = 0;
97 private float m_linearDeflectionEfficiency = 0;
98 private float m_linearDeflectionTimescale = 0;
99
100 //Banking properties
101 private float m_bankingEfficiency = 0;
102 private float m_bankingMix = 0;
103 private float m_bankingTimescale = 0;
104
105 //Hover and Buoyancy properties
106 private BSVMotor m_hoverMotor = new BSVMotor("Hover");
107 private float m_VhoverHeight = 0f;
108 private float m_VhoverEfficiency = 0f;
109 private float m_VhoverTimescale = 0f;
110 private float m_VhoverTargetHeight = -1.0f; // if <0 then no hover, else its the current target height
111 private float m_VehicleBuoyancy = 0f; //KF: m_VehicleBuoyancy is set by VEHICLE_BUOYANCY for a vehicle.
112 // Modifies gravity. Slider between -1 (double-gravity) and 1 (full anti-gravity)
113 // KF: So far I have found no good method to combine a script-requested .Z velocity and gravity.
114 // Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity.
115
116 //Attractor properties
117 private BSVMotor m_verticalAttractionMotor = new BSVMotor("VerticalAttraction");
118 private float m_verticalAttractionEfficiency = 1.0f; // damped
119 private float m_verticalAttractionCutoff = 500f; // per the documentation
120 // Timescale > cutoff means no vert attractor.
121 private float m_verticalAttractionTimescale = 510f;
122
123 // Just some recomputed constants:
124 static readonly float PIOverFour = ((float)Math.PI) / 4f;
125 static readonly float PIOverTwo = ((float)Math.PI) / 2f;
126
127 public BSDynamics(BSScene myScene, BSPrim myPrim)
128 {
129 PhysicsScene = myScene;
130 Prim = myPrim;
131 Type = Vehicle.TYPE_NONE;
132 }
133
134 // Return 'true' if this vehicle is doing vehicle things
135 public bool IsActive
136 {
137 get { return Type != Vehicle.TYPE_NONE && Prim.IsPhysical; }
138 }
139
140 internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue)
141 {
142 VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue);
143 switch (pParam)
144 {
145 case Vehicle.ANGULAR_DEFLECTION_EFFICIENCY:
146 m_angularDeflectionEfficiency = Math.Max(pValue, 0.01f);
147 break;
148 case Vehicle.ANGULAR_DEFLECTION_TIMESCALE:
149 m_angularDeflectionTimescale = Math.Max(pValue, 0.01f);
150 break;
151 case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE:
152 m_angularMotorDecayTimescale = ClampInRange(0.01f, pValue, 120);
153 m_angularMotor.TargetValueDecayTimeScale = m_angularMotorDecayTimescale;
154 break;
155 case Vehicle.ANGULAR_MOTOR_TIMESCALE:
156 m_angularMotorTimescale = Math.Max(pValue, 0.01f);
157 m_angularMotor.TimeScale = m_angularMotorTimescale;
158 break;
159 case Vehicle.BANKING_EFFICIENCY:
160 m_bankingEfficiency = ClampInRange(-1f, pValue, 1f);
161 break;
162 case Vehicle.BANKING_MIX:
163 m_bankingMix = Math.Max(pValue, 0.01f);
164 break;
165 case Vehicle.BANKING_TIMESCALE:
166 m_bankingTimescale = Math.Max(pValue, 0.01f);
167 break;
168 case Vehicle.BUOYANCY:
169 m_VehicleBuoyancy = ClampInRange(-1f, pValue, 1f);
170 break;
171 case Vehicle.HOVER_EFFICIENCY:
172 m_VhoverEfficiency = ClampInRange(0f, pValue, 1f);
173 break;
174 case Vehicle.HOVER_HEIGHT:
175 m_VhoverHeight = pValue;
176 break;
177 case Vehicle.HOVER_TIMESCALE:
178 m_VhoverTimescale = Math.Max(pValue, 0.01f);
179 break;
180 case Vehicle.LINEAR_DEFLECTION_EFFICIENCY:
181 m_linearDeflectionEfficiency = Math.Max(pValue, 0.01f);
182 break;
183 case Vehicle.LINEAR_DEFLECTION_TIMESCALE:
184 m_linearDeflectionTimescale = Math.Max(pValue, 0.01f);
185 break;
186 case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE:
187 m_linearMotorDecayTimescale = ClampInRange(0.01f, pValue, 120);
188 m_linearMotor.TargetValueDecayTimeScale = m_linearMotorDecayTimescale;
189 break;
190 case Vehicle.LINEAR_MOTOR_TIMESCALE:
191 m_linearMotorTimescale = Math.Max(pValue, 0.01f);
192 m_linearMotor.TimeScale = m_linearMotorTimescale;
193 break;
194 case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY:
195 m_verticalAttractionEfficiency = ClampInRange(0.1f, pValue, 1f);
196 m_verticalAttractionMotor.Efficiency = m_verticalAttractionEfficiency;
197 break;
198 case Vehicle.VERTICAL_ATTRACTION_TIMESCALE:
199 m_verticalAttractionTimescale = Math.Max(pValue, 0.01f);
200 m_verticalAttractionMotor.TimeScale = m_verticalAttractionTimescale;
201 break;
202
203 // These are vector properties but the engine lets you use a single float value to
204 // set all of the components to the same value
205 case Vehicle.ANGULAR_FRICTION_TIMESCALE:
206 m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue);
207 m_angularMotor.FrictionTimescale = m_angularFrictionTimescale;
208 break;
209 case Vehicle.ANGULAR_MOTOR_DIRECTION:
210 m_angularMotorDirection = new Vector3(pValue, pValue, pValue);
211 m_angularMotor.SetTarget(m_angularMotorDirection);
212 break;
213 case Vehicle.LINEAR_FRICTION_TIMESCALE:
214 m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue);
215 m_linearMotor.FrictionTimescale = m_linearFrictionTimescale;
216 break;
217 case Vehicle.LINEAR_MOTOR_DIRECTION:
218 m_linearMotorDirection = new Vector3(pValue, pValue, pValue);
219 m_linearMotorDirectionLASTSET = new Vector3(pValue, pValue, pValue);
220 m_linearMotor.SetTarget(m_linearMotorDirection);
221 break;
222 case Vehicle.LINEAR_MOTOR_OFFSET:
223 m_linearMotorOffset = new Vector3(pValue, pValue, pValue);
224 break;
225
226 }
227 }//end ProcessFloatVehicleParam
228
229 internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue)
230 {
231 VDetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue);
232 switch (pParam)
233 {
234 case Vehicle.ANGULAR_FRICTION_TIMESCALE:
235 m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
236 m_angularMotor.FrictionTimescale = m_angularFrictionTimescale;
237 break;
238 case Vehicle.ANGULAR_MOTOR_DIRECTION:
239 // Limit requested angular speed to 2 rps= 4 pi rads/sec
240 pValue.X = ClampInRange(-12.56f, pValue.X, 12.56f);
241 pValue.Y = ClampInRange(-12.56f, pValue.Y, 12.56f);
242 pValue.Z = ClampInRange(-12.56f, pValue.Z, 12.56f);
243 m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
244 m_angularMotor.SetTarget(m_angularMotorDirection);
245 break;
246 case Vehicle.LINEAR_FRICTION_TIMESCALE:
247 m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
248 m_linearMotor.FrictionTimescale = m_linearFrictionTimescale;
249 break;
250 case Vehicle.LINEAR_MOTOR_DIRECTION:
251 m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
252 m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z);
253 m_linearMotor.SetTarget(m_linearMotorDirection);
254 break;
255 case Vehicle.LINEAR_MOTOR_OFFSET:
256 m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z);
257 break;
258 case Vehicle.BLOCK_EXIT:
259 m_BlockingEndPoint = new Vector3(pValue.X, pValue.Y, pValue.Z);
260 break;
261 }
262 }//end ProcessVectorVehicleParam
263
264 internal void ProcessRotationVehicleParam(Vehicle pParam, Quaternion pValue)
265 {
266 VDetailLog("{0},ProcessRotationalVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue);
267 switch (pParam)
268 {
269 case Vehicle.REFERENCE_FRAME:
270 m_referenceFrame = pValue;
271 break;
272 case Vehicle.ROLL_FRAME:
273 m_RollreferenceFrame = pValue;
274 break;
275 }
276 }//end ProcessRotationVehicleParam
277
278 internal void ProcessVehicleFlags(int pParam, bool remove)
279 {
280 VDetailLog("{0},ProcessVehicleFlags,param={1},remove={2}", Prim.LocalID, pParam, remove);
281 VehicleFlag parm = (VehicleFlag)pParam;
282 if (pParam == -1)
283 m_flags = (VehicleFlag)0;
284 else
285 {
286 if (remove)
287 m_flags &= ~parm;
288 else
289 m_flags |= parm;
290 }
291 }
292
293 internal void ProcessTypeChange(Vehicle pType)
294 {
295 VDetailLog("{0},ProcessTypeChange,type={1}", Prim.LocalID, pType);
296 // Set Defaults For Type
297 Type = pType;
298 switch (pType)
299 {
300 case Vehicle.TYPE_NONE:
301 m_linearMotorDirection = Vector3.Zero;
302 m_linearMotorTimescale = 0;
303 m_linearMotorDecayTimescale = 0;
304 m_linearFrictionTimescale = new Vector3(0, 0, 0);
305
306 m_angularMotorDirection = Vector3.Zero;
307 m_angularMotorDecayTimescale = 0;
308 m_angularMotorTimescale = 0;
309 m_angularFrictionTimescale = new Vector3(0, 0, 0);
310
311 m_VhoverHeight = 0;
312 m_VhoverEfficiency = 0;
313 m_VhoverTimescale = 0;
314 m_VehicleBuoyancy = 0;
315
316 m_linearDeflectionEfficiency = 1;
317 m_linearDeflectionTimescale = 1;
318
319 m_angularDeflectionEfficiency = 0;
320 m_angularDeflectionTimescale = 1000;
321
322 m_verticalAttractionEfficiency = 0;
323 m_verticalAttractionTimescale = 0;
324
325 m_bankingEfficiency = 0;
326 m_bankingTimescale = 1000;
327 m_bankingMix = 1;
328
329 m_referenceFrame = Quaternion.Identity;
330 m_flags = (VehicleFlag)0;
331
332 break;
333
334 case Vehicle.TYPE_SLED:
335 m_linearMotorDirection = Vector3.Zero;
336 m_linearMotorTimescale = 1000;
337 m_linearMotorDecayTimescale = 120;
338 m_linearFrictionTimescale = new Vector3(30, 1, 1000);
339
340 m_angularMotorDirection = Vector3.Zero;
341 m_angularMotorTimescale = 1000;
342 m_angularMotorDecayTimescale = 120;
343 m_angularFrictionTimescale = new Vector3(1000, 1000, 1000);
344
345 m_VhoverHeight = 0;
346 m_VhoverEfficiency = 10; // TODO: this looks wrong!!
347 m_VhoverTimescale = 10;
348 m_VehicleBuoyancy = 0;
349
350 m_linearDeflectionEfficiency = 1;
351 m_linearDeflectionTimescale = 1;
352
353 m_angularDeflectionEfficiency = 1;
354 m_angularDeflectionTimescale = 1000;
355
356 m_verticalAttractionEfficiency = 0;
357 m_verticalAttractionTimescale = 0;
358
359 m_bankingEfficiency = 0;
360 m_bankingTimescale = 10;
361 m_bankingMix = 1;
362
363 m_referenceFrame = Quaternion.Identity;
364 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
365 | VehicleFlag.HOVER_TERRAIN_ONLY
366 | VehicleFlag.HOVER_GLOBAL_HEIGHT
367 | VehicleFlag.HOVER_UP_ONLY);
368 m_flags |= (VehicleFlag.NO_DEFLECTION_UP
369 | VehicleFlag.LIMIT_ROLL_ONLY
370 | VehicleFlag.LIMIT_MOTOR_UP);
371
372 break;
373 case Vehicle.TYPE_CAR:
374 m_linearMotorDirection = Vector3.Zero;
375 m_linearMotorTimescale = 1;
376 m_linearMotorDecayTimescale = 60;
377 m_linearFrictionTimescale = new Vector3(100, 2, 1000);
378
379 m_angularMotorDirection = Vector3.Zero;
380 m_angularMotorTimescale = 1;
381 m_angularMotorDecayTimescale = 0.8f;
382 m_angularFrictionTimescale = new Vector3(1000, 1000, 1000);
383
384 m_VhoverHeight = 0;
385 m_VhoverEfficiency = 0;
386 m_VhoverTimescale = 1000;
387 m_VehicleBuoyancy = 0;
388
389 m_linearDeflectionEfficiency = 1;
390 m_linearDeflectionTimescale = 2;
391
392 m_angularDeflectionEfficiency = 0;
393 m_angularDeflectionTimescale = 10;
394
395 m_verticalAttractionEfficiency = 1f;
396 m_verticalAttractionTimescale = 10f;
397
398 m_bankingEfficiency = -0.2f;
399 m_bankingMix = 1;
400 m_bankingTimescale = 1;
401
402 m_referenceFrame = Quaternion.Identity;
403 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
404 | VehicleFlag.HOVER_TERRAIN_ONLY
405 | VehicleFlag.HOVER_GLOBAL_HEIGHT);
406 m_flags |= (VehicleFlag.NO_DEFLECTION_UP
407 | VehicleFlag.LIMIT_ROLL_ONLY
408 | VehicleFlag.LIMIT_MOTOR_UP
409 | VehicleFlag.HOVER_UP_ONLY);
410 break;
411 case Vehicle.TYPE_BOAT:
412 m_linearMotorDirection = Vector3.Zero;
413 m_linearMotorTimescale = 5;
414 m_linearMotorDecayTimescale = 60;
415 m_linearFrictionTimescale = new Vector3(10, 3, 2);
416
417 m_angularMotorDirection = Vector3.Zero;
418 m_angularMotorTimescale = 4;
419 m_angularMotorDecayTimescale = 4;
420 m_angularFrictionTimescale = new Vector3(10,10,10);
421
422 m_VhoverHeight = 0;
423 m_VhoverEfficiency = 0.5f;
424 m_VhoverTimescale = 2;
425 m_VehicleBuoyancy = 1;
426
427 m_linearDeflectionEfficiency = 0.5f;
428 m_linearDeflectionTimescale = 3;
429
430 m_angularDeflectionEfficiency = 0.5f;
431 m_angularDeflectionTimescale = 5;
432
433 m_verticalAttractionEfficiency = 0.5f;
434 m_verticalAttractionTimescale = 5f;
435
436 m_bankingEfficiency = -0.3f;
437 m_bankingMix = 0.8f;
438 m_bankingTimescale = 1;
439
440 m_referenceFrame = Quaternion.Identity;
441 m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY
442 | VehicleFlag.HOVER_GLOBAL_HEIGHT
443 | VehicleFlag.LIMIT_ROLL_ONLY
444 | VehicleFlag.HOVER_UP_ONLY);
445 m_flags |= (VehicleFlag.NO_DEFLECTION_UP
446 | VehicleFlag.LIMIT_MOTOR_UP
447 | VehicleFlag.HOVER_WATER_ONLY);
448 break;
449 case Vehicle.TYPE_AIRPLANE:
450 m_linearMotorDirection = Vector3.Zero;
451 m_linearMotorTimescale = 2;
452 m_linearMotorDecayTimescale = 60;
453 m_linearFrictionTimescale = new Vector3(200, 10, 5);
454
455 m_angularMotorDirection = Vector3.Zero;
456 m_angularMotorTimescale = 4;
457 m_angularMotorDecayTimescale = 4;
458 m_angularFrictionTimescale = new Vector3(20, 20, 20);
459
460 m_VhoverHeight = 0;
461 m_VhoverEfficiency = 0.5f;
462 m_VhoverTimescale = 1000;
463 m_VehicleBuoyancy = 0;
464
465 m_linearDeflectionEfficiency = 0.5f;
466 m_linearDeflectionTimescale = 3;
467
468 m_angularDeflectionEfficiency = 1;
469 m_angularDeflectionTimescale = 2;
470
471 m_verticalAttractionEfficiency = 0.9f;
472 m_verticalAttractionTimescale = 2f;
473
474 m_bankingEfficiency = 1;
475 m_bankingMix = 0.7f;
476 m_bankingTimescale = 2;
477
478 m_referenceFrame = Quaternion.Identity;
479 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
480 | VehicleFlag.HOVER_TERRAIN_ONLY
481 | VehicleFlag.HOVER_GLOBAL_HEIGHT
482 | VehicleFlag.HOVER_UP_ONLY
483 | VehicleFlag.NO_DEFLECTION_UP
484 | VehicleFlag.LIMIT_MOTOR_UP);
485 m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY);
486 break;
487 case Vehicle.TYPE_BALLOON:
488 m_linearMotorDirection = Vector3.Zero;
489 m_linearMotorTimescale = 5;
490 m_linearFrictionTimescale = new Vector3(5, 5, 5);
491 m_linearMotorDecayTimescale = 60;
492
493 m_angularMotorDirection = Vector3.Zero;
494 m_angularMotorTimescale = 6;
495 m_angularFrictionTimescale = new Vector3(10, 10, 10);
496 m_angularMotorDecayTimescale = 10;
497
498 m_VhoverHeight = 5;
499 m_VhoverEfficiency = 0.8f;
500 m_VhoverTimescale = 10;
501 m_VehicleBuoyancy = 1;
502
503 m_linearDeflectionEfficiency = 0;
504 m_linearDeflectionTimescale = 5;
505
506 m_angularDeflectionEfficiency = 0;
507 m_angularDeflectionTimescale = 5;
508
509 m_verticalAttractionEfficiency = 1f;
510 m_verticalAttractionTimescale = 100f;
511
512 m_bankingEfficiency = 0;
513 m_bankingMix = 0.7f;
514 m_bankingTimescale = 5;
515
516 m_referenceFrame = Quaternion.Identity;
517
518 m_referenceFrame = Quaternion.Identity;
519 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
520 | VehicleFlag.HOVER_TERRAIN_ONLY
521 | VehicleFlag.HOVER_UP_ONLY
522 | VehicleFlag.NO_DEFLECTION_UP
523 | VehicleFlag.LIMIT_MOTOR_UP);
524 m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY
525 | VehicleFlag.HOVER_GLOBAL_HEIGHT);
526 break;
527 }
528
529 // Update any physical parameters based on this type.
530 Refresh();
531
532 m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale,
533 m_linearMotorDecayTimescale, m_linearFrictionTimescale,
534 1f);
535 m_linearMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
536
537 m_angularMotor = new BSVMotor("AngularMotor", m_angularMotorTimescale,
538 m_angularMotorDecayTimescale, m_angularFrictionTimescale,
539 1f);
540 m_angularMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
541
542 m_verticalAttractionMotor = new BSVMotor("VerticalAttraction", m_verticalAttractionTimescale,
543 BSMotor.Infinite, BSMotor.InfiniteVector,
544 m_verticalAttractionEfficiency);
545 // Z goes away and we keep X and Y
546 m_verticalAttractionMotor.FrictionTimescale = new Vector3(BSMotor.Infinite, BSMotor.Infinite, 0.1f);
547 m_verticalAttractionMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
548 }
549
550 // Some of the properties of this prim may have changed.
551 // Do any updating needed for a vehicle
552 public void Refresh()
553 {
554 if (IsActive)
555 {
556 // Remember the mass so we don't have to fetch it every step
557 m_vehicleMass = Prim.Linkset.LinksetMass;
558
559 // Friction affects are handled by this vehicle code
560 float friction = 0f;
561 BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, friction);
562
563 // Moderate angular movement introduced by Bullet.
564 // TODO: possibly set AngularFactor and LinearFactor for the type of vehicle.
565 // Maybe compute linear and angular factor and damping from params.
566 float angularDamping = BSParam.VehicleAngularDamping;
567 BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping);
568
569 // Vehicles report collision events so we know when it's on the ground
570 BulletSimAPI.AddToCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
571
572 Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(Prim.PhysShape.ptr, m_vehicleMass);
573 BulletSimAPI.SetMassProps2(Prim.PhysBody.ptr, m_vehicleMass, localInertia);
574 BulletSimAPI.UpdateInertiaTensor2(Prim.PhysBody.ptr);
575
576 VDetailLog("{0},BSDynamics.Refresh,mass={1},frict={2},inert={3},aDamp={4}",
577 Prim.LocalID, m_vehicleMass, friction, localInertia, angularDamping);
578 }
579 else
580 {
581 BulletSimAPI.RemoveFromCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
582 }
583 }
584
585 public bool RemoveBodyDependencies(BSPhysObject prim)
586 {
587 // If active, we need to add our properties back when the body is rebuilt.
588 return IsActive;
589 }
590
591 public void RestoreBodyDependencies(BSPhysObject prim)
592 {
593 if (Prim.LocalID != prim.LocalID)
594 {
595 // The call should be on us by our prim. Error if not.
596 PhysicsScene.Logger.ErrorFormat("{0} RestoreBodyDependencies: called by not my prim. passedLocalID={1}, vehiclePrimLocalID={2}",
597 LogHeader, prim.LocalID, Prim.LocalID);
598 return;
599 }
600 Refresh();
601 }
602
603 #region Known vehicle value functions
604 // Vehicle physical parameters that we buffer from constant getting and setting.
605 // The "m_known*" values are unknown until they are fetched and the m_knownHas flag is set.
606 // Changing is remembered and the parameter is stored back into the physics engine only if updated.
607 // This does two things: 1) saves continuious calls into unmanaged code, and
608 // 2) signals when a physics property update must happen back to the simulator
609 // to update values modified for the vehicle.
610 private int m_knownChanged;
611 private int m_knownHas;
612 private float m_knownTerrainHeight;
613 private float m_knownWaterLevel;
614 private Vector3 m_knownPosition;
615 private Vector3 m_knownVelocity;
616 private Vector3 m_knownForce;
617 private Quaternion m_knownOrientation;
618 private Vector3 m_knownRotationalVelocity;
619 private Vector3 m_knownRotationalForce;
620 private Vector3 m_knownForwardVelocity; // vehicle relative forward speed
621
622 private const int m_knownChangedPosition = 1 << 0;
623 private const int m_knownChangedVelocity = 1 << 1;
624 private const int m_knownChangedForce = 1 << 2;
625 private const int m_knownChangedOrientation = 1 << 3;
626 private const int m_knownChangedRotationalVelocity = 1 << 4;
627 private const int m_knownChangedRotationalForce = 1 << 5;
628 private const int m_knownChangedTerrainHeight = 1 << 6;
629 private const int m_knownChangedWaterLevel = 1 << 7;
630 private const int m_knownChangedForwardVelocity = 1 << 8;
631
632 private void ForgetKnownVehicleProperties()
633 {
634 m_knownHas = 0;
635 m_knownChanged = 0;
636 }
637 // Push all the changed values back into the physics engine
638 private void PushKnownChanged()
639 {
640 if (m_knownChanged != 0)
641 {
642 if ((m_knownChanged & m_knownChangedPosition) != 0)
643 Prim.ForcePosition = m_knownPosition;
644
645 if ((m_knownChanged & m_knownChangedOrientation) != 0)
646 Prim.ForceOrientation = m_knownOrientation;
647
648 if ((m_knownChanged & m_knownChangedVelocity) != 0)
649 {
650 Prim.ForceVelocity = m_knownVelocity;
651 BulletSimAPI.SetInterpolationLinearVelocity2(Prim.PhysBody.ptr, VehicleVelocity);
652 }
653
654 if ((m_knownChanged & m_knownChangedForce) != 0)
655 Prim.AddForce((Vector3)m_knownForce, false, true);
656
657 if ((m_knownChanged & m_knownChangedRotationalVelocity) != 0)
658 {
659 Prim.ForceRotationalVelocity = m_knownRotationalVelocity;
660 // Fake out Bullet by making it think the velocity is the same as last time.
661 BulletSimAPI.SetInterpolationAngularVelocity2(Prim.PhysBody.ptr, m_knownRotationalVelocity);
662 }
663
664 if ((m_knownChanged & m_knownChangedRotationalForce) != 0)
665 Prim.AddAngularForce((Vector3)m_knownRotationalForce, false, true);
666
667 // If we set one of the values (ie, the physics engine didn't do it) we must force
668 // an UpdateProperties event to send the changes up to the simulator.
669 BulletSimAPI.PushUpdate2(Prim.PhysBody.ptr);
670 }
671 m_knownChanged = 0;
672 }
673
674 // Since the computation of terrain height can be a little involved, this routine
675 // is used to fetch the height only once for each vehicle simulation step.
676 private float GetTerrainHeight(Vector3 pos)
677 {
678 if ((m_knownHas & m_knownChangedTerrainHeight) == 0)
679 {
680 m_knownTerrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos);
681 m_knownHas |= m_knownChangedTerrainHeight;
682 }
683 return m_knownTerrainHeight;
684 }
685
686 // Since the computation of water level can be a little involved, this routine
687 // is used ot fetch the level only once for each vehicle simulation step.
688 private float GetWaterLevel(Vector3 pos)
689 {
690 if ((m_knownHas & m_knownChangedWaterLevel) == 0)
691 {
692 m_knownWaterLevel = Prim.PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(pos);
693 m_knownHas |= m_knownChangedWaterLevel;
694 }
695 return (float)m_knownWaterLevel;
696 }
697
698 private Vector3 VehiclePosition
699 {
700 get
701 {
702 if ((m_knownHas & m_knownChangedPosition) == 0)
703 {
704 m_knownPosition = Prim.ForcePosition;
705 m_knownHas |= m_knownChangedPosition;
706 }
707 return m_knownPosition;
708 }
709 set
710 {
711 m_knownPosition = value;
712 m_knownChanged |= m_knownChangedPosition;
713 m_knownHas |= m_knownChangedPosition;
714 }
715 }
716
717 private Quaternion VehicleOrientation
718 {
719 get
720 {
721 if ((m_knownHas & m_knownChangedOrientation) == 0)
722 {
723 m_knownOrientation = Prim.ForceOrientation;
724 m_knownHas |= m_knownChangedOrientation;
725 }
726 return m_knownOrientation;
727 }
728 set
729 {
730 m_knownOrientation = value;
731 m_knownChanged |= m_knownChangedOrientation;
732 m_knownHas |= m_knownChangedOrientation;
733 }
734 }
735
736 private Vector3 VehicleVelocity
737 {
738 get
739 {
740 if ((m_knownHas & m_knownChangedVelocity) == 0)
741 {
742 m_knownVelocity = Prim.ForceVelocity;
743 m_knownHas |= m_knownChangedVelocity;
744 }
745 return (Vector3)m_knownVelocity;
746 }
747 set
748 {
749 m_knownVelocity = value;
750 m_knownChanged |= m_knownChangedVelocity;
751 m_knownHas |= m_knownChangedVelocity;
752 }
753 }
754
755 private void VehicleAddForce(Vector3 aForce)
756 {
757 if ((m_knownHas & m_knownChangedForce) == 0)
758 {
759 m_knownForce = Vector3.Zero;
760 }
761 m_knownForce += aForce;
762 m_knownChanged |= m_knownChangedForce;
763 m_knownHas |= m_knownChangedForce;
764 }
765
766 private Vector3 VehicleRotationalVelocity
767 {
768 get
769 {
770 if ((m_knownHas & m_knownChangedRotationalVelocity) == 0)
771 {
772 m_knownRotationalVelocity = Prim.ForceRotationalVelocity;
773 m_knownHas |= m_knownChangedRotationalVelocity;
774 }
775 return (Vector3)m_knownRotationalVelocity;
776 }
777 set
778 {
779 m_knownRotationalVelocity = value;
780 m_knownChanged |= m_knownChangedRotationalVelocity;
781 m_knownHas |= m_knownChangedRotationalVelocity;
782 }
783 }
784 private void VehicleAddAngularForce(Vector3 aForce)
785 {
786 if ((m_knownHas & m_knownChangedRotationalForce) == 0)
787 {
788 m_knownRotationalForce = Vector3.Zero;
789 }
790 m_knownRotationalForce += aForce;
791 m_knownChanged |= m_knownChangedRotationalForce;
792 m_knownHas |= m_knownChangedRotationalForce;
793 }
794 // Vehicle relative forward velocity
795 private Vector3 VehicleForwardVelocity
796 {
797 get
798 {
799 if ((m_knownHas & m_knownChangedForwardVelocity) == 0)
800 {
801 m_knownForwardVelocity = VehicleVelocity * Quaternion.Inverse(Quaternion.Normalize(VehicleOrientation));
802 m_knownHas |= m_knownChangedForwardVelocity;
803 }
804 return m_knownForwardVelocity;
805 }
806 }
807 private float VehicleForwardSpeed
808 {
809 get
810 {
811 return VehicleForwardVelocity.X;
812 }
813 }
814
815 #endregion // Known vehicle value functions
816
817 // One step of the vehicle properties for the next 'pTimestep' seconds.
818 internal void Step(float pTimestep)
819 {
820 if (!IsActive) return;
821
822 ForgetKnownVehicleProperties();
823
824 MoveLinear(pTimestep);
825 MoveAngular(pTimestep);
826
827 LimitRotation(pTimestep);
828
829 // remember the position so next step we can limit absolute movement effects
830 m_lastPositionVector = VehiclePosition;
831
832 // If we forced the changing of some vehicle parameters, update the values and
833 // for the physics engine to note the changes so an UpdateProperties event will happen.
834 PushKnownChanged();
835
836 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
837 Prim.LocalID, VehiclePosition, Prim.Force, VehicleVelocity, VehicleRotationalVelocity);
838 }
839
840 // Apply the effect of the linear motor and other linear motions (like hover and float).
841 private void MoveLinear(float pTimestep)
842 {
843 Vector3 linearMotorContribution = m_linearMotor.Step(pTimestep);
844
845 // The movement computed in the linear motor is relative to the vehicle
846 // coordinates. Rotate the movement to world coordinates.
847 linearMotorContribution *= VehicleOrientation;
848
849 // ==================================================================
850 // Buoyancy: force to overcome gravity.
851 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
852 // So, if zero, don't change anything (let gravity happen). If one, negate the effect of gravity.
853 Vector3 buoyancyContribution = Prim.PhysicsScene.DefaultGravity * m_VehicleBuoyancy;
854
855 Vector3 terrainHeightContribution = ComputeLinearTerrainHeightCorrection(pTimestep);
856
857 Vector3 hoverContribution = ComputeLinearHover(pTimestep);
858
859 ComputeLinearBlockingEndPoint(pTimestep);
860
861 Vector3 limitMotorUpContribution = ComputeLinearMotorUp(pTimestep);
862
863 // ==================================================================
864 Vector3 newVelocity = linearMotorContribution
865 + terrainHeightContribution
866 + hoverContribution
867 + limitMotorUpContribution;
868
869 Vector3 newForce = buoyancyContribution;
870
871 // If not changing some axis, reduce out velocity
872 if ((m_flags & (VehicleFlag.NO_X)) != 0)
873 newVelocity.X = 0;
874 if ((m_flags & (VehicleFlag.NO_Y)) != 0)
875 newVelocity.Y = 0;
876 if ((m_flags & (VehicleFlag.NO_Z)) != 0)
877 newVelocity.Z = 0;
878
879 // ==================================================================
880 // Clamp high or low velocities
881 float newVelocityLengthSq = newVelocity.LengthSquared();
882 if (newVelocityLengthSq > 1000f)
883 {
884 newVelocity /= newVelocity.Length();
885 newVelocity *= 1000f;
886 }
887 else if (newVelocityLengthSq < 0.001f)
888 newVelocity = Vector3.Zero;
889
890 // ==================================================================
891 // Stuff new linear velocity into the vehicle.
892 // Since the velocity is just being set, it is not scaled by pTimeStep. Bullet will do that for us.
893 VehicleVelocity = newVelocity;
894
895 // Other linear forces are applied as forces.
896 Vector3 totalDownForce = newForce * m_vehicleMass;
897 if (!totalDownForce.ApproxEquals(Vector3.Zero, 0.01f))
898 {
899 VehicleAddForce(totalDownForce);
900 }
901
902 VDetailLog("{0}, MoveLinear,done,newVel={1},totDown={2},IsColliding={3}",
903 Prim.LocalID, newVelocity, totalDownForce, Prim.IsColliding);
904 VDetailLog("{0}, MoveLinear,done,linContrib={1},terrContrib={2},hoverContrib={3},limitContrib={4},buoyContrib={5}",
905 Prim.LocalID,
906 linearMotorContribution, terrainHeightContribution, hoverContribution,
907 limitMotorUpContribution, buoyancyContribution
908 );
909
910 } // end MoveLinear()
911
912 public Vector3 ComputeLinearTerrainHeightCorrection(float pTimestep)
913 {
914 Vector3 ret = Vector3.Zero;
915 // If below the terrain, move us above the ground a little.
916 // TODO: Consider taking the rotated size of the object or possibly casting a ray.
917 if (VehiclePosition.Z < GetTerrainHeight(VehiclePosition))
918 {
919 // TODO: correct position by applying force rather than forcing position.
920 Vector3 newPosition = VehiclePosition;
921 newPosition.Z = GetTerrainHeight(VehiclePosition) + 1f;
922 VehiclePosition = newPosition;
923 VDetailLog("{0}, MoveLinear,terrainHeight,terrainHeight={1},pos={2}",
924 Prim.LocalID, GetTerrainHeight(VehiclePosition), VehiclePosition);
925 }
926 return ret;
927 }
928
929 public Vector3 ComputeLinearHover(float pTimestep)
930 {
931 Vector3 ret = Vector3.Zero;
932
933 // m_VhoverEfficiency: 0=bouncy, 1=totally damped
934 // m_VhoverTimescale: time to achieve height
935 if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
936 {
937 // We should hover, get the target height
938 if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0)
939 {
940 m_VhoverTargetHeight = GetWaterLevel(VehiclePosition) + m_VhoverHeight;
941 }
942 if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
943 {
944 m_VhoverTargetHeight = GetTerrainHeight(VehiclePosition) + m_VhoverHeight;
945 }
946 if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
947 {
948 m_VhoverTargetHeight = m_VhoverHeight;
949 }
950
951 if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0)
952 {
953 // If body is already heigher, use its height as target height
954 if (VehiclePosition.Z > m_VhoverTargetHeight)
955 m_VhoverTargetHeight = VehiclePosition.Z;
956 }
957
958 if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0)
959 {
960 if (Math.Abs(VehiclePosition.Z - m_VhoverTargetHeight) > 0.2f)
961 {
962 Vector3 pos = VehiclePosition;
963 pos.Z = m_VhoverTargetHeight;
964 VehiclePosition = pos;
965 }
966 }
967 else
968 {
969 // Error is positive if below the target and negative if above.
970 float verticalError = m_VhoverTargetHeight - VehiclePosition.Z;
971 float verticalCorrectionVelocity = verticalError / m_VhoverTimescale;
972
973 // TODO: implement m_VhoverEfficiency correctly
974 if (Math.Abs(verticalError) > m_VhoverEfficiency)
975 {
976 ret = new Vector3(0f, 0f, verticalCorrectionVelocity);
977 }
978 }
979
980 VDetailLog("{0}, MoveLinear,hover,pos={1},ret={2},hoverTS={3},height={4},target={5}",
981 Prim.LocalID, VehiclePosition, ret, m_VhoverTimescale, m_VhoverHeight, m_VhoverTargetHeight);
982 }
983
984 return ret;
985 }
986
987 public bool ComputeLinearBlockingEndPoint(float pTimestep)
988 {
989 bool changed = false;
990
991 Vector3 pos = VehiclePosition;
992 Vector3 posChange = pos - m_lastPositionVector;
993 if (m_BlockingEndPoint != Vector3.Zero)
994 {
995 if (pos.X >= (m_BlockingEndPoint.X - (float)1))
996 {
997 pos.X -= posChange.X + 1;
998 changed = true;
999 }
1000 if (pos.Y >= (m_BlockingEndPoint.Y - (float)1))
1001 {
1002 pos.Y -= posChange.Y + 1;
1003 changed = true;
1004 }
1005 if (pos.Z >= (m_BlockingEndPoint.Z - (float)1))
1006 {
1007 pos.Z -= posChange.Z + 1;
1008 changed = true;
1009 }
1010 if (pos.X <= 0)
1011 {
1012 pos.X += posChange.X + 1;
1013 changed = true;
1014 }
1015 if (pos.Y <= 0)
1016 {
1017 pos.Y += posChange.Y + 1;
1018 changed = true;
1019 }
1020 if (changed)
1021 {
1022 VehiclePosition = pos;
1023 VDetailLog("{0}, MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}",
1024 Prim.LocalID, m_BlockingEndPoint, posChange, pos);
1025 }
1026 }
1027 return changed;
1028 }
1029
1030 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
1031 // Prevent ground vehicles from motoring into the sky. This flag has a subtle effect when
1032 // used with conjunction with banking: the strength of the banking will decay when the
1033 // vehicle no longer experiences collisions. The decay timescale is the same as
1034 // VEHICLE_BANKING_TIMESCALE. This is to help prevent ground vehicles from steering
1035 // when they are in mid jump.
1036 // TODO: this code is wrong. Also, what should it do for boats (height from water)?
1037 // This is just using the ground and a general collision check. Should really be using
1038 // a downward raycast to find what is below.
1039 public Vector3 ComputeLinearMotorUp(float pTimestep)
1040 {
1041 Vector3 ret = Vector3.Zero;
1042 float distanceAboveGround = 0f;
1043
1044 if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
1045 {
1046 float targetHeight = Type == Vehicle.TYPE_BOAT ? GetWaterLevel(VehiclePosition) : GetTerrainHeight(VehiclePosition);
1047 distanceAboveGround = VehiclePosition.Z - targetHeight;
1048 // Not colliding if the vehicle is off the ground
1049 if (!Prim.IsColliding)
1050 {
1051 // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale);
1052 ret = new Vector3(0, 0, -distanceAboveGround);
1053 }
1054 // TODO: this calculation is wrong. From the description at
1055 // (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce
1056 // has a decay factor. This says this force should
1057 // be computed with a motor.
1058 // TODO: add interaction with banking.
1059 }
1060 VDetailLog("{0}, MoveLinear,limitMotorUp,distAbove={1},colliding={2},ret={3}",
1061 Prim.LocalID, distanceAboveGround, Prim.IsColliding, ret);
1062 return ret;
1063 }
1064
1065 // =======================================================================
1066 // =======================================================================
1067 // Apply the effect of the angular motor.
1068 // The 'contribution' is how much angular correction velocity each function wants.
1069 // All the contributions are added together and the resulting velocity is
1070 // set directly on the vehicle.
1071 private void MoveAngular(float pTimestep)
1072 {
1073 // The user wants this many radians per second angular change?
1074 Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep);
1075
1076 // ==================================================================
1077 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
1078 // This flag prevents linear deflection parallel to world z-axis. This is useful
1079 // for preventing ground vehicles with large linear deflection, like bumper cars,
1080 // from climbing their linear deflection into the sky.
1081 // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement
1082 if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
1083 {
1084 angularMotorContribution.X = 0f;
1085 angularMotorContribution.Y = 0f;
1086 VDetailLog("{0}, MoveAngular,noDeflectionUp,angularMotorContrib={1}", Prim.LocalID, angularMotorContribution);
1087 }
1088
1089 Vector3 verticalAttractionContribution = ComputeAngularVerticalAttraction();
1090
1091 Vector3 deflectionContribution = ComputeAngularDeflection();
1092
1093 Vector3 bankingContribution = ComputeAngularBanking();
1094
1095 // ==================================================================
1096 m_lastVertAttractor = verticalAttractionContribution;
1097
1098 m_lastAngularVelocity = angularMotorContribution
1099 + verticalAttractionContribution
1100 + deflectionContribution
1101 + bankingContribution;
1102
1103 // ==================================================================
1104 // Apply the correction velocity.
1105 // TODO: Should this be applied as an angular force (torque)?
1106 if (!m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f))
1107 {
1108 VehicleRotationalVelocity = m_lastAngularVelocity;
1109
1110 VDetailLog("{0}, MoveAngular,done,nonZero,angMotorContrib={1},vertAttrContrib={2},bankContrib={3},deflectContrib={4},totalContrib={5}",
1111 Prim.LocalID,
1112 angularMotorContribution, verticalAttractionContribution,
1113 bankingContribution, deflectionContribution,
1114 m_lastAngularVelocity
1115 );
1116 }
1117 else
1118 {
1119 // The vehicle is not adding anything angular wise.
1120 VehicleRotationalVelocity = Vector3.Zero;
1121 VDetailLog("{0}, MoveAngular,done,zero", Prim.LocalID);
1122 }
1123
1124 // ==================================================================
1125 //Offset section
1126 if (m_linearMotorOffset != Vector3.Zero)
1127 {
1128 //Offset of linear velocity doesn't change the linear velocity,
1129 // but causes a torque to be applied, for example...
1130 //
1131 // IIIII >>> IIIII
1132 // IIIII >>> IIIII
1133 // IIIII >>> IIIII
1134 // ^
1135 // | Applying a force at the arrow will cause the object to move forward, but also rotate
1136 //
1137 //
1138 // The torque created is the linear velocity crossed with the offset
1139
1140 // TODO: this computation should be in the linear section
1141 // because that is where we know the impulse being applied.
1142 Vector3 torqueFromOffset = Vector3.Zero;
1143 // torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse);
1144 if (float.IsNaN(torqueFromOffset.X))
1145 torqueFromOffset.X = 0;
1146 if (float.IsNaN(torqueFromOffset.Y))
1147 torqueFromOffset.Y = 0;
1148 if (float.IsNaN(torqueFromOffset.Z))
1149 torqueFromOffset.Z = 0;
1150
1151 VehicleAddAngularForce(torqueFromOffset * m_vehicleMass);
1152 VDetailLog("{0}, BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset);
1153 }
1154
1155 }
1156 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
1157 // Some vehicles, like boats, should always keep their up-side up. This can be done by
1158 // enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to
1159 // the world z-axis (a.k.a. "up"). To take advantage of this feature you would set the
1160 // VEHICLE_VERTICAL_ATTRACTION_TIMESCALE to control the period of the spring frequency,
1161 // and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An
1162 // efficiency of 0.0 will cause the spring to wobble around its equilibrium, while an
1163 // efficiency of 1.0 will cause the spring to reach its equilibrium with exponential decay.
1164 public Vector3 ComputeAngularVerticalAttraction()
1165 {
1166 Vector3 ret = Vector3.Zero;
1167
1168 // If vertical attaction timescale is reasonable
1169 if (m_verticalAttractionTimescale < m_verticalAttractionCutoff)
1170 {
1171 // Take a vector pointing up and convert it from world to vehicle relative coords.
1172 Vector3 verticalError = Vector3.UnitZ * VehicleOrientation;
1173
1174 // If vertical attraction correction is needed, the vector that was pointing up (UnitZ)
1175 // is now:
1176 // leaning to one side: rotated around the X axis with the Y value going
1177 // from zero (nearly straight up) to one (completely to the side)) or
1178 // leaning front-to-back: rotated around the Y axis with the value of X being between
1179 // zero and one.
1180 // The value of Z is how far the rotation is off with 1 meaning none and 0 being 90 degrees.
1181
1182 // Y error means needed rotation around X axis and visa versa.
1183 // Since the error goes from zero to one, the asin is the corresponding angle.
1184 ret.X = (float)Math.Asin(verticalError.Y);
1185 // (Tilt forward (positive X) needs to tilt back (rotate negative) around Y axis.)
1186 ret.Y = -(float)Math.Asin(verticalError.X);
1187
1188 // If verticalError.Z is negative, the vehicle is upside down. Add additional push.
1189 if (verticalError.Z < 0f)
1190 {
1191 ret.X += PIOverFour;
1192 ret.Y += PIOverFour;
1193 }
1194
1195 // 'ret' is now the necessary velocity to correct tilt in one second.
1196 // Correction happens over a number of seconds.
1197 Vector3 unscaledContrib = ret;
1198 ret /= m_verticalAttractionTimescale;
1199
1200 VDetailLog("{0}, MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},eff={3},ts={4},vertAttr={5}",
1201 Prim.LocalID, verticalError, unscaledContrib, m_verticalAttractionEfficiency, m_verticalAttractionTimescale, ret);
1202 }
1203 return ret;
1204 }
1205
1206 // Return the angular correction to correct the direction the vehicle is pointing to be
1207 // the direction is should want to be pointing.
1208 // The vehicle is moving in some direction and correct its orientation to it is pointing
1209 // in that direction.
1210 // TODO: implement reference frame.
1211 public Vector3 ComputeAngularDeflection()
1212 {
1213 Vector3 ret = Vector3.Zero;
1214 return ret; // DEBUG DEBUG DEBUG
1215 // Disable angular deflection for the moment.
1216 // Since angularMotorUp and angularDeflection are computed independently, they will calculate
1217 // approximately the same X or Y correction. When added together (when contributions are combined)
1218 // this creates an over-correction and then wabbling as the target is overshot.
1219 // TODO: rethink how the different correction computations inter-relate.
1220
1221 if (m_angularDeflectionEfficiency != 0)
1222 {
1223 // The direction the vehicle is moving
1224 Vector3 movingDirection = VehicleVelocity;
1225 movingDirection.Normalize();
1226
1227 // The direction the vehicle is pointing
1228 Vector3 pointingDirection = Vector3.UnitX * VehicleOrientation;
1229 pointingDirection.Normalize();
1230
1231 // The difference between what is and what should be.
1232 Vector3 deflectionError = movingDirection - pointingDirection;
1233
1234 // Don't try to correct very large errors (not our job)
1235 if (Math.Abs(deflectionError.X) > PIOverFour) deflectionError.X = 0f;
1236 if (Math.Abs(deflectionError.Y) > PIOverFour) deflectionError.Y = 0f;
1237 if (Math.Abs(deflectionError.Z) > PIOverFour) deflectionError.Z = 0f;
1238
1239 // ret = m_angularDeflectionCorrectionMotor(1f, deflectionError);
1240
1241 // Scale the correction by recovery timescale and efficiency
1242 ret = (-deflectionError) * m_angularDeflectionEfficiency;
1243 ret /= m_angularDeflectionTimescale;
1244
1245 VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}",
1246 Prim.LocalID, movingDirection, pointingDirection, deflectionError, ret);
1247 VDetailLog("{0}, MoveAngular,Deflection,fwdSpd={1},defEff={2},defTS={3}",
1248 Prim.LocalID, VehicleForwardSpeed, m_angularDeflectionEfficiency, m_angularDeflectionTimescale);
1249 }
1250 return ret;
1251 }
1252
1253 // Return an angular change to rotate the vehicle around the Z axis when the vehicle
1254 // is tipped around the X axis.
1255 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
1256 // The vertical attractor feature must be enabled in order for the banking behavior to
1257 // function. The way banking works is this: a rotation around the vehicle's roll-axis will
1258 // produce a angular velocity around the yaw-axis, causing the vehicle to turn. The magnitude
1259 // of the yaw effect will be proportional to the
1260 // VEHICLE_BANKING_EFFICIENCY, the angle of the roll rotation, and sometimes the vehicle's
1261 // velocity along its preferred axis of motion.
1262 // The VEHICLE_BANKING_EFFICIENCY can vary between -1 and +1. When it is positive then any
1263 // positive rotation (by the right-hand rule) about the roll-axis will effect a
1264 // (negative) torque around the yaw-axis, making it turn to the right--that is the
1265 // vehicle will lean into the turn, which is how real airplanes and motorcycle's work.
1266 // Negating the banking coefficient will make it so that the vehicle leans to the
1267 // outside of the turn (not very "physical" but might allow interesting vehicles so why not?).
1268 // The VEHICLE_BANKING_MIX is a fake (i.e. non-physical) parameter that is useful for making
1269 // banking vehicles do what you want rather than what the laws of physics allow.
1270 // For example, consider a real motorcycle...it must be moving forward in order for
1271 // it to turn while banking, however video-game motorcycles are often configured
1272 // to turn in place when at a dead stop--because they are often easier to control
1273 // that way using the limited interface of the keyboard or game controller. The
1274 // VEHICLE_BANKING_MIX enables combinations of both realistic and non-realistic
1275 // banking by functioning as a slider between a banking that is correspondingly
1276 // totally static (0.0) and totally dynamic (1.0). By "static" we mean that the
1277 // banking effect depends only on the vehicle's rotation about its roll-axis compared
1278 // to "dynamic" where the banking is also proportional to its velocity along its
1279 // roll-axis. Finding the best value of the "mixture" will probably require trial and error.
1280 // The time it takes for the banking behavior to defeat a preexisting angular velocity about the
1281 // world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to
1282 // bank quickly then give it a banking timescale of about a second or less, otherwise you can
1283 // make a sluggish vehicle by giving it a timescale of several seconds.
1284 public Vector3 ComputeAngularBanking()
1285 {
1286 Vector3 ret = Vector3.Zero;
1287
1288 if (m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff)
1289 {
1290 // This works by rotating a unit vector to the orientation of the vehicle. The
1291 // roll (tilt) will be Y component of a tilting Z vector (zero for no tilt
1292 // up to one for full over).
1293 Vector3 rollComponents = Vector3.UnitZ * VehicleOrientation;
1294
1295 // Figure out the yaw value for this much roll.
1296 float turnComponent = rollComponents.Y * rollComponents.Y * m_bankingEfficiency;
1297 // Keep the sign
1298 if (rollComponents.Y < 0f)
1299 turnComponent = -turnComponent;
1300
1301 // TODO: there must be a better computation of the banking force.
1302 float bankingTurnForce = turnComponent;
1303
1304 // actual error = static turn error + dynamic turn error
1305 float mixedBankingError = bankingTurnForce * (1f - m_bankingMix) + bankingTurnForce * m_bankingMix * VehicleForwardSpeed;
1306 // TODO: the banking effect should not go to infinity but what to limit it to?
1307 mixedBankingError = ClampInRange(-20f, mixedBankingError, 20f);
1308
1309 // Build the force vector to change rotation from what it is to what it should be
1310 ret.Z = -mixedBankingError;
1311
1312 // Don't do it all at once.
1313 ret /= m_bankingTimescale;
1314
1315 VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},turnComp={3},bankErr={4},mixedBankErr={5},ret={6}",
1316 Prim.LocalID, rollComponents, VehicleForwardSpeed, turnComponent, bankingTurnForce, mixedBankingError, ret);
1317 }
1318 return ret;
1319 }
1320
1321 // This is from previous instantiations of XXXDynamics.cs.
1322 // Applies roll reference frame.
1323 // TODO: is this the right way to separate the code to do this operation?
1324 // Should this be in MoveAngular()?
1325 internal void LimitRotation(float timestep)
1326 {
1327 Quaternion rotq = VehicleOrientation;
1328 Quaternion m_rot = rotq;
1329 if (m_RollreferenceFrame != Quaternion.Identity)
1330 {
1331 if (rotq.X >= m_RollreferenceFrame.X)
1332 {
1333 m_rot.X = rotq.X - (m_RollreferenceFrame.X / 2);
1334 }
1335 if (rotq.Y >= m_RollreferenceFrame.Y)
1336 {
1337 m_rot.Y = rotq.Y - (m_RollreferenceFrame.Y / 2);
1338 }
1339 if (rotq.X <= -m_RollreferenceFrame.X)
1340 {
1341 m_rot.X = rotq.X + (m_RollreferenceFrame.X / 2);
1342 }
1343 if (rotq.Y <= -m_RollreferenceFrame.Y)
1344 {
1345 m_rot.Y = rotq.Y + (m_RollreferenceFrame.Y / 2);
1346 }
1347 }
1348 if ((m_flags & VehicleFlag.LOCK_ROTATION) != 0)
1349 {
1350 m_rot.X = 0;
1351 m_rot.Y = 0;
1352 }
1353 if (rotq != m_rot)
1354 {
1355 VehicleOrientation = m_rot;
1356 VDetailLog("{0}, LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot);
1357 }
1358
1359 }
1360
1361 private float ClampInRange(float low, float val, float high)
1362 {
1363 return Math.Max(low, Math.Min(val, high));
1364 // return Utils.Clamp(val, low, high);
1365 }
1366
1367 // Invoke the detailed logger and output something if it's enabled.
1368 private void VDetailLog(string msg, params Object[] args)
1369 {
1370 if (Prim.PhysicsScene.VehicleLoggingEnabled)
1371 Prim.PhysicsScene.DetailLog(msg, args);
1372 }
1373 }
1374}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSLinkset.cs
new file mode 100644
index 0000000..253128b
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSLinkset.cs
@@ -0,0 +1,333 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OMV = OpenMetaverse;
32
33namespace OpenSim.Region.Physics.BulletSNPlugin
34{
35
36// A BSPrim can get individual information about its linkedness attached
37// to it through an instance of a subclass of LinksetInfo.
38// Each type of linkset will define the information needed for its type.
39public abstract class BSLinksetInfo
40{
41 public virtual void Clear() { }
42}
43
44public abstract class BSLinkset
45{
46 // private static string LogHeader = "[BULLETSIM LINKSET]";
47
48 public enum LinksetImplementation
49 {
50 Constraint = 0, // linkset tied together with constraints
51 Compound = 1, // linkset tied together as a compound object
52 Manual = 2 // linkset tied together manually (code moves all the pieces)
53 }
54 // Create the correct type of linkset for this child
55 public static BSLinkset Factory(BSScene physScene, BSPhysObject parent)
56 {
57 BSLinkset ret = null;
58
59 switch ((int)BSParam.LinksetImplementation)
60 {
61 case (int)LinksetImplementation.Constraint:
62 ret = new BSLinksetConstraints(physScene, parent);
63 break;
64 case (int)LinksetImplementation.Compound:
65 ret = new BSLinksetCompound(physScene, parent);
66 break;
67 case (int)LinksetImplementation.Manual:
68 // ret = new BSLinksetManual(physScene, parent);
69 break;
70 default:
71 ret = new BSLinksetCompound(physScene, parent);
72 break;
73 }
74 return ret;
75 }
76
77 public BSPhysObject LinksetRoot { get; protected set; }
78
79 public BSScene PhysicsScene { get; private set; }
80
81 static int m_nextLinksetID = 1;
82 public int LinksetID { get; private set; }
83
84 // The children under the root in this linkset.
85 protected HashSet<BSPhysObject> m_children;
86
87 // We lock the diddling of linkset classes to prevent any badness.
88 // This locks the modification of the instances of this class. Changes
89 // to the physical representation is done via the tainting mechenism.
90 protected object m_linksetActivityLock = new Object();
91
92 // Some linksets have a preferred physical shape.
93 // Returns SHAPE_UNKNOWN if there is no preference. Causes the correct shape to be selected.
94 public virtual BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
95 {
96 return BSPhysicsShapeType.SHAPE_UNKNOWN;
97 }
98
99 // We keep the prim's mass in the linkset structure since it could be dependent on other prims
100 protected float m_mass;
101 public float LinksetMass
102 {
103 get
104 {
105 return m_mass;
106 }
107 }
108
109 public virtual bool LinksetIsColliding { get { return false; } }
110
111 public OMV.Vector3 CenterOfMass
112 {
113 get { return ComputeLinksetCenterOfMass(); }
114 }
115
116 public OMV.Vector3 GeometricCenter
117 {
118 get { return ComputeLinksetGeometricCenter(); }
119 }
120
121 protected BSLinkset(BSScene scene, BSPhysObject parent)
122 {
123 // A simple linkset of one (no children)
124 LinksetID = m_nextLinksetID++;
125 // We create LOTS of linksets.
126 if (m_nextLinksetID <= 0)
127 m_nextLinksetID = 1;
128 PhysicsScene = scene;
129 LinksetRoot = parent;
130 m_children = new HashSet<BSPhysObject>();
131 m_mass = parent.RawMass;
132 Rebuilding = false;
133 }
134
135 // Link to a linkset where the child knows the parent.
136 // Parent changing should not happen so do some sanity checking.
137 // We return the parent's linkset so the child can track its membership.
138 // Called at runtime.
139 public BSLinkset AddMeToLinkset(BSPhysObject child)
140 {
141 lock (m_linksetActivityLock)
142 {
143 // Don't add the root to its own linkset
144 if (!IsRoot(child))
145 AddChildToLinkset(child);
146 m_mass = ComputeLinksetMass();
147 }
148 return this;
149 }
150
151 // Remove a child from a linkset.
152 // Returns a new linkset for the child which is a linkset of one (just the
153 // orphened child).
154 // Called at runtime.
155 public BSLinkset RemoveMeFromLinkset(BSPhysObject child)
156 {
157 lock (m_linksetActivityLock)
158 {
159 if (IsRoot(child))
160 {
161 // Cannot remove the root from a linkset.
162 return this;
163 }
164 RemoveChildFromLinkset(child);
165 m_mass = ComputeLinksetMass();
166 }
167
168 // The child is down to a linkset of just itself
169 return BSLinkset.Factory(PhysicsScene, child);
170 }
171
172 // Return 'true' if the passed object is the root object of this linkset
173 public bool IsRoot(BSPhysObject requestor)
174 {
175 return (requestor.LocalID == LinksetRoot.LocalID);
176 }
177
178 public int NumberOfChildren { get { return m_children.Count; } }
179
180 // Return 'true' if this linkset has any children (more than the root member)
181 public bool HasAnyChildren { get { return (m_children.Count > 0); } }
182
183 // Return 'true' if this child is in this linkset
184 public bool HasChild(BSPhysObject child)
185 {
186 bool ret = false;
187 lock (m_linksetActivityLock)
188 {
189 ret = m_children.Contains(child);
190 /* Safer version but the above should work
191 foreach (BSPhysObject bp in m_children)
192 {
193 if (child.LocalID == bp.LocalID)
194 {
195 ret = true;
196 break;
197 }
198 }
199 */
200 }
201 return ret;
202 }
203
204 // Perform an action on each member of the linkset including root prim.
205 // Depends on the action on whether this should be done at taint time.
206 public delegate bool ForEachMemberAction(BSPhysObject obj);
207 public virtual bool ForEachMember(ForEachMemberAction action)
208 {
209 bool ret = false;
210 lock (m_linksetActivityLock)
211 {
212 action(LinksetRoot);
213 foreach (BSPhysObject po in m_children)
214 {
215 if (action(po))
216 break;
217 }
218 }
219 return ret;
220 }
221
222 // I am the root of a linkset and a new child is being added
223 // Called while LinkActivity is locked.
224 protected abstract void AddChildToLinkset(BSPhysObject child);
225
226 // I am the root of a linkset and one of my children is being removed.
227 // Safe to call even if the child is not really in my linkset.
228 protected abstract void RemoveChildFromLinkset(BSPhysObject child);
229
230 // When physical properties are changed the linkset needs to recalculate
231 // its internal properties.
232 // May be called at runtime or taint-time.
233 public abstract void Refresh(BSPhysObject requestor);
234
235 // Flag denoting the linkset is in the process of being rebuilt.
236 // Used to know not the schedule a rebuild in the middle of a rebuild.
237 protected bool Rebuilding { get; set; }
238
239 // The object is going dynamic (physical). Do any setup necessary
240 // for a dynamic linkset.
241 // Only the state of the passed object can be modified. The rest of the linkset
242 // has not yet been fully constructed.
243 // Return 'true' if any properties updated on the passed object.
244 // Called at taint-time!
245 public abstract bool MakeDynamic(BSPhysObject child);
246
247 // The object is going static (non-physical). Do any setup necessary
248 // for a static linkset.
249 // Return 'true' if any properties updated on the passed object.
250 // Called at taint-time!
251 public abstract bool MakeStatic(BSPhysObject child);
252
253 // Called when a parameter update comes from the physics engine for any object
254 // of the linkset is received.
255 // Passed flag is update came from physics engine (true) or the user (false).
256 // Called at taint-time!!
257 public abstract void UpdateProperties(BSPhysObject physObject, bool physicalUpdate);
258
259 // Routine used when rebuilding the body of the root of the linkset
260 // Destroy all the constraints have have been made to root.
261 // This is called when the root body is changing.
262 // Returns 'true' of something was actually removed and would need restoring
263 // Called at taint-time!!
264 public abstract bool RemoveBodyDependencies(BSPrim child);
265
266 // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
267 // this routine will restore the removed constraints.
268 // Called at taint-time!!
269 public abstract void RestoreBodyDependencies(BSPrim child);
270
271 // ================================================================
272 protected virtual float ComputeLinksetMass()
273 {
274 float mass = LinksetRoot.RawMass;
275 if (HasAnyChildren)
276 {
277 lock (m_linksetActivityLock)
278 {
279 foreach (BSPhysObject bp in m_children)
280 {
281 mass += bp.RawMass;
282 }
283 }
284 }
285 return mass;
286 }
287
288 protected virtual OMV.Vector3 ComputeLinksetCenterOfMass()
289 {
290 OMV.Vector3 com;
291 lock (m_linksetActivityLock)
292 {
293 com = LinksetRoot.Position * LinksetRoot.RawMass;
294 float totalMass = LinksetRoot.RawMass;
295
296 foreach (BSPhysObject bp in m_children)
297 {
298 com += bp.Position * bp.RawMass;
299 totalMass += bp.RawMass;
300 }
301 if (totalMass != 0f)
302 com /= totalMass;
303 }
304
305 return com;
306 }
307
308 protected virtual OMV.Vector3 ComputeLinksetGeometricCenter()
309 {
310 OMV.Vector3 com;
311 lock (m_linksetActivityLock)
312 {
313 com = LinksetRoot.Position;
314
315 foreach (BSPhysObject bp in m_children)
316 {
317 com += bp.Position * bp.RawMass;
318 }
319 com /= (m_children.Count + 1);
320 }
321
322 return com;
323 }
324
325 // Invoke the detailed logger and output something if it's enabled.
326 protected void DetailLog(string msg, params Object[] args)
327 {
328 if (PhysicsScene.PhysicsLogging.Enabled)
329 PhysicsScene.DetailLog(msg, args);
330 }
331
332}
333}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs
new file mode 100644
index 0000000..23a0b8b
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs
@@ -0,0 +1,396 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OpenSim.Framework;
32
33using OMV = OpenMetaverse;
34
35namespace OpenSim.Region.Physics.BulletSNPlugin
36{
37
38// When a child is linked, the relationship position of the child to the parent
39// is remembered so the child's world position can be recomputed when it is
40// removed from the linkset.
41sealed class BSLinksetCompoundInfo : BSLinksetInfo
42{
43 public OMV.Vector3 OffsetPos;
44 public OMV.Quaternion OffsetRot;
45 public BSLinksetCompoundInfo(OMV.Vector3 p, OMV.Quaternion r)
46 {
47 OffsetPos = p;
48 OffsetRot = r;
49 }
50 public override void Clear()
51 {
52 OffsetPos = OMV.Vector3.Zero;
53 OffsetRot = OMV.Quaternion.Identity;
54 }
55 public override string ToString()
56 {
57 StringBuilder buff = new StringBuilder();
58 buff.Append("<p=");
59 buff.Append(OffsetPos.ToString());
60 buff.Append(",r=");
61 buff.Append(OffsetRot.ToString());
62 buff.Append(">");
63 return buff.ToString();
64 }
65};
66
67public sealed class BSLinksetCompound : BSLinkset
68{
69 private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]";
70
71 public BSLinksetCompound(BSScene scene, BSPhysObject parent) : base(scene, parent)
72 {
73 }
74
75 // For compound implimented linksets, if there are children, use compound shape for the root.
76 public override BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
77 {
78 // Returning 'unknown' means we don't have a preference.
79 BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN;
80 if (IsRoot(requestor) && HasAnyChildren)
81 {
82 ret = BSPhysicsShapeType.SHAPE_COMPOUND;
83 }
84 // DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret);
85 return ret;
86 }
87
88 // When physical properties are changed the linkset needs to recalculate
89 // its internal properties.
90 public override void Refresh(BSPhysObject requestor)
91 {
92 // Something changed so do the rebuilding thing
93 // ScheduleRebuild();
94 }
95
96 // Schedule a refresh to happen after all the other taint processing.
97 private void ScheduleRebuild(BSPhysObject requestor)
98 {
99 DetailLog("{0},BSLinksetCompound.Refresh,schedulingRefresh,rebuilding={1}",
100 requestor.LocalID, Rebuilding);
101 // When rebuilding, it is possible to set properties that would normally require a rebuild.
102 // If already rebuilding, don't request another rebuild.
103 if (!Rebuilding)
104 {
105 PhysicsScene.PostTaintObject("BSLinksetCompound.Refresh", LinksetRoot.LocalID, delegate()
106 {
107 if (HasAnyChildren)
108 RecomputeLinksetCompound();
109 });
110 }
111 }
112
113 // The object is going dynamic (physical). Do any setup necessary
114 // for a dynamic linkset.
115 // Only the state of the passed object can be modified. The rest of the linkset
116 // has not yet been fully constructed.
117 // Return 'true' if any properties updated on the passed object.
118 // Called at taint-time!
119 public override bool MakeDynamic(BSPhysObject child)
120 {
121 bool ret = false;
122 DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child));
123 if (IsRoot(child))
124 {
125 // The root is going dynamic. Make sure mass is properly set.
126 m_mass = ComputeLinksetMass();
127 ScheduleRebuild(LinksetRoot);
128 }
129 else
130 {
131 // The origional prims are removed from the world as the shape of the root compound
132 // shape takes over.
133 BulletSimAPI.AddToCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
134 BulletSimAPI.ForceActivationState2(child.PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
135 // We don't want collisions from the old linkset children.
136 BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
137
138 child.PhysBody.collisionType = CollisionType.LinksetChild;
139
140 ret = true;
141 }
142 return ret;
143 }
144
145 // The object is going static (non-physical). Do any setup necessary for a static linkset.
146 // Return 'true' if any properties updated on the passed object.
147 // This doesn't normally happen -- OpenSim removes the objects from the physical
148 // world if it is a static linkset.
149 // Called at taint-time!
150 public override bool MakeStatic(BSPhysObject child)
151 {
152 bool ret = false;
153 DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child));
154 if (IsRoot(child))
155 {
156 ScheduleRebuild(LinksetRoot);
157 }
158 else
159 {
160 // The non-physical children can come back to life.
161 BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
162
163 child.PhysBody.collisionType = CollisionType.LinksetChild;
164
165 // Don't force activation so setting of DISABLE_SIMULATION can stay if used.
166 BulletSimAPI.Activate2(child.PhysBody.ptr, false);
167 ret = true;
168 }
169 return ret;
170 }
171
172 public override void UpdateProperties(BSPhysObject updated, bool physicalUpdate)
173 {
174 // The user moving a child around requires the rebuilding of the linkset compound shape
175 // One problem is this happens when a border is crossed -- the simulator implementation
176 // is to store the position into the group which causes the move of the object
177 // but it also means all the child positions get updated.
178 // What would cause an unnecessary rebuild so we make sure the linkset is in a
179 // region before bothering to do a rebuild.
180 if (!IsRoot(updated)
181 && !physicalUpdate
182 && PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
183 {
184 updated.LinksetInfo = null;
185 ScheduleRebuild(updated);
186 }
187 }
188
189 // Routine called when rebuilding the body of some member of the linkset.
190 // Since we don't keep in world relationships, do nothing unless it's a child changing.
191 // Returns 'true' of something was actually removed and would need restoring
192 // Called at taint-time!!
193 public override bool RemoveBodyDependencies(BSPrim child)
194 {
195 bool ret = false;
196
197 DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}",
198 child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(), IsRoot(child));
199
200 if (!IsRoot(child))
201 {
202 // Because it is a convenient time, recompute child world position and rotation based on
203 // its position in the linkset.
204 RecomputeChildWorldPosition(child, true);
205 }
206
207 // Cannot schedule a refresh/rebuild here because this routine is called when
208 // the linkset is being rebuilt.
209 // InternalRefresh(LinksetRoot);
210
211 return ret;
212 }
213
214 // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
215 // this routine will restore the removed constraints.
216 // Called at taint-time!!
217 public override void RestoreBodyDependencies(BSPrim child)
218 {
219 }
220
221 // When the linkset is built, the child shape is added to the compound shape relative to the
222 // root shape. The linkset then moves around but this does not move the actual child
223 // prim. The child prim's location must be recomputed based on the location of the root shape.
224 private void RecomputeChildWorldPosition(BSPhysObject child, bool inTaintTime)
225 {
226 BSLinksetCompoundInfo lci = child.LinksetInfo as BSLinksetCompoundInfo;
227 if (lci != null)
228 {
229 if (inTaintTime)
230 {
231 OMV.Vector3 oldPos = child.RawPosition;
232 child.ForcePosition = LinksetRoot.RawPosition + lci.OffsetPos;
233 child.ForceOrientation = LinksetRoot.RawOrientation * lci.OffsetRot;
234 DetailLog("{0},BSLinksetCompound.RecomputeChildWorldPosition,oldPos={1},lci={2},newPos={3}",
235 child.LocalID, oldPos, lci, child.RawPosition);
236 }
237 else
238 {
239 // TaintedObject is not used here so the raw position is set now and not at taint-time.
240 child.Position = LinksetRoot.RawPosition + lci.OffsetPos;
241 child.Orientation = LinksetRoot.RawOrientation * lci.OffsetRot;
242 }
243 }
244 else
245 {
246 // This happens when children have been added to the linkset but the linkset
247 // has not been constructed yet. So like, at taint time, adding children to a linkset
248 // and then changing properties of the children (makePhysical, for instance)
249 // but the post-print action of actually rebuilding the linkset has not yet happened.
250 // PhysicsScene.Logger.WarnFormat("{0} Restoring linkset child position failed because of no relative position computed. ID={1}",
251 // LogHeader, child.LocalID);
252 DetailLog("{0},BSLinksetCompound.recomputeChildWorldPosition,noRelativePositonInfo", child.LocalID);
253 }
254 }
255
256 // ================================================================
257
258 // Add a new child to the linkset.
259 // Called while LinkActivity is locked.
260 protected override void AddChildToLinkset(BSPhysObject child)
261 {
262 if (!HasChild(child))
263 {
264 m_children.Add(child);
265
266 DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
267
268 // Rebuild the compound shape with the new child shape included
269 ScheduleRebuild(child);
270 }
271 return;
272 }
273
274 // Remove the specified child from the linkset.
275 // Safe to call even if the child is not really in the linkset.
276 protected override void RemoveChildFromLinkset(BSPhysObject child)
277 {
278 if (m_children.Remove(child))
279 {
280 DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
281 child.LocalID,
282 LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(),
283 child.LocalID, child.PhysBody.ptr.ToString());
284
285 // Cause the child's body to be rebuilt and thus restored to normal operation
286 RecomputeChildWorldPosition(child, false);
287 child.ForceBodyShapeRebuild(false);
288
289 if (!HasAnyChildren)
290 {
291 // The linkset is now empty. The root needs rebuilding.
292 LinksetRoot.ForceBodyShapeRebuild(false);
293 }
294 else
295 {
296 // Rebuild the compound shape with the child removed
297 ScheduleRebuild(child);
298 }
299 }
300 return;
301 }
302
303 // Called before the simulation step to make sure the compound based linkset
304 // is all initialized.
305 // Constraint linksets are rebuilt every time.
306 // Note that this works for rebuilding just the root after a linkset is taken apart.
307 // Called at taint time!!
308 private void RecomputeLinksetCompound()
309 {
310 try
311 {
312 // Suppress rebuilding while rebuilding
313 Rebuilding = true;
314
315 // Cause the root shape to be rebuilt as a compound object with just the root in it
316 LinksetRoot.ForceBodyShapeRebuild(true);
317
318 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}",
319 LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren);
320
321 // Add a shape for each of the other children in the linkset
322 ForEachMember(delegate(BSPhysObject cPrim)
323 {
324 if (!IsRoot(cPrim))
325 {
326 // Compute the displacement of the child from the root of the linkset.
327 // This info is saved in the child prim so the relationship does not
328 // change over time and the new child position can be computed
329 // when the linkset is being disassembled (the linkset may have moved).
330 BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo;
331 if (lci == null)
332 {
333 // Each child position and rotation is given relative to the root.
334 OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
335 OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation;
336 OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation;
337
338 // Save relative position for recomputing child's world position after moving linkset.
339 lci = new BSLinksetCompoundInfo(displacementPos, displacementRot);
340 cPrim.LinksetInfo = lci;
341 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci);
342 }
343
344 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}",
345 LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, lci.OffsetPos, lci.OffsetRot);
346
347 if (cPrim.PhysShape.isNativeShape)
348 {
349 // A native shape is turning into a hull collision shape because native
350 // shapes are not shared so we have to hullify it so it will be tracked
351 // and freed at the correct time. This also solves the scaling problem
352 // (native shapes scaled but hull/meshes are assumed to not be).
353 // TODO: decide of the native shape can just be used in the compound shape.
354 // Use call to CreateGeomNonSpecial().
355 BulletShape saveShape = cPrim.PhysShape;
356 cPrim.PhysShape.Clear(); // Don't let the create free the child's shape
357 // PhysicsScene.Shapes.CreateGeomNonSpecial(true, cPrim, null);
358 PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null);
359 BulletShape newShape = cPrim.PhysShape;
360 cPrim.PhysShape = saveShape;
361 BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, lci.OffsetPos, lci.OffsetRot);
362 }
363 else
364 {
365 // For the shared shapes (meshes and hulls), just use the shape in the child.
366 // The reference count added here will be decremented when the compound shape
367 // is destroyed in BSShapeCollection (the child shapes are looped over and dereferenced).
368 if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape))
369 {
370 PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}",
371 LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape);
372 }
373 BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, lci.OffsetPos, lci.OffsetRot);
374 }
375 }
376 return false; // 'false' says to move onto the next child in the list
377 });
378
379 // With all of the linkset packed into the root prim, it has the mass of everyone.
380 float linksetMass = LinksetMass;
381 LinksetRoot.UpdatePhysicalMassProperties(linksetMass);
382 }
383 finally
384 {
385 Rebuilding = false;
386 }
387
388 BulletSimAPI.RecalculateCompoundShapeLocalAabb2(LinksetRoot.PhysShape.ptr);
389
390 // DEBUG: see of inter-linkset collisions are causing problems for constraint linksets.
391 // BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
392 // (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
393
394 }
395}
396} \ No newline at end of file
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetConstraints.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetConstraints.cs
new file mode 100644
index 0000000..5757e64
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetConstraints.cs
@@ -0,0 +1,314 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OMV = OpenMetaverse;
32
33namespace OpenSim.Region.Physics.BulletSNPlugin
34{
35public sealed class BSLinksetConstraints : BSLinkset
36{
37 // private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]";
38
39 public BSLinksetConstraints(BSScene scene, BSPhysObject parent) : base(scene, parent)
40 {
41 }
42
43 // When physical properties are changed the linkset needs to recalculate
44 // its internal properties.
45 // This is queued in the 'post taint' queue so the
46 // refresh will happen once after all the other taints are applied.
47 public override void Refresh(BSPhysObject requestor)
48 {
49 // Queue to happen after all the other taint processing
50 PhysicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate()
51 {
52 if (HasAnyChildren && IsRoot(requestor))
53 RecomputeLinksetConstraints();
54 });
55 }
56
57 // The object is going dynamic (physical). Do any setup necessary
58 // for a dynamic linkset.
59 // Only the state of the passed object can be modified. The rest of the linkset
60 // has not yet been fully constructed.
61 // Return 'true' if any properties updated on the passed object.
62 // Called at taint-time!
63 public override bool MakeDynamic(BSPhysObject child)
64 {
65 // What is done for each object in BSPrim is what we want.
66 return false;
67 }
68
69 // The object is going static (non-physical). Do any setup necessary for a static linkset.
70 // Return 'true' if any properties updated on the passed object.
71 // This doesn't normally happen -- OpenSim removes the objects from the physical
72 // world if it is a static linkset.
73 // Called at taint-time!
74 public override bool MakeStatic(BSPhysObject child)
75 {
76 // What is done for each object in BSPrim is what we want.
77 return false;
78 }
79
80 // Called at taint-time!!
81 public override void UpdateProperties(BSPhysObject updated, bool inTaintTime)
82 {
83 // Nothing to do for constraints on property updates
84 }
85
86 // Routine called when rebuilding the body of some member of the linkset.
87 // Destroy all the constraints have have been made to root and set
88 // up to rebuild the constraints before the next simulation step.
89 // Returns 'true' of something was actually removed and would need restoring
90 // Called at taint-time!!
91 public override bool RemoveBodyDependencies(BSPrim child)
92 {
93 bool ret = false;
94
95 DetailLog("{0},BSLinksetConstraint.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}",
96 child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString());
97
98 lock (m_linksetActivityLock)
99 {
100 // Just undo all the constraints for this linkset. Rebuild at the end of the step.
101 ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot);
102 // Cause the constraints, et al to be rebuilt before the next simulation step.
103 Refresh(LinksetRoot);
104 }
105 return ret;
106 }
107
108 // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
109 // this routine will restore the removed constraints.
110 // Called at taint-time!!
111 public override void RestoreBodyDependencies(BSPrim child)
112 {
113 // The Refresh operation queued by RemoveBodyDependencies() will build any missing constraints.
114 }
115
116 // ================================================================
117
118 // Add a new child to the linkset.
119 // Called while LinkActivity is locked.
120 protected override void AddChildToLinkset(BSPhysObject child)
121 {
122 if (!HasChild(child))
123 {
124 m_children.Add(child);
125
126 DetailLog("{0},BSLinksetConstraints.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
127
128 // Cause constraints and assorted properties to be recomputed before the next simulation step.
129 Refresh(LinksetRoot);
130 }
131 return;
132 }
133
134 // Remove the specified child from the linkset.
135 // Safe to call even if the child is not really in my linkset.
136 protected override void RemoveChildFromLinkset(BSPhysObject child)
137 {
138 if (m_children.Remove(child))
139 {
140 BSPhysObject rootx = LinksetRoot; // capture the root and body as of now
141 BSPhysObject childx = child;
142
143 DetailLog("{0},BSLinksetConstraints.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
144 childx.LocalID,
145 rootx.LocalID, rootx.PhysBody.ptr.ToString(),
146 childx.LocalID, childx.PhysBody.ptr.ToString());
147
148 PhysicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate()
149 {
150 PhysicallyUnlinkAChildFromRoot(rootx, childx);
151 });
152 // See that the linkset parameters are recomputed at the end of the taint time.
153 Refresh(LinksetRoot);
154 }
155 else
156 {
157 // Non-fatal occurance.
158 // PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader);
159 }
160 return;
161 }
162
163 // Create a constraint between me (root of linkset) and the passed prim (the child).
164 // Called at taint time!
165 private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
166 {
167 // Don't build the constraint when asked. Put it off until just before the simulation step.
168 Refresh(rootPrim);
169 }
170
171 private BSConstraint BuildConstraint(BSPhysObject rootPrim, BSPhysObject childPrim)
172 {
173 // Zero motion for children so they don't interpolate
174 childPrim.ZeroMotion(true);
175
176 // Relative position normalized to the root prim
177 // Essentually a vector pointing from center of rootPrim to center of childPrim
178 OMV.Vector3 childRelativePosition = childPrim.Position - rootPrim.Position;
179
180 // real world coordinate of midpoint between the two objects
181 OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2);
182
183 DetailLog("{0},BSLinksetConstraint.BuildConstraint,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}",
184 rootPrim.LocalID,
185 rootPrim.LocalID, rootPrim.PhysBody.ptr.ToString(),
186 childPrim.LocalID, childPrim.PhysBody.ptr.ToString(),
187 rootPrim.Position, childPrim.Position, midPoint);
188
189 // create a constraint that allows no freedom of movement between the two objects
190 // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
191
192 BSConstraint6Dof constrain = new BSConstraint6Dof(
193 PhysicsScene.World, rootPrim.PhysBody, childPrim.PhysBody, midPoint, true, true );
194 // PhysicsScene.World, childPrim.BSBody, rootPrim.BSBody, midPoint, true, true );
195
196 /* NOTE: below is an attempt to build constraint with full frame computation, etc.
197 * Using the midpoint is easier since it lets the Bullet code manipulate the transforms
198 * of the objects.
199 * Code left for future programmers.
200 // ==================================================================================
201 // relative position normalized to the root prim
202 OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation);
203 OMV.Vector3 childRelativePosition = (childPrim.Position - rootPrim.Position) * invThisOrientation;
204
205 // relative rotation of the child to the parent
206 OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation;
207 OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation);
208
209 DetailLog("{0},BSLinksetConstraint.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID);
210 BS6DofConstraint constrain = new BS6DofConstraint(
211 PhysicsScene.World, rootPrim.Body, childPrim.Body,
212 OMV.Vector3.Zero,
213 OMV.Quaternion.Inverse(rootPrim.Orientation),
214 OMV.Vector3.Zero,
215 OMV.Quaternion.Inverse(childPrim.Orientation),
216 true,
217 true
218 );
219 // ==================================================================================
220 */
221
222 PhysicsScene.Constraints.AddConstraint(constrain);
223
224 // zero linear and angular limits makes the objects unable to move in relation to each other
225 constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
226 constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
227
228 // tweek the constraint to increase stability
229 constrain.UseFrameOffset(BSParam.BoolNumeric(BSParam.LinkConstraintUseFrameOffset));
230 constrain.TranslationalLimitMotor(BSParam.BoolNumeric(BSParam.LinkConstraintEnableTransMotor),
231 BSParam.LinkConstraintTransMotorMaxVel,
232 BSParam.LinkConstraintTransMotorMaxForce);
233 constrain.SetCFMAndERP(BSParam.LinkConstraintCFM, BSParam.LinkConstraintERP);
234 if (BSParam.LinkConstraintSolverIterations != 0f)
235 {
236 constrain.SetSolverIterations(BSParam.LinkConstraintSolverIterations);
237 }
238 return constrain;
239 }
240
241 // Remove linkage between the linkset root and a particular child
242 // The root and child bodies are passed in because we need to remove the constraint between
243 // the bodies that were present at unlink time.
244 // Called at taint time!
245 private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
246 {
247 bool ret = false;
248 DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}",
249 rootPrim.LocalID,
250 rootPrim.LocalID, rootPrim.PhysBody.ptr.ToString(),
251 childPrim.LocalID, childPrim.PhysBody.ptr.ToString());
252
253 // Find the constraint for this link and get rid of it from the overall collection and from my list
254 if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody))
255 {
256 // Make the child refresh its location
257 BulletSimAPI.PushUpdate2(childPrim.PhysBody.ptr);
258 ret = true;
259 }
260
261 return ret;
262 }
263
264 // Remove linkage between myself and any possible children I might have.
265 // Returns 'true' of any constraints were destroyed.
266 // Called at taint time!
267 private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim)
268 {
269 DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID);
270
271 return PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody);
272 }
273
274 // Call each of the constraints that make up this linkset and recompute the
275 // various transforms and variables. Create constraints of not created yet.
276 // Called before the simulation step to make sure the constraint based linkset
277 // is all initialized.
278 // Called at taint time!!
279 private void RecomputeLinksetConstraints()
280 {
281 float linksetMass = LinksetMass;
282 LinksetRoot.UpdatePhysicalMassProperties(linksetMass);
283
284 // DEBUG: see of inter-linkset collisions are causing problems
285 // BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
286 // (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
287 DetailLog("{0},BSLinksetConstraint.RecomputeLinksetConstraints,set,rBody={1},linksetMass={2}",
288 LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(), linksetMass);
289
290 foreach (BSPhysObject child in m_children)
291 {
292 // A child in the linkset physically shows the mass of the whole linkset.
293 // This allows Bullet to apply enough force on the child to move the whole linkset.
294 // (Also do the mass stuff before recomputing the constraint so mass is not zero.)
295 child.UpdatePhysicalMassProperties(linksetMass);
296
297 BSConstraint constrain;
298 if (!PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain))
299 {
300 // If constraint doesn't exist yet, create it.
301 constrain = BuildConstraint(LinksetRoot, child);
302 }
303 constrain.RecomputeConstraintVariables(linksetMass);
304
305 // DEBUG: see of inter-linkset collisions are causing problems
306 // BulletSimAPI.SetCollisionFilterMask2(child.BSBody.ptr,
307 // (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
308
309 // BulletSimAPI.DumpConstraint2(PhysicsScene.World.ptr, constrain.Constraint.ptr); // DEBUG DEBUG
310 }
311
312 }
313}
314}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSMaterials.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSMaterials.cs
new file mode 100644
index 0000000..732191f
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSMaterials.cs
@@ -0,0 +1,200 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using System.Reflection;
31using Nini.Config;
32
33namespace OpenSim.Region.Physics.BulletSNPlugin
34{
35
36public struct MaterialAttributes
37{
38 // Material type values that correspond with definitions for LSL
39 public enum Material : int
40 {
41 Stone = 0,
42 Metal,
43 Glass,
44 Wood,
45 Flesh,
46 Plastic,
47 Rubber,
48 Light,
49 // Hereafter are BulletSim additions
50 Avatar,
51 NumberOfTypes // the count of types in the enum.
52 }
53
54 // Names must be in the order of the above enum.
55 // These names must coorespond to the lower case field names in the MaterialAttributes
56 // structure as reflection is used to select the field to put the value in.
57 public static readonly string[] MaterialAttribs = { "Density", "Friction", "Restitution"};
58
59 public MaterialAttributes(string t, float d, float f, float r)
60 {
61 type = t;
62 density = d;
63 friction = f;
64 restitution = r;
65 }
66 public string type;
67 public float density;
68 public float friction;
69 public float restitution;
70}
71
72public static class BSMaterials
73{
74 // Attributes for each material type
75 private static readonly MaterialAttributes[] Attributes;
76
77 // Map of material name to material type code
78 public static readonly Dictionary<string, MaterialAttributes.Material> MaterialMap;
79
80 static BSMaterials()
81 {
82 // Attribute sets for both the non-physical and physical instances of materials.
83 Attributes = new MaterialAttributes[(int)MaterialAttributes.Material.NumberOfTypes * 2];
84
85 // Map of name to type code.
86 MaterialMap = new Dictionary<string, MaterialAttributes.Material>();
87 MaterialMap.Add("Stone", MaterialAttributes.Material.Stone);
88 MaterialMap.Add("Metal", MaterialAttributes.Material.Metal);
89 MaterialMap.Add("Glass", MaterialAttributes.Material.Glass);
90 MaterialMap.Add("Wood", MaterialAttributes.Material.Wood);
91 MaterialMap.Add("Flesh", MaterialAttributes.Material.Flesh);
92 MaterialMap.Add("Plastic", MaterialAttributes.Material.Plastic);
93 MaterialMap.Add("Rubber", MaterialAttributes.Material.Rubber);
94 MaterialMap.Add("Light", MaterialAttributes.Material.Light);
95 MaterialMap.Add("Avatar", MaterialAttributes.Material.Avatar);
96 }
97
98 // This is where all the default material attributes are defined.
99 public static void InitializeFromDefaults(ConfigurationParameters parms)
100 {
101 // Values from http://wiki.secondlife.com/wiki/PRIM_MATERIAL
102 float dDensity = parms.defaultDensity;
103 float dFriction = parms.defaultFriction;
104 float dRestitution = parms.defaultRestitution;
105 Attributes[(int)MaterialAttributes.Material.Stone] =
106 new MaterialAttributes("stone",dDensity, 0.8f, 0.4f);
107 Attributes[(int)MaterialAttributes.Material.Metal] =
108 new MaterialAttributes("metal",dDensity, 0.3f, 0.4f);
109 Attributes[(int)MaterialAttributes.Material.Glass] =
110 new MaterialAttributes("glass",dDensity, 0.2f, 0.7f);
111 Attributes[(int)MaterialAttributes.Material.Wood] =
112 new MaterialAttributes("wood",dDensity, 0.6f, 0.5f);
113 Attributes[(int)MaterialAttributes.Material.Flesh] =
114 new MaterialAttributes("flesh",dDensity, 0.9f, 0.3f);
115 Attributes[(int)MaterialAttributes.Material.Plastic] =
116 new MaterialAttributes("plastic",dDensity, 0.4f, 0.7f);
117 Attributes[(int)MaterialAttributes.Material.Rubber] =
118 new MaterialAttributes("rubber",dDensity, 0.9f, 0.9f);
119 Attributes[(int)MaterialAttributes.Material.Light] =
120 new MaterialAttributes("light",dDensity, dFriction, dRestitution);
121 Attributes[(int)MaterialAttributes.Material.Avatar] =
122 new MaterialAttributes("avatar",60f, 0.2f, 0f);
123
124 Attributes[(int)MaterialAttributes.Material.Stone + (int)MaterialAttributes.Material.NumberOfTypes] =
125 new MaterialAttributes("stonePhysical",dDensity, 0.8f, 0.4f);
126 Attributes[(int)MaterialAttributes.Material.Metal + (int)MaterialAttributes.Material.NumberOfTypes] =
127 new MaterialAttributes("metalPhysical",dDensity, 0.8f, 0.4f);
128 Attributes[(int)MaterialAttributes.Material.Glass + (int)MaterialAttributes.Material.NumberOfTypes] =
129 new MaterialAttributes("glassPhysical",dDensity, 0.8f, 0.7f);
130 Attributes[(int)MaterialAttributes.Material.Wood + (int)MaterialAttributes.Material.NumberOfTypes] =
131 new MaterialAttributes("woodPhysical",dDensity, 0.8f, 0.5f);
132 Attributes[(int)MaterialAttributes.Material.Flesh + (int)MaterialAttributes.Material.NumberOfTypes] =
133 new MaterialAttributes("fleshPhysical",dDensity, 0.8f, 0.3f);
134 Attributes[(int)MaterialAttributes.Material.Plastic + (int)MaterialAttributes.Material.NumberOfTypes] =
135 new MaterialAttributes("plasticPhysical",dDensity, 0.8f, 0.7f);
136 Attributes[(int)MaterialAttributes.Material.Rubber + (int)MaterialAttributes.Material.NumberOfTypes] =
137 new MaterialAttributes("rubberPhysical",dDensity, 0.8f, 0.9f);
138 Attributes[(int)MaterialAttributes.Material.Light + (int)MaterialAttributes.Material.NumberOfTypes] =
139 new MaterialAttributes("lightPhysical",dDensity, dFriction, dRestitution);
140 Attributes[(int)MaterialAttributes.Material.Avatar + (int)MaterialAttributes.Material.NumberOfTypes] =
141 new MaterialAttributes("avatarPhysical",60f, 0.2f, 0f);
142 }
143
144 // Under the [BulletSim] section, one can change the individual material
145 // attribute values. The format of the configuration parameter is:
146 // <materialName><Attribute>["Physical"] = floatValue
147 // For instance:
148 // [BulletSim]
149 // StoneFriction = 0.2
150 // FleshRestitutionPhysical = 0.8
151 // Materials can have different parameters for their static and
152 // physical instantiations. When setting the non-physical value,
153 // both values are changed. Setting the physical value only changes
154 // the physical value.
155 public static void InitializefromParameters(IConfig pConfig)
156 {
157 foreach (KeyValuePair<string, MaterialAttributes.Material> kvp in MaterialMap)
158 {
159 string matName = kvp.Key;
160 foreach (string attribName in MaterialAttributes.MaterialAttribs)
161 {
162 string paramName = matName + attribName;
163 if (pConfig.Contains(paramName))
164 {
165 float paramValue = pConfig.GetFloat(paramName);
166 SetAttributeValue((int)kvp.Value, attribName, paramValue);
167 // set the physical value also
168 SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
169 }
170 paramName += "Physical";
171 if (pConfig.Contains(paramName))
172 {
173 float paramValue = pConfig.GetFloat(paramName);
174 SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
175 }
176 }
177 }
178 }
179
180 // Use reflection to set the value in the attribute structure.
181 private static void SetAttributeValue(int matType, string attribName, float val)
182 {
183 MaterialAttributes thisAttrib = Attributes[matType];
184 FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName.ToLower());
185 if (fieldInfo != null)
186 {
187 fieldInfo.SetValue(thisAttrib, val);
188 Attributes[matType] = thisAttrib;
189 }
190 }
191
192 // Given a material type, return a structure of attributes.
193 public static MaterialAttributes GetAttributes(MaterialAttributes.Material type, bool isPhysical)
194 {
195 int ind = (int)type;
196 if (isPhysical) ind += (int)MaterialAttributes.Material.NumberOfTypes;
197 return Attributes[ind];
198 }
199}
200}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSMotors.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSMotors.cs
new file mode 100644
index 0000000..7abc9b2
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSMotors.cs
@@ -0,0 +1,347 @@
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 */
28using System;
29using System.Collections.Generic;
30using System.Text;
31using OpenMetaverse;
32using OpenSim.Framework;
33
34namespace OpenSim.Region.Physics.BulletSNPlugin
35{
36public abstract class BSMotor
37{
38 // Timescales and other things can be turned off by setting them to 'infinite'.
39 public const float Infinite = 12345.6f;
40 public readonly static Vector3 InfiniteVector = new Vector3(BSMotor.Infinite, BSMotor.Infinite, BSMotor.Infinite);
41
42 public BSMotor(string useName)
43 {
44 UseName = useName;
45 PhysicsScene = null;
46 Enabled = true;
47 }
48 public virtual bool Enabled { get; set; }
49 public virtual void Reset() { }
50 public virtual void Zero() { }
51 public virtual void GenerateTestOutput(float timeStep) { }
52
53 // A name passed at motor creation for easily identifyable debugging messages.
54 public string UseName { get; private set; }
55
56 // Used only for outputting debug information. Might not be set so check for null.
57 public BSScene PhysicsScene { get; set; }
58 protected void MDetailLog(string msg, params Object[] parms)
59 {
60 if (PhysicsScene != null)
61 {
62 if (PhysicsScene.VehicleLoggingEnabled)
63 {
64 PhysicsScene.DetailLog(msg, parms);
65 }
66 }
67 }
68}
69
70// Motor which moves CurrentValue to TargetValue over TimeScale seconds.
71// The TargetValue decays in TargetValueDecayTimeScale and
72// the CurrentValue will be held back by FrictionTimeScale.
73// This motor will "zero itself" over time in that the targetValue will
74// decay to zero and the currentValue will follow it to that zero.
75// The overall effect is for the returned correction value to go from large
76// values (the total difference between current and target minus friction)
77// to small and eventually zero values.
78// TimeScale and TargetDelayTimeScale may be 'infinite' which means no decay.
79
80// For instance, if something is moving at speed X and the desired speed is Y,
81// CurrentValue is X and TargetValue is Y. As the motor is stepped, new
82// values of CurrentValue are returned that approach the TargetValue.
83// The feature of decaying TargetValue is so vehicles will eventually
84// come to a stop rather than run forever. This can be disabled by
85// setting TargetValueDecayTimescale to 'infinite'.
86// The change from CurrentValue to TargetValue is linear over TimeScale seconds.
87public class BSVMotor : BSMotor
88{
89 // public Vector3 FrameOfReference { get; set; }
90 // public Vector3 Offset { get; set; }
91
92 public virtual float TimeScale { get; set; }
93 public virtual float TargetValueDecayTimeScale { get; set; }
94 public virtual Vector3 FrictionTimescale { get; set; }
95 public virtual float Efficiency { get; set; }
96
97 public virtual float ErrorZeroThreshold { get; set; }
98
99 public virtual Vector3 TargetValue { get; protected set; }
100 public virtual Vector3 CurrentValue { get; protected set; }
101 public virtual Vector3 LastError { get; protected set; }
102
103 public virtual bool ErrorIsZero
104 { get {
105 return (LastError == Vector3.Zero || LastError.LengthSquared() <= ErrorZeroThreshold);
106 }
107 }
108
109 public BSVMotor(string useName)
110 : base(useName)
111 {
112 TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite;
113 Efficiency = 1f;
114 FrictionTimescale = BSMotor.InfiniteVector;
115 CurrentValue = TargetValue = Vector3.Zero;
116 ErrorZeroThreshold = 0.001f;
117 }
118 public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency)
119 : this(useName)
120 {
121 TimeScale = timeScale;
122 TargetValueDecayTimeScale = decayTimeScale;
123 FrictionTimescale = frictionTimeScale;
124 Efficiency = efficiency;
125 CurrentValue = TargetValue = Vector3.Zero;
126 }
127 public void SetCurrent(Vector3 current)
128 {
129 CurrentValue = current;
130 }
131 public void SetTarget(Vector3 target)
132 {
133 TargetValue = target;
134 }
135 public override void Zero()
136 {
137 base.Zero();
138 CurrentValue = TargetValue = Vector3.Zero;
139 }
140
141 // Compute the next step and return the new current value
142 public virtual Vector3 Step(float timeStep)
143 {
144 if (!Enabled) return TargetValue;
145
146 Vector3 origTarget = TargetValue; // DEBUG
147 Vector3 origCurrVal = CurrentValue; // DEBUG
148
149 Vector3 correction = Vector3.Zero;
150 Vector3 error = TargetValue - CurrentValue;
151 if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
152 {
153 correction = Step(timeStep, error);
154
155 CurrentValue += correction;
156
157 // The desired value reduces to zero which also reduces the difference with current.
158 // If the decay time is infinite, don't decay at all.
159 float decayFactor = 0f;
160 if (TargetValueDecayTimeScale != BSMotor.Infinite)
161 {
162 decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep;
163 TargetValue *= (1f - decayFactor);
164 }
165
166 // The amount we can correct the error is reduced by the friction
167 Vector3 frictionFactor = Vector3.Zero;
168 if (FrictionTimescale != BSMotor.InfiniteVector)
169 {
170 // frictionFactor = (Vector3.One / FrictionTimescale) * timeStep;
171 // Individual friction components can be 'infinite' so compute each separately.
172 frictionFactor.X = (FrictionTimescale.X == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.X);
173 frictionFactor.Y = (FrictionTimescale.Y == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Y);
174 frictionFactor.Z = (FrictionTimescale.Z == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Z);
175 frictionFactor *= timeStep;
176 CurrentValue *= (Vector3.One - frictionFactor);
177 }
178
179 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},err={5},corr={6}",
180 BSScene.DetailLogZero, UseName, origCurrVal, origTarget,
181 timeStep, error, correction);
182 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},tgtDecayTS={2},decayFact={3},frictTS={4},frictFact={5},tgt={6},curr={7}",
183 BSScene.DetailLogZero, UseName,
184 TargetValueDecayTimeScale, decayFactor, FrictionTimescale, frictionFactor,
185 TargetValue, CurrentValue);
186 }
187 else
188 {
189 // Difference between what we have and target is small. Motor is done.
190 CurrentValue = TargetValue;
191 MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}",
192 BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue);
193 }
194
195 return CurrentValue;
196 }
197 public virtual Vector3 Step(float timeStep, Vector3 error)
198 {
199 if (!Enabled) return Vector3.Zero;
200
201 LastError = error;
202 Vector3 returnCorrection = Vector3.Zero;
203 if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
204 {
205 // correction = error / secondsItShouldTakeToCorrect
206 Vector3 correctionAmount;
207 if (TimeScale == 0f || TimeScale == BSMotor.Infinite)
208 correctionAmount = error * timeStep;
209 else
210 correctionAmount = error / TimeScale * timeStep;
211
212 returnCorrection = correctionAmount;
213 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},timeStep={2},timeScale={3},err={4},corr={5}",
214 BSScene.DetailLogZero, UseName, timeStep, TimeScale, error, correctionAmount);
215 }
216 return returnCorrection;
217 }
218
219 // The user sets all the parameters and calls this which outputs values until error is zero.
220 public override void GenerateTestOutput(float timeStep)
221 {
222 // maximum number of outputs to generate.
223 int maxOutput = 50;
224 MDetailLog("{0},BSVMotor.Test,{1},===================================== BEGIN Test Output", BSScene.DetailLogZero, UseName);
225 MDetailLog("{0},BSVMotor.Test,{1},timeScale={2},targDlyTS={3},frictTS={4},eff={5},curr={6},tgt={7}",
226 BSScene.DetailLogZero, UseName,
227 TimeScale, TargetValueDecayTimeScale, FrictionTimescale, Efficiency,
228 CurrentValue, TargetValue);
229
230 LastError = BSMotor.InfiniteVector;
231 while (maxOutput-- > 0 && !LastError.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
232 {
233 Vector3 lastStep = Step(timeStep);
234 MDetailLog("{0},BSVMotor.Test,{1},cur={2},tgt={3},lastError={4},lastStep={5}",
235 BSScene.DetailLogZero, UseName, CurrentValue, TargetValue, LastError, lastStep);
236 }
237 MDetailLog("{0},BSVMotor.Test,{1},===================================== END Test Output", BSScene.DetailLogZero, UseName);
238
239
240 }
241
242 public override string ToString()
243 {
244 return String.Format("<{0},curr={1},targ={2},decayTS={3},frictTS={4}>",
245 UseName, CurrentValue, TargetValue, TargetValueDecayTimeScale, FrictionTimescale);
246 }
247}
248
249public class BSFMotor : BSMotor
250{
251 public float TimeScale { get; set; }
252 public float DecayTimeScale { get; set; }
253 public float Friction { get; set; }
254 public float Efficiency { get; set; }
255
256 public float Target { get; private set; }
257 public float CurrentValue { get; private set; }
258
259 public BSFMotor(string useName, float timeScale, float decayTimescale, float friction, float efficiency)
260 : base(useName)
261 {
262 }
263 public void SetCurrent(float target)
264 {
265 }
266 public void SetTarget(float target)
267 {
268 }
269 public virtual float Step(float timeStep)
270 {
271 return 0f;
272 }
273}
274
275// Proportional, Integral, Derivitive Motor
276// Good description at http://www.answers.com/topic/pid-controller . Includes processes for choosing p, i and d factors.
277public class BSPIDVMotor : BSVMotor
278{
279 // Larger makes more overshoot, smaller means converge quicker. Range of 0.1 to 10.
280 public Vector3 proportionFactor { get; set; }
281 public Vector3 integralFactor { get; set; }
282 public Vector3 derivFactor { get; set; }
283
284 // Arbritrary factor range.
285 // EfficiencyHigh means move quickly to the correct number. EfficiencyLow means might over correct.
286 public float EfficiencyHigh = 0.4f;
287 public float EfficiencyLow = 4.0f;
288
289 // Running integration of the error
290 Vector3 RunningIntegration { get; set; }
291
292 public BSPIDVMotor(string useName)
293 : base(useName)
294 {
295 proportionFactor = new Vector3(1.00f, 1.00f, 1.00f);
296 integralFactor = new Vector3(1.00f, 1.00f, 1.00f);
297 derivFactor = new Vector3(1.00f, 1.00f, 1.00f);
298 RunningIntegration = Vector3.Zero;
299 LastError = Vector3.Zero;
300 }
301
302 public override void Zero()
303 {
304 base.Zero();
305 }
306
307 public override float Efficiency
308 {
309 get { return base.Efficiency; }
310 set
311 {
312 base.Efficiency = Util.Clamp(value, 0f, 1f);
313 // Compute factors based on efficiency.
314 // If efficiency is high (1f), use a factor value that moves the error value to zero with little overshoot.
315 // If efficiency is low (0f), use a factor value that overcorrects.
316 // TODO: might want to vary contribution of different factor depending on efficiency.
317 float factor = ((1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow) / 3f;
318 // float factor = (1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow;
319 proportionFactor = new Vector3(factor, factor, factor);
320 integralFactor = new Vector3(factor, factor, factor);
321 derivFactor = new Vector3(factor, factor, factor);
322 }
323 }
324
325 // Ignore Current and Target Values and just advance the PID computation on this error.
326 public override Vector3 Step(float timeStep, Vector3 error)
327 {
328 if (!Enabled) return Vector3.Zero;
329
330 // Add up the error so we can integrate over the accumulated errors
331 RunningIntegration += error * timeStep;
332
333 // A simple derivitive is the rate of change from the last error.
334 Vector3 derivFactor = (error - LastError) * timeStep;
335 LastError = error;
336
337 // Correction = -(proportionOfPresentError + accumulationOfPastError + rateOfChangeOfError)
338 Vector3 ret = -(
339 error * proportionFactor
340 + RunningIntegration * integralFactor
341 + derivFactor * derivFactor
342 );
343
344 return ret;
345 }
346}
347}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSParam.cs
new file mode 100644
index 0000000..0bb1674
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSParam.cs
@@ -0,0 +1,559 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OpenSim.Region.Physics.Manager;
32
33using OpenMetaverse;
34using Nini.Config;
35
36namespace OpenSim.Region.Physics.BulletSNPlugin
37{
38public static class BSParam
39{
40 // Level of Detail values kept as float because that's what the Meshmerizer wants
41 public static float MeshLOD { get; private set; }
42 public static float MeshMegaPrimLOD { get; private set; }
43 public static float MeshMegaPrimThreshold { get; private set; }
44 public static float SculptLOD { get; private set; }
45
46 public static float MinimumObjectMass { get; private set; }
47 public static float MaximumObjectMass { get; private set; }
48
49 public static float LinearDamping { get; private set; }
50 public static float AngularDamping { get; private set; }
51 public static float DeactivationTime { get; private set; }
52 public static float LinearSleepingThreshold { get; private set; }
53 public static float AngularSleepingThreshold { get; private set; }
54 public static float CcdMotionThreshold { get; private set; }
55 public static float CcdSweptSphereRadius { get; private set; }
56 public static float ContactProcessingThreshold { get; private set; }
57
58 public static bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed
59 public static bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes
60 public static bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects
61
62 public static float TerrainImplementation { get; private set; }
63 public static float TerrainFriction { get; private set; }
64 public static float TerrainHitFraction { get; private set; }
65 public static float TerrainRestitution { get; private set; }
66 public static float TerrainCollisionMargin { get; private set; }
67
68 // Avatar parameters
69 public static float AvatarFriction { get; private set; }
70 public static float AvatarStandingFriction { get; private set; }
71 public static float AvatarDensity { get; private set; }
72 public static float AvatarRestitution { get; private set; }
73 public static float AvatarCapsuleWidth { get; private set; }
74 public static float AvatarCapsuleDepth { get; private set; }
75 public static float AvatarCapsuleHeight { get; private set; }
76 public static float AvatarContactProcessingThreshold { get; private set; }
77
78 public static float VehicleAngularDamping { get; private set; }
79
80 public static float LinksetImplementation { get; private set; }
81 public static float LinkConstraintUseFrameOffset { get; private set; }
82 public static float LinkConstraintEnableTransMotor { get; private set; }
83 public static float LinkConstraintTransMotorMaxVel { get; private set; }
84 public static float LinkConstraintTransMotorMaxForce { get; private set; }
85 public static float LinkConstraintERP { get; private set; }
86 public static float LinkConstraintCFM { get; private set; }
87 public static float LinkConstraintSolverIterations { get; private set; }
88
89 public static float PID_D { get; private set; } // derivative
90 public static float PID_P { get; private set; } // proportional
91
92 public delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
93 public delegate float ParamGet(BSScene scene);
94 public delegate void ParamSet(BSScene scene, string paramName, uint localID, float val);
95 public delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val);
96
97 public struct ParameterDefn
98 {
99 public string name; // string name of the parameter
100 public string desc; // a short description of what the parameter means
101 public float defaultValue; // default value if not specified anywhere else
102 public ParamUser userParam; // get the value from the configuration file
103 public ParamGet getter; // return the current value stored for this parameter
104 public ParamSet setter; // set the current value for this parameter
105 public SetOnObject onObject; // set the value on an object in the physical domain
106 public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s)
107 {
108 name = n;
109 desc = d;
110 defaultValue = v;
111 userParam = u;
112 getter = g;
113 setter = s;
114 onObject = null;
115 }
116 public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o)
117 {
118 name = n;
119 desc = d;
120 defaultValue = v;
121 userParam = u;
122 getter = g;
123 setter = s;
124 onObject = o;
125 }
126 }
127
128 // List of all of the externally visible parameters.
129 // For each parameter, this table maps a text name to getter and setters.
130 // To add a new externally referencable/settable parameter, add the paramter storage
131 // location somewhere in the program and make an entry in this table with the
132 // getters and setters.
133 // It is easiest to find an existing definition and copy it.
134 // Parameter values are floats. Booleans are converted to a floating value.
135 //
136 // A ParameterDefn() takes the following parameters:
137 // -- the text name of the parameter. This is used for console input and ini file.
138 // -- a short text description of the parameter. This shows up in the console listing.
139 // -- a default value (float)
140 // -- a delegate for fetching the parameter from the ini file.
141 // Should handle fetching the right type from the ini file and converting it.
142 // -- a delegate for getting the value as a float
143 // -- a delegate for setting the value from a float
144 // -- an optional delegate to update the value in the world. Most often used to
145 // push the new value to an in-world object.
146 //
147 // The single letter parameters for the delegates are:
148 // s = BSScene
149 // o = BSPhysObject
150 // p = string parameter name
151 // l = localID of referenced object
152 // v = value (float)
153 // cf = parameter configuration class (for fetching values from ini file)
154 private static ParameterDefn[] ParameterDefinitions =
155 {
156 new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties",
157 ConfigurationParameters.numericTrue,
158 (s,cf,p,v) => { ShouldMeshSculptedPrim = cf.GetBoolean(p, BSParam.BoolNumeric(v)); },
159 (s) => { return BSParam.NumericBool(ShouldMeshSculptedPrim); },
160 (s,p,l,v) => { ShouldMeshSculptedPrim = BSParam.BoolNumeric(v); } ),
161 new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects",
162 ConfigurationParameters.numericFalse,
163 (s,cf,p,v) => { ShouldForceSimplePrimMeshing = cf.GetBoolean(p, BSParam.BoolNumeric(v)); },
164 (s) => { return BSParam.NumericBool(ShouldForceSimplePrimMeshing); },
165 (s,p,l,v) => { ShouldForceSimplePrimMeshing = BSParam.BoolNumeric(v); } ),
166 new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects",
167 ConfigurationParameters.numericTrue,
168 (s,cf,p,v) => { ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, BSParam.BoolNumeric(v)); },
169 (s) => { return BSParam.NumericBool(ShouldUseHullsForPhysicalObjects); },
170 (s,p,l,v) => { ShouldUseHullsForPhysicalObjects = BSParam.BoolNumeric(v); } ),
171
172 new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
173 8f,
174 (s,cf,p,v) => { MeshLOD = (float)cf.GetInt(p, (int)v); },
175 (s) => { return MeshLOD; },
176 (s,p,l,v) => { MeshLOD = v; } ),
177 new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters",
178 16f,
179 (s,cf,p,v) => { MeshMegaPrimLOD = (float)cf.GetInt(p, (int)v); },
180 (s) => { return MeshMegaPrimLOD; },
181 (s,p,l,v) => { MeshMegaPrimLOD = v; } ),
182 new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD",
183 10f,
184 (s,cf,p,v) => { MeshMegaPrimThreshold = (float)cf.GetInt(p, (int)v); },
185 (s) => { return MeshMegaPrimThreshold; },
186 (s,p,l,v) => { MeshMegaPrimThreshold = v; } ),
187 new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)",
188 32f,
189 (s,cf,p,v) => { SculptLOD = (float)cf.GetInt(p, (int)v); },
190 (s) => { return SculptLOD; },
191 (s,p,l,v) => { SculptLOD = v; } ),
192
193 new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps",
194 10f,
195 (s,cf,p,v) => { s.m_maxSubSteps = cf.GetInt(p, (int)v); },
196 (s) => { return (float)s.m_maxSubSteps; },
197 (s,p,l,v) => { s.m_maxSubSteps = (int)v; } ),
198 new ParameterDefn("FixedTimeStep", "In simulation step, seconds of one substep (1/60)",
199 1f / 60f,
200 (s,cf,p,v) => { s.m_fixedTimeStep = cf.GetFloat(p, v); },
201 (s) => { return (float)s.m_fixedTimeStep; },
202 (s,p,l,v) => { s.m_fixedTimeStep = v; } ),
203 new ParameterDefn("MaxCollisionsPerFrame", "Max collisions returned at end of each frame",
204 2048f,
205 (s,cf,p,v) => { s.m_maxCollisionsPerFrame = cf.GetInt(p, (int)v); },
206 (s) => { return (float)s.m_maxCollisionsPerFrame; },
207 (s,p,l,v) => { s.m_maxCollisionsPerFrame = (int)v; } ),
208 new ParameterDefn("MaxUpdatesPerFrame", "Max updates returned at end of each frame",
209 8000f,
210 (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); },
211 (s) => { return (float)s.m_maxUpdatesPerFrame; },
212 (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
213 new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step",
214 500f,
215 (s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); },
216 (s) => { return (float)s.m_taintsToProcessPerStep; },
217 (s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ),
218 new ParameterDefn("MinObjectMass", "Minimum object mass (0.0001)",
219 0.0001f,
220 (s,cf,p,v) => { MinimumObjectMass = cf.GetFloat(p, v); },
221 (s) => { return (float)MinimumObjectMass; },
222 (s,p,l,v) => { MinimumObjectMass = v; } ),
223 new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)",
224 10000.01f,
225 (s,cf,p,v) => { MaximumObjectMass = cf.GetFloat(p, v); },
226 (s) => { return (float)MaximumObjectMass; },
227 (s,p,l,v) => { MaximumObjectMass = v; } ),
228
229 new ParameterDefn("PID_D", "Derivitive factor for motion smoothing",
230 2200f,
231 (s,cf,p,v) => { PID_D = cf.GetFloat(p, v); },
232 (s) => { return (float)PID_D; },
233 (s,p,l,v) => { PID_D = v; } ),
234 new ParameterDefn("PID_P", "Parameteric factor for motion smoothing",
235 900f,
236 (s,cf,p,v) => { PID_P = cf.GetFloat(p, v); },
237 (s) => { return (float)PID_P; },
238 (s,p,l,v) => { PID_P = v; } ),
239
240 new ParameterDefn("DefaultFriction", "Friction factor used on new objects",
241 0.2f,
242 (s,cf,p,v) => { s.UnmanagedParams[0].defaultFriction = cf.GetFloat(p, v); },
243 (s) => { return s.UnmanagedParams[0].defaultFriction; },
244 (s,p,l,v) => { s.UnmanagedParams[0].defaultFriction = v; } ),
245 new ParameterDefn("DefaultDensity", "Density for new objects" ,
246 10.000006836f, // Aluminum g/cm3
247 (s,cf,p,v) => { s.UnmanagedParams[0].defaultDensity = cf.GetFloat(p, v); },
248 (s) => { return s.UnmanagedParams[0].defaultDensity; },
249 (s,p,l,v) => { s.UnmanagedParams[0].defaultDensity = v; } ),
250 new ParameterDefn("DefaultRestitution", "Bouncyness of an object" ,
251 0f,
252 (s,cf,p,v) => { s.UnmanagedParams[0].defaultRestitution = cf.GetFloat(p, v); },
253 (s) => { return s.UnmanagedParams[0].defaultRestitution; },
254 (s,p,l,v) => { s.UnmanagedParams[0].defaultRestitution = v; } ),
255 new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)",
256 0.04f,
257 (s,cf,p,v) => { s.UnmanagedParams[0].collisionMargin = cf.GetFloat(p, v); },
258 (s) => { return s.UnmanagedParams[0].collisionMargin; },
259 (s,p,l,v) => { s.UnmanagedParams[0].collisionMargin = v; } ),
260 new ParameterDefn("Gravity", "Vertical force of gravity (negative means down)",
261 -9.80665f,
262 (s,cf,p,v) => { s.UnmanagedParams[0].gravity = cf.GetFloat(p, v); },
263 (s) => { return s.UnmanagedParams[0].gravity; },
264 (s,p,l,v) => { s.UpdateParameterObject((x)=>{s.UnmanagedParams[0].gravity=x;}, p, PhysParameterEntry.APPLY_TO_NONE, v); },
265 (s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ),
266
267
268 new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)",
269 0f,
270 (s,cf,p,v) => { LinearDamping = cf.GetFloat(p, v); },
271 (s) => { return LinearDamping; },
272 (s,p,l,v) => { s.UpdateParameterObject((x)=>{LinearDamping=x;}, p, l, v); },
273 (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, v, AngularDamping); } ),
274 new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)",
275 0f,
276 (s,cf,p,v) => { AngularDamping = cf.GetFloat(p, v); },
277 (s) => { return AngularDamping; },
278 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AngularDamping=x;}, p, l, v); },
279 (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, LinearDamping, v); } ),
280 new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static",
281 0.2f,
282 (s,cf,p,v) => { DeactivationTime = cf.GetFloat(p, v); },
283 (s) => { return DeactivationTime; },
284 (s,p,l,v) => { s.UpdateParameterObject((x)=>{DeactivationTime=x;}, p, l, v); },
285 (s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.PhysBody.ptr, v); } ),
286 new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static",
287 0.8f,
288 (s,cf,p,v) => { LinearSleepingThreshold = cf.GetFloat(p, v); },
289 (s) => { return LinearSleepingThreshold; },
290 (s,p,l,v) => { s.UpdateParameterObject((x)=>{LinearSleepingThreshold=x;}, p, l, v); },
291 (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
292 new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static",
293 1.0f,
294 (s,cf,p,v) => { AngularSleepingThreshold = cf.GetFloat(p, v); },
295 (s) => { return AngularSleepingThreshold; },
296 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AngularSleepingThreshold=x;}, p, l, v); },
297 (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
298 new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" ,
299 0f, // set to zero to disable
300 (s,cf,p,v) => { CcdMotionThreshold = cf.GetFloat(p, v); },
301 (s) => { return CcdMotionThreshold; },
302 (s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdMotionThreshold=x;}, p, l, v); },
303 (s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.PhysBody.ptr, v); } ),
304 new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" ,
305 0f,
306 (s,cf,p,v) => { CcdSweptSphereRadius = cf.GetFloat(p, v); },
307 (s) => { return CcdSweptSphereRadius; },
308 (s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdSweptSphereRadius=x;}, p, l, v); },
309 (s,o,v) => { BulletSimAPI.SetCcdSweptSphereRadius2(o.PhysBody.ptr, v); } ),
310 new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" ,
311 0.1f,
312 (s,cf,p,v) => { ContactProcessingThreshold = cf.GetFloat(p, v); },
313 (s) => { return ContactProcessingThreshold; },
314 (s,p,l,v) => { s.UpdateParameterObject((x)=>{ContactProcessingThreshold=x;}, p, l, v); },
315 (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ),
316
317 new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)",
318 (float)BSTerrainPhys.TerrainImplementation.Mesh,
319 (s,cf,p,v) => { TerrainImplementation = cf.GetFloat(p,v); },
320 (s) => { return TerrainImplementation; },
321 (s,p,l,v) => { TerrainImplementation = v; } ),
322 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
323 0.3f,
324 (s,cf,p,v) => { TerrainFriction = cf.GetFloat(p, v); },
325 (s) => { return TerrainFriction; },
326 (s,p,l,v) => { TerrainFriction = v; /* TODO: set on real terrain */} ),
327 new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" ,
328 0.8f,
329 (s,cf,p,v) => { TerrainHitFraction = cf.GetFloat(p, v); },
330 (s) => { return TerrainHitFraction; },
331 (s,p,l,v) => { TerrainHitFraction = v; /* TODO: set on real terrain */ } ),
332 new ParameterDefn("TerrainRestitution", "Bouncyness" ,
333 0f,
334 (s,cf,p,v) => { TerrainRestitution = cf.GetFloat(p, v); },
335 (s) => { return TerrainRestitution; },
336 (s,p,l,v) => { TerrainRestitution = v; /* TODO: set on real terrain */ } ),
337 new ParameterDefn("TerrainCollisionMargin", "Margin where collision checking starts" ,
338 0.04f,
339 (s,cf,p,v) => { TerrainCollisionMargin = cf.GetFloat(p, v); },
340 (s) => { return TerrainCollisionMargin; },
341 (s,p,l,v) => { TerrainCollisionMargin = v; /* TODO: set on real terrain */ } ),
342
343 new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
344 0.2f,
345 (s,cf,p,v) => { AvatarFriction = cf.GetFloat(p, v); },
346 (s) => { return AvatarFriction; },
347 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarFriction=x;}, p, l, v); } ),
348 new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
349 10.0f,
350 (s,cf,p,v) => { AvatarStandingFriction = cf.GetFloat(p, v); },
351 (s) => { return AvatarStandingFriction; },
352 (s,p,l,v) => { AvatarStandingFriction = v; } ),
353 new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.",
354 60f,
355 (s,cf,p,v) => { AvatarDensity = cf.GetFloat(p, v); },
356 (s) => { return AvatarDensity; },
357 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarDensity=x;}, p, l, v); } ),
358 new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.",
359 0f,
360 (s,cf,p,v) => { AvatarRestitution = cf.GetFloat(p, v); },
361 (s) => { return AvatarRestitution; },
362 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarRestitution=x;}, p, l, v); } ),
363 new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule",
364 0.6f,
365 (s,cf,p,v) => { AvatarCapsuleWidth = cf.GetFloat(p, v); },
366 (s) => { return AvatarCapsuleWidth; },
367 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleWidth=x;}, p, l, v); } ),
368 new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule",
369 0.45f,
370 (s,cf,p,v) => { AvatarCapsuleDepth = cf.GetFloat(p, v); },
371 (s) => { return AvatarCapsuleDepth; },
372 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleDepth=x;}, p, l, v); } ),
373 new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar",
374 1.5f,
375 (s,cf,p,v) => { AvatarCapsuleHeight = cf.GetFloat(p, v); },
376 (s) => { return AvatarCapsuleHeight; },
377 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleHeight=x;}, p, l, v); } ),
378 new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions",
379 0.1f,
380 (s,cf,p,v) => { AvatarContactProcessingThreshold = cf.GetFloat(p, v); },
381 (s) => { return AvatarContactProcessingThreshold; },
382 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarContactProcessingThreshold=x;}, p, l, v); } ),
383
384 new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)",
385 0.95f,
386 (s,cf,p,v) => { VehicleAngularDamping = cf.GetFloat(p, v); },
387 (s) => { return VehicleAngularDamping; },
388 (s,p,l,v) => { VehicleAngularDamping = v; } ),
389
390 new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)",
391 0f,
392 (s,cf,p,v) => { s.UnmanagedParams[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); },
393 (s) => { return s.UnmanagedParams[0].maxPersistantManifoldPoolSize; },
394 (s,p,l,v) => { s.UnmanagedParams[0].maxPersistantManifoldPoolSize = v; } ),
395 new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)",
396 0f,
397 (s,cf,p,v) => { s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); },
398 (s) => { return s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize; },
399 (s,p,l,v) => { s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize = v; } ),
400 new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count",
401 ConfigurationParameters.numericFalse,
402 (s,cf,p,v) => { s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
403 (s) => { return s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation; },
404 (s,p,l,v) => { s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = v; } ),
405 new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step",
406 ConfigurationParameters.numericFalse,
407 (s,cf,p,v) => { s.UnmanagedParams[0].shouldForceUpdateAllAabbs = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
408 (s) => { return s.UnmanagedParams[0].shouldForceUpdateAllAabbs; },
409 (s,p,l,v) => { s.UnmanagedParams[0].shouldForceUpdateAllAabbs = v; } ),
410 new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction",
411 ConfigurationParameters.numericTrue,
412 (s,cf,p,v) => { s.UnmanagedParams[0].shouldRandomizeSolverOrder = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
413 (s) => { return s.UnmanagedParams[0].shouldRandomizeSolverOrder; },
414 (s,p,l,v) => { s.UnmanagedParams[0].shouldRandomizeSolverOrder = v; } ),
415 new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands",
416 ConfigurationParameters.numericTrue,
417 (s,cf,p,v) => { s.UnmanagedParams[0].shouldSplitSimulationIslands = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
418 (s) => { return s.UnmanagedParams[0].shouldSplitSimulationIslands; },
419 (s,p,l,v) => { s.UnmanagedParams[0].shouldSplitSimulationIslands = v; } ),
420 new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching",
421 ConfigurationParameters.numericFalse,
422 (s,cf,p,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
423 (s) => { return s.UnmanagedParams[0].shouldEnableFrictionCaching; },
424 (s,p,l,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = v; } ),
425 new ParameterDefn("NumberOfSolverIterations", "Number of internal iterations (0 means default)",
426 0f, // zero says use Bullet default
427 (s,cf,p,v) => { s.UnmanagedParams[0].numberOfSolverIterations = cf.GetFloat(p, v); },
428 (s) => { return s.UnmanagedParams[0].numberOfSolverIterations; },
429 (s,p,l,v) => { s.UnmanagedParams[0].numberOfSolverIterations = v; } ),
430
431 new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)",
432 (float)BSLinkset.LinksetImplementation.Compound,
433 (s,cf,p,v) => { LinksetImplementation = cf.GetFloat(p,v); },
434 (s) => { return LinksetImplementation; },
435 (s,p,l,v) => { LinksetImplementation = v; } ),
436 new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.",
437 ConfigurationParameters.numericFalse,
438 (s,cf,p,v) => { LinkConstraintUseFrameOffset = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
439 (s) => { return LinkConstraintUseFrameOffset; },
440 (s,p,l,v) => { LinkConstraintUseFrameOffset = v; } ),
441 new ParameterDefn("LinkConstraintEnableTransMotor", "Whether to enable translational motor on linkset constraints",
442 ConfigurationParameters.numericTrue,
443 (s,cf,p,v) => { LinkConstraintEnableTransMotor = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
444 (s) => { return LinkConstraintEnableTransMotor; },
445 (s,p,l,v) => { LinkConstraintEnableTransMotor = v; } ),
446 new ParameterDefn("LinkConstraintTransMotorMaxVel", "Maximum velocity to be applied by translational motor in linkset constraints",
447 5.0f,
448 (s,cf,p,v) => { LinkConstraintTransMotorMaxVel = cf.GetFloat(p, v); },
449 (s) => { return LinkConstraintTransMotorMaxVel; },
450 (s,p,l,v) => { LinkConstraintTransMotorMaxVel = v; } ),
451 new ParameterDefn("LinkConstraintTransMotorMaxForce", "Maximum force to be applied by translational motor in linkset constraints",
452 0.1f,
453 (s,cf,p,v) => { LinkConstraintTransMotorMaxForce = cf.GetFloat(p, v); },
454 (s) => { return LinkConstraintTransMotorMaxForce; },
455 (s,p,l,v) => { LinkConstraintTransMotorMaxForce = v; } ),
456 new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1",
457 0.1f,
458 (s,cf,p,v) => { LinkConstraintCFM = cf.GetFloat(p, v); },
459 (s) => { return LinkConstraintCFM; },
460 (s,p,l,v) => { LinkConstraintCFM = v; } ),
461 new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2",
462 0.1f,
463 (s,cf,p,v) => { LinkConstraintERP = cf.GetFloat(p, v); },
464 (s) => { return LinkConstraintERP; },
465 (s,p,l,v) => { LinkConstraintERP = v; } ),
466 new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)",
467 40,
468 (s,cf,p,v) => { LinkConstraintSolverIterations = cf.GetFloat(p, v); },
469 (s) => { return LinkConstraintSolverIterations; },
470 (s,p,l,v) => { LinkConstraintSolverIterations = v; } ),
471
472 new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)",
473 0f,
474 (s,cf,p,v) => { s.UnmanagedParams[0].physicsLoggingFrames = cf.GetInt(p, (int)v); },
475 (s) => { return (float)s.UnmanagedParams[0].physicsLoggingFrames; },
476 (s,p,l,v) => { s.UnmanagedParams[0].physicsLoggingFrames = (int)v; } ),
477 };
478
479 // Convert a boolean to our numeric true and false values
480 public static float NumericBool(bool b)
481 {
482 return (b ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse);
483 }
484
485 // Convert numeric true and false values to a boolean
486 public static bool BoolNumeric(float b)
487 {
488 return (b == ConfigurationParameters.numericTrue ? true : false);
489 }
490
491 // Search through the parameter definitions and return the matching
492 // ParameterDefn structure.
493 // Case does not matter as names are compared after converting to lower case.
494 // Returns 'false' if the parameter is not found.
495 internal static bool TryGetParameter(string paramName, out ParameterDefn defn)
496 {
497 bool ret = false;
498 ParameterDefn foundDefn = new ParameterDefn();
499 string pName = paramName.ToLower();
500
501 foreach (ParameterDefn parm in ParameterDefinitions)
502 {
503 if (pName == parm.name.ToLower())
504 {
505 foundDefn = parm;
506 ret = true;
507 break;
508 }
509 }
510 defn = foundDefn;
511 return ret;
512 }
513
514 // Pass through the settable parameters and set the default values
515 internal static void SetParameterDefaultValues(BSScene physicsScene)
516 {
517 foreach (ParameterDefn parm in ParameterDefinitions)
518 {
519 parm.setter(physicsScene, parm.name, PhysParameterEntry.APPLY_TO_NONE, parm.defaultValue);
520 }
521 }
522
523 // Get user set values out of the ini file.
524 internal static void SetParameterConfigurationValues(BSScene physicsScene, IConfig cfg)
525 {
526 foreach (ParameterDefn parm in ParameterDefinitions)
527 {
528 parm.userParam(physicsScene, cfg, parm.name, parm.defaultValue);
529 }
530 }
531
532 internal static PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1];
533
534 // This creates an array in the correct format for returning the list of
535 // parameters. This is used by the 'list' option of the 'physics' command.
536 internal static void BuildParameterTable()
537 {
538 if (SettableParameters.Length < ParameterDefinitions.Length)
539 {
540 List<PhysParameterEntry> entries = new List<PhysParameterEntry>();
541 for (int ii = 0; ii < ParameterDefinitions.Length; ii++)
542 {
543 ParameterDefn pd = ParameterDefinitions[ii];
544 entries.Add(new PhysParameterEntry(pd.name, pd.desc));
545 }
546
547 // make the list in alphabetical order for estetic reasons
548 entries.Sort(delegate(PhysParameterEntry ppe1, PhysParameterEntry ppe2)
549 {
550 return ppe1.name.CompareTo(ppe2.name);
551 });
552
553 SettableParameters = entries.ToArray();
554 }
555 }
556
557
558}
559}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSPhysObject.cs
new file mode 100644
index 0000000..4096ef8
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSPhysObject.cs
@@ -0,0 +1,345 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OMV = OpenMetaverse;
32using OpenSim.Framework;
33using OpenSim.Region.Physics.Manager;
34
35namespace OpenSim.Region.Physics.BulletSNPlugin
36{
37/*
38 * Class to wrap all objects.
39 * The rest of BulletSim doesn't need to keep checking for avatars or prims
40 * unless the difference is significant.
41 *
42 * Variables in the physicsl objects are in three forms:
43 * VariableName: used by the simulator and performs taint operations, etc
44 * RawVariableName: direct reference to the BulletSim storage for the variable value
45 * ForceVariableName: direct reference (store and fetch) to the value in the physics engine.
46 * The last two (and certainly the last one) should be referenced only in taint-time.
47 */
48
49/*
50 * As of 20121221, the following are the call sequences (going down) for different script physical functions:
51 * llApplyImpulse llApplyRotImpulse llSetTorque llSetForce
52 * SOP.ApplyImpulse SOP.ApplyAngularImpulse SOP.SetAngularImpulse SOP.SetForce
53 * SOG.ApplyImpulse SOG.ApplyAngularImpulse SOG.SetAngularImpulse
54 * PA.AddForce PA.AddAngularForce PA.Torque = v PA.Force = v
55 * BS.ApplyCentralForce BS.ApplyTorque
56 */
57
58public abstract class BSPhysObject : PhysicsActor
59{
60 protected BSPhysObject()
61 {
62 }
63 protected BSPhysObject(BSScene parentScene, uint localID, string name, string typeName)
64 {
65 PhysicsScene = parentScene;
66 LocalID = localID;
67 PhysObjectName = name;
68 TypeName = typeName;
69
70 Linkset = BSLinkset.Factory(PhysicsScene, this);
71 LastAssetBuildFailed = false;
72
73 // Default material type
74 Material = MaterialAttributes.Material.Wood;
75
76 CollisionCollection = new CollisionEventUpdate();
77 SubscribedEventsMs = 0;
78 CollidingStep = 0;
79 CollidingGroundStep = 0;
80 }
81
82 // Tell the object to clean up.
83 public virtual void Destroy()
84 {
85 UnRegisterAllPreStepActions();
86 }
87
88 public BSScene PhysicsScene { get; protected set; }
89 // public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor
90 public string PhysObjectName { get; protected set; }
91 public string TypeName { get; protected set; }
92
93 public BSLinkset Linkset { get; set; }
94 public BSLinksetInfo LinksetInfo { get; set; }
95
96 // Return the object mass without calculating it or having side effects
97 public abstract float RawMass { get; }
98 // Set the raw mass but also update physical mass properties (inertia, ...)
99 public abstract void UpdatePhysicalMassProperties(float mass);
100
101 // The last value calculated for the prim's inertia
102 public OMV.Vector3 Inertia { get; set; }
103
104 // Reference to the physical body (btCollisionObject) of this object
105 public BulletBody PhysBody;
106 // Reference to the physical shape (btCollisionShape) of this object
107 public BulletShape PhysShape;
108
109 // 'true' if the mesh's underlying asset failed to build.
110 // This will keep us from looping after the first time the build failed.
111 public bool LastAssetBuildFailed { get; set; }
112
113 // The objects base shape information. Null if not a prim type shape.
114 public PrimitiveBaseShape BaseShape { get; protected set; }
115 // Some types of objects have preferred physical representations.
116 // Returns SHAPE_UNKNOWN if there is no preference.
117 public virtual BSPhysicsShapeType PreferredPhysicalShape
118 {
119 get { return BSPhysicsShapeType.SHAPE_UNKNOWN; }
120 }
121
122 // When the physical properties are updated, an EntityProperty holds the update values.
123 // Keep the current and last EntityProperties to enable computation of differences
124 // between the current update and the previous values.
125 public EntityProperties CurrentEntityProperties { get; set; }
126 public EntityProperties LastEntityProperties { get; set; }
127
128 public virtual OMV.Vector3 Scale { get; set; }
129 public abstract bool IsSolid { get; }
130 public abstract bool IsStatic { get; }
131
132 // Materialness
133 public MaterialAttributes.Material Material { get; private set; }
134 public override void SetMaterial(int material)
135 {
136 Material = (MaterialAttributes.Material)material;
137 }
138
139 // Stop all physical motion.
140 public abstract void ZeroMotion(bool inTaintTime);
141 public abstract void ZeroAngularMotion(bool inTaintTime);
142
143 // Step the vehicle simulation for this object. A NOOP if the vehicle was not configured.
144 public virtual void StepVehicle(float timeStep) { }
145
146 // Update the physical location and motion of the object. Called with data from Bullet.
147 public abstract void UpdateProperties(EntityProperties entprop);
148
149 public abstract OMV.Vector3 RawPosition { get; set; }
150 public abstract OMV.Vector3 ForcePosition { get; set; }
151
152 public abstract OMV.Quaternion RawOrientation { get; set; }
153 public abstract OMV.Quaternion ForceOrientation { get; set; }
154
155 // The system is telling us the velocity it wants to move at.
156 // protected OMV.Vector3 m_targetVelocity; // use the definition in PhysicsActor
157 public override OMV.Vector3 TargetVelocity
158 {
159 get { return m_targetVelocity; }
160 set
161 {
162 m_targetVelocity = value;
163 Velocity = value;
164 }
165 }
166 public abstract OMV.Vector3 ForceVelocity { get; set; }
167
168 public abstract OMV.Vector3 ForceRotationalVelocity { get; set; }
169
170 public abstract float ForceBuoyancy { get; set; }
171
172 public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
173
174 #region Collisions
175
176 // Requested number of milliseconds between collision events. Zero means disabled.
177 protected int SubscribedEventsMs { get; set; }
178 // Given subscription, the time that a collision may be passed up
179 protected int NextCollisionOkTime { get; set; }
180 // The simulation step that last had a collision
181 protected long CollidingStep { get; set; }
182 // The simulation step that last had a collision with the ground
183 protected long CollidingGroundStep { get; set; }
184 // The collision flags we think are set in Bullet
185 protected CollisionFlags CurrentCollisionFlags { get; set; }
186
187 // The collisions that have been collected this tick
188 protected CollisionEventUpdate CollisionCollection;
189
190 // The simulation step is telling this object about a collision.
191 // Return 'true' if a collision was processed and should be sent up.
192 // Called at taint time from within the Step() function
193 public virtual bool Collide(uint collidingWith, BSPhysObject collidee,
194 OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
195 {
196 bool ret = false;
197
198 // The following lines make IsColliding() and IsCollidingGround() work
199 CollidingStep = PhysicsScene.SimulationStep;
200 if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID)
201 {
202 CollidingGroundStep = PhysicsScene.SimulationStep;
203 }
204
205 // prims in the same linkset cannot collide with each other
206 if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID))
207 {
208 return ret;
209 }
210
211 // if someone has subscribed for collision events....
212 if (SubscribedEvents()) {
213 CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
214 DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}",
215 LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth);
216
217 ret = true;
218 }
219 return ret;
220 }
221
222 // Send the collected collisions into the simulator.
223 // Called at taint time from within the Step() function thus no locking problems
224 // with CollisionCollection and ObjectsWithNoMoreCollisions.
225 // Return 'true' if there were some actual collisions passed up
226 public virtual bool SendCollisions()
227 {
228 bool ret = true;
229 // If the 'no collision' call, force it to happen right now so quick collision_end
230 bool force = (CollisionCollection.Count == 0);
231
232 // throttle the collisions to the number of milliseconds specified in the subscription
233 if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime))
234 {
235 NextCollisionOkTime = PhysicsScene.SimulationNowTime + SubscribedEventsMs;
236
237 // We are called if we previously had collisions. If there are no collisions
238 // this time, send up one last empty event so OpenSim can sense collision end.
239 if (CollisionCollection.Count == 0)
240 {
241 // If I have no collisions this time, remove me from the list of objects with collisions.
242 ret = false;
243 }
244
245 // DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
246 base.SendCollisionUpdate(CollisionCollection);
247
248 // The CollisionCollection instance is passed around in the simulator.
249 // Make sure we don't have a handle to that one and that a new one is used for next time.
250 // This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here,
251 // a race condition is created for the other users of this instance.
252 CollisionCollection = new CollisionEventUpdate();
253 }
254 return ret;
255 }
256
257 // Subscribe for collision events.
258 // Parameter is the millisecond rate the caller wishes collision events to occur.
259 public override void SubscribeEvents(int ms) {
260 // DetailLog("{0},{1}.SubscribeEvents,subscribing,ms={2}", LocalID, TypeName, ms);
261 SubscribedEventsMs = ms;
262 if (ms > 0)
263 {
264 // make sure first collision happens
265 NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs);
266
267 PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate()
268 {
269 if (PhysBody.HasPhysicalBody)
270 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
271 });
272 }
273 else
274 {
275 // Subscribing for zero or less is the same as unsubscribing
276 UnSubscribeEvents();
277 }
278 }
279 public override void UnSubscribeEvents() {
280 // DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName);
281 SubscribedEventsMs = 0;
282 PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate()
283 {
284 // Make sure there is a body there because sometimes destruction happens in an un-ideal order.
285 if (PhysBody.HasPhysicalBody)
286 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
287 });
288 }
289 // Return 'true' if the simulator wants collision events
290 public override bool SubscribedEvents() {
291 return (SubscribedEventsMs > 0);
292 }
293
294 #endregion // Collisions
295
296 #region Per Simulation Step actions
297 // There are some actions that must be performed for a physical object before each simulation step.
298 // These actions are optional so, rather than scanning all the physical objects and asking them
299 // if they have anything to do, a physical object registers for an event call before the step is performed.
300 // This bookkeeping makes it easy to add, remove and clean up after all these registrations.
301 private Dictionary<string, BSScene.PreStepAction> RegisteredActions = new Dictionary<string, BSScene.PreStepAction>();
302 protected void RegisterPreStepAction(string op, uint id, BSScene.PreStepAction actn)
303 {
304 string identifier = op + "-" + id.ToString();
305 RegisteredActions[identifier] = actn;
306 PhysicsScene.BeforeStep += actn;
307 DetailLog("{0},BSPhysObject.RegisterPreStepAction,id={1}", LocalID, identifier);
308 }
309
310 // Unregister a pre step action. Safe to call if the action has not been registered.
311 protected void UnRegisterPreStepAction(string op, uint id)
312 {
313 string identifier = op + "-" + id.ToString();
314 bool removed = false;
315 if (RegisteredActions.ContainsKey(identifier))
316 {
317 PhysicsScene.BeforeStep -= RegisteredActions[identifier];
318 RegisteredActions.Remove(identifier);
319 removed = true;
320 }
321 DetailLog("{0},BSPhysObject.UnRegisterPreStepAction,id={1},removed={2}", LocalID, identifier, removed);
322 }
323
324 protected void UnRegisterAllPreStepActions()
325 {
326 foreach (KeyValuePair<string, BSScene.PreStepAction> kvp in RegisteredActions)
327 {
328 PhysicsScene.BeforeStep -= kvp.Value;
329 }
330 RegisteredActions.Clear();
331 DetailLog("{0},BSPhysObject.UnRegisterAllPreStepActions,", LocalID);
332 }
333
334
335 #endregion // Per Simulation Step actions
336
337 // High performance detailed logging routine used by the physical objects.
338 protected void DetailLog(string msg, params Object[] args)
339 {
340 if (PhysicsScene.PhysicsLogging.Enabled)
341 PhysicsScene.DetailLog(msg, args);
342 }
343
344}
345}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSPlugin.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSPlugin.cs
new file mode 100644
index 0000000..75963ee
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSPlugin.cs
@@ -0,0 +1,81 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using OpenSim.Framework;
30using OpenSim.Region.Physics.Manager;
31using OpenMetaverse;
32
33namespace OpenSim.Region.Physics.BulletSNPlugin
34{
35 /// <summary>
36 /// Entry for a port of Bullet (http://bulletphysics.org/) to OpenSim.
37 /// This module interfaces to an unmanaged C++ library which makes the
38 /// actual calls into the Bullet physics engine.
39 /// The unmanaged library is found in opensim-libs::trunk/unmanaged/BulletSim/.
40 /// The unmanaged library is compiled and linked statically with Bullet
41 /// to create BulletSim.dll and libBulletSim.so (for both 32 and 64 bit).
42 /// </summary>
43public class BSPlugin : IPhysicsPlugin
44{
45 //private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
46
47 private BSScene _mScene;
48
49 public BSPlugin()
50 {
51 }
52
53 public bool Init()
54 {
55 return true;
56 }
57
58 public PhysicsScene GetScene(String sceneIdentifier)
59 {
60 if (_mScene == null)
61 {
62
63 // If not Windows, loading is performed by the
64 // Mono loader as specified in
65 // "bin/Physics/OpenSim.Region.Physics.BulletSNPlugin.dll.config".
66
67 _mScene = new BSScene(sceneIdentifier);
68 }
69 return (_mScene);
70 }
71
72 public string GetName()
73 {
74 return ("BulletSimN");
75 }
76
77 public void Dispose()
78 {
79 }
80}
81}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs
new file mode 100644
index 0000000..a889c24
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs
@@ -0,0 +1,1467 @@
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 copyrightD
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
28using System;
29using System.Reflection;
30using System.Collections.Generic;
31using System.Xml;
32using log4net;
33using OMV = OpenMetaverse;
34using OpenSim.Framework;
35using OpenSim.Region.Physics.Manager;
36using OpenSim.Region.Physics.ConvexDecompositionDotNet;
37
38namespace OpenSim.Region.Physics.BulletSNPlugin
39{
40
41 [Serializable]
42public sealed class BSPrim : BSPhysObject
43{
44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
45 private static readonly string LogHeader = "[BULLETS PRIM]";
46
47 // _size is what the user passed. Scale is what we pass to the physics engine with the mesh.
48 private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user
49
50 private bool _grabbed;
51 private bool _isSelected;
52 private bool _isVolumeDetect;
53 private OMV.Vector3 _position;
54 private float _mass; // the mass of this object
55 private float _density;
56 private OMV.Vector3 _force;
57 private OMV.Vector3 _velocity;
58 private OMV.Vector3 _torque;
59 private float _collisionScore;
60 private OMV.Vector3 _acceleration;
61 private OMV.Quaternion _orientation;
62 private int _physicsActorType;
63 private bool _isPhysical;
64 private bool _flying;
65 private float _friction;
66 private float _restitution;
67 private bool _setAlwaysRun;
68 private bool _throttleUpdates;
69 private bool _isColliding;
70 private bool _collidingGround;
71 private bool _collidingObj;
72 private bool _floatOnWater;
73 private OMV.Vector3 _rotationalVelocity;
74 private bool _kinematic;
75 private float _buoyancy;
76
77 private BSDynamics _vehicle;
78
79 private OMV.Vector3 _PIDTarget;
80 private bool _usePID;
81 private float _PIDTau;
82 private bool _useHoverPID;
83 private float _PIDHoverHeight;
84 private PIDHoverType _PIDHoverType;
85 private float _PIDHoverTao;
86
87 public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size,
88 OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical)
89 : base(parent_scene, localID, primName, "BSPrim")
90 {
91 // m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID);
92 _physicsActorType = (int)ActorTypes.Prim;
93 _position = pos;
94 _size = size;
95 Scale = size; // prims are the size the user wants them to be (different for BSCharactes).
96 _orientation = rotation;
97 _buoyancy = 1f;
98 _velocity = OMV.Vector3.Zero;
99 _rotationalVelocity = OMV.Vector3.Zero;
100 BaseShape = pbs;
101 _isPhysical = pisPhysical;
102 _isVolumeDetect = false;
103
104 // Someday set default attributes based on the material but, for now, we don't know the prim material yet.
105 // MaterialAttributes primMat = BSMaterials.GetAttributes(Material, pisPhysical);
106 _density = PhysicsScene.Params.defaultDensity;
107 _friction = PhysicsScene.Params.defaultFriction;
108 _restitution = PhysicsScene.Params.defaultRestitution;
109
110 _vehicle = new BSDynamics(PhysicsScene, this); // add vehicleness
111
112 _mass = CalculateMass();
113
114 // No body or shape yet
115 PhysBody = new BulletBody(LocalID);
116 PhysShape = new BulletShape();
117
118 DetailLog("{0},BSPrim.constructor,call", LocalID);
119 // do the actual object creation at taint time
120 PhysicsScene.TaintedObject("BSPrim.create", delegate()
121 {
122 CreateGeomAndObject(true);
123
124 CurrentCollisionFlags = BulletSimAPI.GetCollisionFlags2(PhysBody.ptr);
125 });
126 }
127
128 // called when this prim is being destroyed and we should free all the resources
129 public override void Destroy()
130 {
131 // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID);
132 base.Destroy();
133
134 // Undo any links between me and any other object
135 BSPhysObject parentBefore = Linkset.LinksetRoot;
136 int childrenBefore = Linkset.NumberOfChildren;
137
138 Linkset = Linkset.RemoveMeFromLinkset(this);
139
140 DetailLog("{0},BSPrim.Destroy,call,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}",
141 LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
142
143 // Undo any vehicle properties
144 this.VehicleType = (int)Vehicle.TYPE_NONE;
145
146 PhysicsScene.TaintedObject("BSPrim.destroy", delegate()
147 {
148 DetailLog("{0},BSPrim.Destroy,taint,", LocalID);
149 // If there are physical body and shape, release my use of same.
150 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
151 PhysBody.Clear();
152 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
153 PhysShape.Clear();
154 });
155 }
156
157 // No one uses this property.
158 public override bool Stopped {
159 get { return false; }
160 }
161 public override OMV.Vector3 Size {
162 get { return _size; }
163 set {
164 // We presume the scale and size are the same. If scale must be changed for
165 // the physical shape, that is done when the geometry is built.
166 _size = value;
167 Scale = _size;
168 ForceBodyShapeRebuild(false);
169 }
170 }
171
172 public override PrimitiveBaseShape Shape {
173 set {
174 BaseShape = value;
175 ForceBodyShapeRebuild(false);
176 }
177 }
178 // Whatever the linkset wants is what I want.
179 public override BSPhysicsShapeType PreferredPhysicalShape
180 { get { return Linkset.PreferredPhysicalShape(this); } }
181
182 public override bool ForceBodyShapeRebuild(bool inTaintTime)
183 {
184 LastAssetBuildFailed = false;
185 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ForceBodyShapeRebuild", delegate()
186 {
187 _mass = CalculateMass(); // changing the shape changes the mass
188 CreateGeomAndObject(true);
189 });
190 return true;
191 }
192 public override bool Grabbed {
193 set { _grabbed = value;
194 }
195 }
196 public override bool Selected {
197 set
198 {
199 if (value != _isSelected)
200 {
201 _isSelected = value;
202 PhysicsScene.TaintedObject("BSPrim.setSelected", delegate()
203 {
204 DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected);
205 SetObjectDynamic(false);
206 });
207 }
208 }
209 }
210 public override void CrossingFailure() { return; }
211
212 // link me to the specified parent
213 public override void link(PhysicsActor obj) {
214 BSPrim parent = obj as BSPrim;
215 if (parent != null)
216 {
217 BSPhysObject parentBefore = Linkset.LinksetRoot;
218 int childrenBefore = Linkset.NumberOfChildren;
219
220 Linkset = parent.Linkset.AddMeToLinkset(this);
221
222 DetailLog("{0},BSPrim.link,call,parentBefore={1}, childrenBefore=={2}, parentAfter={3}, childrenAfter={4}",
223 LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
224 }
225 return;
226 }
227
228 // delink me from my linkset
229 public override void delink() {
230 // TODO: decide if this parent checking needs to happen at taint time
231 // Race condition here: if link() and delink() in same simulation tick, the delink will not happen
232
233 BSPhysObject parentBefore = Linkset.LinksetRoot;
234 int childrenBefore = Linkset.NumberOfChildren;
235
236 Linkset = Linkset.RemoveMeFromLinkset(this);
237
238 DetailLog("{0},BSPrim.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ",
239 LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
240 return;
241 }
242
243 // Set motion values to zero.
244 // Do it to the properties so the values get set in the physics engine.
245 // Push the setting of the values to the viewer.
246 // Called at taint time!
247 public override void ZeroMotion(bool inTaintTime)
248 {
249 _velocity = OMV.Vector3.Zero;
250 _acceleration = OMV.Vector3.Zero;
251 _rotationalVelocity = OMV.Vector3.Zero;
252
253 // Zero some other properties in the physics engine
254 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
255 {
256 if (PhysBody.HasPhysicalBody)
257 BulletSimAPI.ClearAllForces2(PhysBody.ptr);
258 });
259 }
260 public override void ZeroAngularMotion(bool inTaintTime)
261 {
262 _rotationalVelocity = OMV.Vector3.Zero;
263 // Zero some other properties in the physics engine
264 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
265 {
266 // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity);
267 if (PhysBody.HasPhysicalBody)
268 {
269 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
270 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
271 }
272 });
273 }
274
275 public override void LockAngularMotion(OMV.Vector3 axis)
276 {
277 DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis);
278 return;
279 }
280
281 public override OMV.Vector3 RawPosition
282 {
283 get { return _position; }
284 set { _position = value; }
285 }
286 public override OMV.Vector3 Position {
287 get {
288 /* NOTE: this refetch is not necessary. The simulator knows about linkset children
289 * and does not fetch this position info for children. Thus this is commented out.
290 // child prims move around based on their parent. Need to get the latest location
291 if (!Linkset.IsRoot(this))
292 _position = Linkset.PositionGet(this);
293 */
294
295 // don't do the GetObjectPosition for root elements because this function is called a zillion times.
296 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
297 return _position;
298 }
299 set {
300 // If the position must be forced into the physics engine, use ForcePosition.
301 // All positions are given in world positions.
302 if (_position == value)
303 {
304 DetailLog("{0},BSPrim.setPosition,taint,positionNotChanging,pos={1},orient={2}", LocalID, _position, _orientation);
305 return;
306 }
307 _position = value;
308 PositionSanityCheck(false);
309
310 // A linkset might need to know if a component information changed.
311 Linkset.UpdateProperties(this, false);
312
313 PhysicsScene.TaintedObject("BSPrim.setPosition", delegate()
314 {
315 DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
316 ForcePosition = _position;
317 });
318 }
319 }
320 public override OMV.Vector3 ForcePosition {
321 get {
322 _position = BulletSimAPI.GetPosition2(PhysBody.ptr);
323 return _position;
324 }
325 set {
326 _position = value;
327 if (PhysBody.HasPhysicalBody)
328 {
329 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
330 ActivateIfPhysical(false);
331 }
332 }
333 }
334
335 // Check that the current position is sane and, if not, modify the position to make it so.
336 // Check for being below terrain and being out of bounds.
337 // Returns 'true' of the position was made sane by some action.
338 private bool PositionSanityCheck(bool inTaintTime)
339 {
340 bool ret = false;
341
342 if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position))
343 {
344 // The physical object is out of the known/simulated area.
345 // Upper levels of code will handle the transition to other areas so, for
346 // the time, we just ignore the position.
347 return ret;
348 }
349
350 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
351 OMV.Vector3 upForce = OMV.Vector3.Zero;
352 if (RawPosition.Z < terrainHeight)
353 {
354 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
355 float targetHeight = terrainHeight + (Size.Z / 2f);
356 // Upforce proportional to the distance away from the terrain. Correct the error in 1 sec.
357 upForce.Z = (terrainHeight - RawPosition.Z) * 1f;
358 ret = true;
359 }
360
361 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
362 {
363 float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
364 // TODO: a floating motor so object will bob in the water
365 if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f)
366 {
367 // Upforce proportional to the distance away from the water. Correct the error in 1 sec.
368 upForce.Z = (waterHeight - RawPosition.Z) * 1f;
369 ret = true;
370 }
371 }
372
373 // The above code computes a force to apply to correct any out-of-bounds problems. Apply same.
374 // TODO: This should be intergrated with a geneal physics action mechanism.
375 // TODO: This should be moderated with PID'ness.
376 if (ret)
377 {
378 // Apply upforce and overcome gravity.
379 OMV.Vector3 correctionForce = upForce - PhysicsScene.DefaultGravity;
380 DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, _position, upForce, correctionForce);
381 AddForce(correctionForce, false, inTaintTime);
382 }
383 return ret;
384 }
385
386 // Return the effective mass of the object.
387 // If there are multiple items in the linkset, add them together for the root
388 public override float Mass
389 {
390 get
391 {
392 return Linkset.LinksetMass;
393 // return _mass;
394 }
395 }
396
397 // used when we only want this prim's mass and not the linkset thing
398 public override float RawMass {
399 get { return _mass; }
400 }
401 // Set the physical mass to the passed mass.
402 // Note that this does not change _mass!
403 public override void UpdatePhysicalMassProperties(float physMass)
404 {
405 if (IsStatic)
406 {
407 Inertia = OMV.Vector3.Zero;
408 BulletSimAPI.SetMassProps2(PhysBody.ptr, 0f, Inertia);
409 BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr);
410 }
411 else
412 {
413 Inertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass);
414 BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, Inertia);
415 BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr);
416 // center of mass is at the zero of the object
417 // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(PhysBody.ptr, ForcePosition, ForceOrientation);
418 DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2}", LocalID, physMass, Inertia);
419 }
420 }
421
422 // Is this used?
423 public override OMV.Vector3 CenterOfMass
424 {
425 get { return Linkset.CenterOfMass; }
426 }
427
428 // Is this used?
429 public override OMV.Vector3 GeometricCenter
430 {
431 get { return Linkset.GeometricCenter; }
432 }
433
434 public override OMV.Vector3 Force {
435 get { return _force; }
436 set {
437 _force = value;
438 if (_force != OMV.Vector3.Zero)
439 {
440 // If the force is non-zero, it must be reapplied each tick because
441 // Bullet clears the forces applied last frame.
442 RegisterPreStepAction("BSPrim.setForce", LocalID,
443 delegate(float timeStep)
444 {
445 DetailLog("{0},BSPrim.setForce,preStep,force={1}", LocalID, _force);
446 if (PhysBody.HasPhysicalBody)
447 {
448 BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, _force);
449 ActivateIfPhysical(false);
450 }
451 }
452 );
453 }
454 else
455 {
456 UnRegisterPreStepAction("BSPrim.setForce", LocalID);
457 }
458 }
459 }
460
461 public override int VehicleType {
462 get {
463 return (int)_vehicle.Type; // if we are a vehicle, return that type
464 }
465 set {
466 Vehicle type = (Vehicle)value;
467
468 PhysicsScene.TaintedObject("setVehicleType", delegate()
469 {
470 // Done at taint time so we're sure the physics engine is not using the variables
471 // Vehicle code changes the parameters for this vehicle type.
472 _vehicle.ProcessTypeChange(type);
473 ActivateIfPhysical(false);
474
475 // If an active vehicle, register the vehicle code to be called before each step
476 if (_vehicle.Type == Vehicle.TYPE_NONE)
477 UnRegisterPreStepAction("BSPrim.Vehicle", LocalID);
478 else
479 RegisterPreStepAction("BSPrim.Vehicle", LocalID, _vehicle.Step);
480 });
481 }
482 }
483 public override void VehicleFloatParam(int param, float value)
484 {
485 PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate()
486 {
487 _vehicle.ProcessFloatVehicleParam((Vehicle)param, value);
488 ActivateIfPhysical(false);
489 });
490 }
491 public override void VehicleVectorParam(int param, OMV.Vector3 value)
492 {
493 PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate()
494 {
495 _vehicle.ProcessVectorVehicleParam((Vehicle)param, value);
496 ActivateIfPhysical(false);
497 });
498 }
499 public override void VehicleRotationParam(int param, OMV.Quaternion rotation)
500 {
501 PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate()
502 {
503 _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation);
504 ActivateIfPhysical(false);
505 });
506 }
507 public override void VehicleFlags(int param, bool remove)
508 {
509 PhysicsScene.TaintedObject("BSPrim.VehicleFlags", delegate()
510 {
511 _vehicle.ProcessVehicleFlags(param, remove);
512 });
513 }
514
515 // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more
516 public override void SetVolumeDetect(int param) {
517 bool newValue = (param != 0);
518 if (_isVolumeDetect != newValue)
519 {
520 _isVolumeDetect = newValue;
521 PhysicsScene.TaintedObject("BSPrim.SetVolumeDetect", delegate()
522 {
523 // DetailLog("{0},setVolumeDetect,taint,volDetect={1}", LocalID, _isVolumeDetect);
524 SetObjectDynamic(true);
525 });
526 }
527 return;
528 }
529 public override OMV.Vector3 Velocity {
530 get { return _velocity; }
531 set {
532 _velocity = value;
533 PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate()
534 {
535 // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity);
536 ForceVelocity = _velocity;
537 });
538 }
539 }
540 public override OMV.Vector3 ForceVelocity {
541 get { return _velocity; }
542 set {
543 PhysicsScene.AssertInTaintTime("BSPrim.ForceVelocity");
544
545 _velocity = value;
546 if (PhysBody.HasPhysicalBody)
547 {
548 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
549 ActivateIfPhysical(false);
550 }
551 }
552 }
553 public override OMV.Vector3 Torque {
554 get { return _torque; }
555 set {
556 _torque = value;
557 if (_torque != OMV.Vector3.Zero)
558 {
559 // If the torque is non-zero, it must be reapplied each tick because
560 // Bullet clears the forces applied last frame.
561 RegisterPreStepAction("BSPrim.setTorque", LocalID,
562 delegate(float timeStep)
563 {
564 if (PhysBody.HasPhysicalBody)
565 AddAngularForce(_torque, false, true);
566 }
567 );
568 }
569 else
570 {
571 UnRegisterPreStepAction("BSPrim.setTorque", LocalID);
572 }
573 // DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque);
574 }
575 }
576 public override float CollisionScore {
577 get { return _collisionScore; }
578 set { _collisionScore = value;
579 }
580 }
581 public override OMV.Vector3 Acceleration {
582 get { return _acceleration; }
583 set { _acceleration = value; }
584 }
585 public override OMV.Quaternion RawOrientation
586 {
587 get { return _orientation; }
588 set { _orientation = value; }
589 }
590 public override OMV.Quaternion Orientation {
591 get {
592 /* NOTE: this refetch is not necessary. The simulator knows about linkset children
593 * and does not fetch this position info for children. Thus this is commented out.
594 // Children move around because tied to parent. Get a fresh value.
595 if (!Linkset.IsRoot(this))
596 {
597 _orientation = Linkset.OrientationGet(this);
598 }
599 */
600 return _orientation;
601 }
602 set {
603 if (_orientation == value)
604 return;
605 _orientation = value;
606
607 // A linkset might need to know if a component information changed.
608 Linkset.UpdateProperties(this, false);
609
610 PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate()
611 {
612 if (PhysBody.HasPhysicalBody)
613 {
614 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
615 // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation);
616 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
617 }
618 });
619 }
620 }
621 // Go directly to Bullet to get/set the value.
622 public override OMV.Quaternion ForceOrientation
623 {
624 get
625 {
626 _orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr);
627 return _orientation;
628 }
629 set
630 {
631 _orientation = value;
632 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
633 }
634 }
635 public override int PhysicsActorType {
636 get { return _physicsActorType; }
637 set { _physicsActorType = value; }
638 }
639 public override bool IsPhysical {
640 get { return _isPhysical; }
641 set {
642 if (_isPhysical != value)
643 {
644 _isPhysical = value;
645 PhysicsScene.TaintedObject("BSPrim.setIsPhysical", delegate()
646 {
647 // DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical);
648 SetObjectDynamic(true);
649 // whether phys-to-static or static-to-phys, the object is not moving.
650 ZeroMotion(true);
651 });
652 }
653 }
654 }
655
656 // An object is static (does not move) if selected or not physical
657 public override bool IsStatic
658 {
659 get { return _isSelected || !IsPhysical; }
660 }
661
662 // An object is solid if it's not phantom and if it's not doing VolumeDetect
663 public override bool IsSolid
664 {
665 get { return !IsPhantom && !_isVolumeDetect; }
666 }
667
668 // Make gravity work if the object is physical and not selected
669 // Called at taint-time!!
670 private void SetObjectDynamic(bool forceRebuild)
671 {
672 // Recreate the physical object if necessary
673 CreateGeomAndObject(forceRebuild);
674 }
675
676 // Convert the simulator's physical properties into settings on BulletSim objects.
677 // There are four flags we're interested in:
678 // IsStatic: Object does not move, otherwise the object has mass and moves
679 // isSolid: other objects bounce off of this object
680 // isVolumeDetect: other objects pass through but can generate collisions
681 // collisionEvents: whether this object returns collision events
682 private void UpdatePhysicalParameters()
683 {
684 // DetailLog("{0},BSPrim.UpdatePhysicalParameters,entry,body={1},shape={2}", LocalID, BSBody, BSShape);
685
686 // Mangling all the physical properties requires the object not be in the physical world.
687 // This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found).
688 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
689
690 // Set up the object physicalness (does gravity and collisions move this object)
691 MakeDynamic(IsStatic);
692
693 // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters)
694 _vehicle.Refresh();
695
696 // Arrange for collision events if the simulator wants them
697 EnableCollisions(SubscribedEvents());
698
699 // Make solid or not (do things bounce off or pass through this object).
700 MakeSolid(IsSolid);
701
702 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr, _position, _orientation);
703
704 // Rebuild its shape
705 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
706
707 // Collision filter can be set only when the object is in the world
708 PhysBody.ApplyCollisionMask();
709
710 // Recompute any linkset parameters.
711 // When going from non-physical to physical, this re-enables the constraints that
712 // had been automatically disabled when the mass was set to zero.
713 // For compound based linksets, this enables and disables interactions of the children.
714 Linkset.Refresh(this);
715
716 DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}",
717 LocalID, IsStatic, IsSolid, _mass, SubscribedEvents(), CurrentCollisionFlags, PhysBody, PhysShape);
718 }
719
720 // "Making dynamic" means changing to and from static.
721 // When static, gravity does not effect the object and it is fixed in space.
722 // When dynamic, the object can fall and be pushed by others.
723 // This is independent of its 'solidness' which controls what passes through
724 // this object and what interacts with it.
725 private void MakeDynamic(bool makeStatic)
726 {
727 if (makeStatic)
728 {
729 // Become a Bullet 'static' object type
730 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
731 // Stop all movement
732 ZeroMotion(true);
733
734 // Set various physical properties so other object interact properly
735 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false);
736 BulletSimAPI.SetFriction2(PhysBody.ptr, matAttrib.friction);
737 BulletSimAPI.SetRestitution2(PhysBody.ptr, matAttrib.restitution);
738
739 // Mass is zero which disables a bunch of physics stuff in Bullet
740 UpdatePhysicalMassProperties(0f);
741 // Set collision detection parameters
742 if (BSParam.CcdMotionThreshold > 0f)
743 {
744 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold);
745 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius);
746 }
747
748 // The activation state is 'disabled' so Bullet will not try to act on it.
749 // BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
750 // Start it out sleeping and physical actions could wake it up.
751 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ISLAND_SLEEPING);
752
753 // This collides like a static object
754 PhysBody.collisionType = CollisionType.Static;
755
756 // There can be special things needed for implementing linksets
757 Linkset.MakeStatic(this);
758 }
759 else
760 {
761 // Not a Bullet static object
762 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
763
764 // Set various physical properties so other object interact properly
765 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, true);
766 BulletSimAPI.SetFriction2(PhysBody.ptr, matAttrib.friction);
767 BulletSimAPI.SetRestitution2(PhysBody.ptr, matAttrib.restitution);
768
769 // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382
770 // Since this can be called multiple times, only zero forces when becoming physical
771 // BulletSimAPI.ClearAllForces2(BSBody.ptr);
772
773 // For good measure, make sure the transform is set through to the motion state
774 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
775
776 // Center of mass is at the center of the object
777 // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation);
778
779 // A dynamic object has mass
780 UpdatePhysicalMassProperties(RawMass);
781
782 // Set collision detection parameters
783 if (BSParam.CcdMotionThreshold > 0f)
784 {
785 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold);
786 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius);
787 }
788
789 // Various values for simulation limits
790 BulletSimAPI.SetDamping2(PhysBody.ptr, BSParam.LinearDamping, BSParam.AngularDamping);
791 BulletSimAPI.SetDeactivationTime2(PhysBody.ptr, BSParam.DeactivationTime);
792 BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold);
793 BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, BSParam.ContactProcessingThreshold);
794
795 // This collides like an object.
796 PhysBody.collisionType = CollisionType.Dynamic;
797
798 // Force activation of the object so Bullet will act on it.
799 // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects.
800 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ACTIVE_TAG);
801
802 // There might be special things needed for implementing linksets.
803 Linkset.MakeDynamic(this);
804 }
805 }
806
807 // "Making solid" means that other object will not pass through this object.
808 // To make transparent, we create a Bullet ghost object.
809 // Note: This expects to be called from the UpdatePhysicalParameters() routine as
810 // the functions after this one set up the state of a possibly newly created collision body.
811 private void MakeSolid(bool makeSolid)
812 {
813 CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(PhysBody.ptr);
814 if (makeSolid)
815 {
816 // Verify the previous code created the correct shape for this type of thing.
817 if ((bodyType & CollisionObjectTypes.CO_RIGID_BODY) == 0)
818 {
819 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType);
820 }
821 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
822 }
823 else
824 {
825 if ((bodyType & CollisionObjectTypes.CO_GHOST_OBJECT) == 0)
826 {
827 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType);
828 }
829 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
830
831 // Change collision info from a static object to a ghosty collision object
832 PhysBody.collisionType = CollisionType.VolumeDetect;
833 }
834 }
835
836 // Enable physical actions. Bullet will keep sleeping non-moving physical objects so
837 // they need waking up when parameters are changed.
838 // Called in taint-time!!
839 private void ActivateIfPhysical(bool forceIt)
840 {
841 if (IsPhysical && PhysBody.HasPhysicalBody)
842 BulletSimAPI.Activate2(PhysBody.ptr, forceIt);
843 }
844
845 // Turn on or off the flag controlling whether collision events are returned to the simulator.
846 private void EnableCollisions(bool wantsCollisionEvents)
847 {
848 if (wantsCollisionEvents)
849 {
850 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
851 }
852 else
853 {
854 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
855 }
856 }
857
858 // prims don't fly
859 public override bool Flying {
860 get { return _flying; }
861 set {
862 _flying = value;
863 }
864 }
865 public override bool SetAlwaysRun {
866 get { return _setAlwaysRun; }
867 set { _setAlwaysRun = value; }
868 }
869 public override bool ThrottleUpdates {
870 get { return _throttleUpdates; }
871 set { _throttleUpdates = value; }
872 }
873 public override bool IsColliding {
874 get { return (CollidingStep == PhysicsScene.SimulationStep); }
875 set { _isColliding = value; }
876 }
877 public override bool CollidingGround {
878 get { return (CollidingGroundStep == PhysicsScene.SimulationStep); }
879 set { _collidingGround = value; }
880 }
881 public override bool CollidingObj {
882 get { return _collidingObj; }
883 set { _collidingObj = value; }
884 }
885 public bool IsPhantom {
886 get {
887 // SceneObjectPart removes phantom objects from the physics scene
888 // so, although we could implement touching and such, we never
889 // are invoked as a phantom object
890 return false;
891 }
892 }
893 public override bool FloatOnWater {
894 set {
895 _floatOnWater = value;
896 PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate()
897 {
898 if (_floatOnWater)
899 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
900 else
901 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
902 });
903 }
904 }
905 public override OMV.Vector3 RotationalVelocity {
906 get {
907 return _rotationalVelocity;
908 }
909 set {
910 _rotationalVelocity = value;
911 // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity);
912 PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
913 {
914 DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
915 ForceRotationalVelocity = _rotationalVelocity;
916 });
917 }
918 }
919 public override OMV.Vector3 ForceRotationalVelocity {
920 get {
921 return _rotationalVelocity;
922 }
923 set {
924 _rotationalVelocity = value;
925 if (PhysBody.HasPhysicalBody)
926 {
927 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
928 ActivateIfPhysical(false);
929 }
930 }
931 }
932 public override bool Kinematic {
933 get { return _kinematic; }
934 set { _kinematic = value;
935 // m_log.DebugFormat("{0}: Kinematic={1}", LogHeader, _kinematic);
936 }
937 }
938 public override float Buoyancy {
939 get { return _buoyancy; }
940 set {
941 _buoyancy = value;
942 PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate()
943 {
944 ForceBuoyancy = _buoyancy;
945 });
946 }
947 }
948 public override float ForceBuoyancy {
949 get { return _buoyancy; }
950 set {
951 _buoyancy = value;
952 // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
953 // Buoyancy is faked by changing the gravity applied to the object
954 if (PhysBody.HasPhysicalBody)
955 {
956 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
957 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
958 ActivateIfPhysical(false);
959 }
960 }
961 }
962
963 // Used for MoveTo
964 public override OMV.Vector3 PIDTarget {
965 set { _PIDTarget = value; }
966 }
967 public override bool PIDActive {
968 set { _usePID = value; }
969 }
970 public override float PIDTau {
971 set { _PIDTau = value; }
972 }
973
974 // Used for llSetHoverHeight and maybe vehicle height
975 // Hover Height will override MoveTo target's Z
976 public override bool PIDHoverActive {
977 set { _useHoverPID = value; }
978 }
979 public override float PIDHoverHeight {
980 set { _PIDHoverHeight = value; }
981 }
982 public override PIDHoverType PIDHoverType {
983 set { _PIDHoverType = value; }
984 }
985 public override float PIDHoverTau {
986 set { _PIDHoverTao = value; }
987 }
988
989 // For RotLookAt
990 public override OMV.Quaternion APIDTarget { set { return; } }
991 public override bool APIDActive { set { return; } }
992 public override float APIDStrength { set { return; } }
993 public override float APIDDamping { set { return; } }
994
995 public override void AddForce(OMV.Vector3 force, bool pushforce) {
996 AddForce(force, pushforce, false);
997 }
998 // Applying a force just adds this to the total force on the object.
999 // This added force will only last the next simulation tick.
1000 public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
1001 // for an object, doesn't matter if force is a pushforce or not
1002 if (force.IsFinite())
1003 {
1004 OMV.Vector3 addForce = force;
1005 DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce);
1006 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate()
1007 {
1008 // Bullet adds this central force to the total force for this tick
1009 DetailLog("{0},BSPrim.addForce,taint,force={1}", LocalID, addForce);
1010 if (PhysBody.HasPhysicalBody)
1011 {
1012 BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, addForce);
1013 ActivateIfPhysical(false);
1014 }
1015 });
1016 }
1017 else
1018 {
1019 m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID);
1020 return;
1021 }
1022 }
1023
1024 public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
1025 AddAngularForce(force, pushforce, false);
1026 }
1027 public void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime)
1028 {
1029 if (force.IsFinite())
1030 {
1031 OMV.Vector3 angForce = force;
1032 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate()
1033 {
1034 if (PhysBody.HasPhysicalBody)
1035 {
1036 BulletSimAPI.ApplyTorque2(PhysBody.ptr, angForce);
1037 ActivateIfPhysical(false);
1038 }
1039 });
1040 }
1041 else
1042 {
1043 m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID);
1044 return;
1045 }
1046 }
1047
1048 // A torque impulse.
1049 // ApplyTorqueImpulse adds torque directly to the angularVelocity.
1050 // AddAngularForce accumulates the force and applied it to the angular velocity all at once.
1051 // Computed as: angularVelocity += impulse * inertia;
1052 public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime)
1053 {
1054 OMV.Vector3 applyImpulse = impulse;
1055 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate()
1056 {
1057 if (PhysBody.HasPhysicalBody)
1058 {
1059 BulletSimAPI.ApplyTorqueImpulse2(PhysBody.ptr, applyImpulse);
1060 ActivateIfPhysical(false);
1061 }
1062 });
1063 }
1064
1065 public override void SetMomentum(OMV.Vector3 momentum) {
1066 // DetailLog("{0},BSPrim.SetMomentum,call,mom={1}", LocalID, momentum);
1067 }
1068 #region Mass Calculation
1069
1070 private float CalculateMass()
1071 {
1072 float volume = _size.X * _size.Y * _size.Z; // default
1073 float tmp;
1074
1075 float returnMass = 0;
1076 float hollowAmount = (float)BaseShape.ProfileHollow * 2.0e-5f;
1077 float hollowVolume = hollowAmount * hollowAmount;
1078
1079 switch (BaseShape.ProfileShape)
1080 {
1081 case ProfileShape.Square:
1082 // default box
1083
1084 if (BaseShape.PathCurve == (byte)Extrusion.Straight)
1085 {
1086 if (hollowAmount > 0.0)
1087 {
1088 switch (BaseShape.HollowShape)
1089 {
1090 case HollowShape.Square:
1091 case HollowShape.Same:
1092 break;
1093
1094 case HollowShape.Circle:
1095
1096 hollowVolume *= 0.78539816339f;
1097 break;
1098
1099 case HollowShape.Triangle:
1100
1101 hollowVolume *= (0.5f * .5f);
1102 break;
1103
1104 default:
1105 hollowVolume = 0;
1106 break;
1107 }
1108 volume *= (1.0f - hollowVolume);
1109 }
1110 }
1111
1112 else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
1113 {
1114 //a tube
1115
1116 volume *= 0.78539816339e-2f * (float)(200 - BaseShape.PathScaleX);
1117 tmp= 1.0f -2.0e-2f * (float)(200 - BaseShape.PathScaleY);
1118 volume -= volume*tmp*tmp;
1119
1120 if (hollowAmount > 0.0)
1121 {
1122 hollowVolume *= hollowAmount;
1123
1124 switch (BaseShape.HollowShape)
1125 {
1126 case HollowShape.Square:
1127 case HollowShape.Same:
1128 break;
1129
1130 case HollowShape.Circle:
1131 hollowVolume *= 0.78539816339f;;
1132 break;
1133
1134 case HollowShape.Triangle:
1135 hollowVolume *= 0.5f * 0.5f;
1136 break;
1137 default:
1138 hollowVolume = 0;
1139 break;
1140 }
1141 volume *= (1.0f - hollowVolume);
1142 }
1143 }
1144
1145 break;
1146
1147 case ProfileShape.Circle:
1148
1149 if (BaseShape.PathCurve == (byte)Extrusion.Straight)
1150 {
1151 volume *= 0.78539816339f; // elipse base
1152
1153 if (hollowAmount > 0.0)
1154 {
1155 switch (BaseShape.HollowShape)
1156 {
1157 case HollowShape.Same:
1158 case HollowShape.Circle:
1159 break;
1160
1161 case HollowShape.Square:
1162 hollowVolume *= 0.5f * 2.5984480504799f;
1163 break;
1164
1165 case HollowShape.Triangle:
1166 hollowVolume *= .5f * 1.27323954473516f;
1167 break;
1168
1169 default:
1170 hollowVolume = 0;
1171 break;
1172 }
1173 volume *= (1.0f - hollowVolume);
1174 }
1175 }
1176
1177 else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
1178 {
1179 volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - BaseShape.PathScaleX);
1180 tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
1181 volume *= (1.0f - tmp * tmp);
1182
1183 if (hollowAmount > 0.0)
1184 {
1185
1186 // calculate the hollow volume by it's shape compared to the prim shape
1187 hollowVolume *= hollowAmount;
1188
1189 switch (BaseShape.HollowShape)
1190 {
1191 case HollowShape.Same:
1192 case HollowShape.Circle:
1193 break;
1194
1195 case HollowShape.Square:
1196 hollowVolume *= 0.5f * 2.5984480504799f;
1197 break;
1198
1199 case HollowShape.Triangle:
1200 hollowVolume *= .5f * 1.27323954473516f;
1201 break;
1202
1203 default:
1204 hollowVolume = 0;
1205 break;
1206 }
1207 volume *= (1.0f - hollowVolume);
1208 }
1209 }
1210 break;
1211
1212 case ProfileShape.HalfCircle:
1213 if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
1214 {
1215 volume *= 0.52359877559829887307710723054658f;
1216 }
1217 break;
1218
1219 case ProfileShape.EquilateralTriangle:
1220
1221 if (BaseShape.PathCurve == (byte)Extrusion.Straight)
1222 {
1223 volume *= 0.32475953f;
1224
1225 if (hollowAmount > 0.0)
1226 {
1227
1228 // calculate the hollow volume by it's shape compared to the prim shape
1229 switch (BaseShape.HollowShape)
1230 {
1231 case HollowShape.Same:
1232 case HollowShape.Triangle:
1233 hollowVolume *= .25f;
1234 break;
1235
1236 case HollowShape.Square:
1237 hollowVolume *= 0.499849f * 3.07920140172638f;
1238 break;
1239
1240 case HollowShape.Circle:
1241 // Hollow shape is a perfect cyllinder in respect to the cube's scale
1242 // Cyllinder hollow volume calculation
1243
1244 hollowVolume *= 0.1963495f * 3.07920140172638f;
1245 break;
1246
1247 default:
1248 hollowVolume = 0;
1249 break;
1250 }
1251 volume *= (1.0f - hollowVolume);
1252 }
1253 }
1254 else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
1255 {
1256 volume *= 0.32475953f;
1257 volume *= 0.01f * (float)(200 - BaseShape.PathScaleX);
1258 tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
1259 volume *= (1.0f - tmp * tmp);
1260
1261 if (hollowAmount > 0.0)
1262 {
1263
1264 hollowVolume *= hollowAmount;
1265
1266 switch (BaseShape.HollowShape)
1267 {
1268 case HollowShape.Same:
1269 case HollowShape.Triangle:
1270 hollowVolume *= .25f;
1271 break;
1272
1273 case HollowShape.Square:
1274 hollowVolume *= 0.499849f * 3.07920140172638f;
1275 break;
1276
1277 case HollowShape.Circle:
1278
1279 hollowVolume *= 0.1963495f * 3.07920140172638f;
1280 break;
1281
1282 default:
1283 hollowVolume = 0;
1284 break;
1285 }
1286 volume *= (1.0f - hollowVolume);
1287 }
1288 }
1289 break;
1290
1291 default:
1292 break;
1293 }
1294
1295
1296
1297 float taperX1;
1298 float taperY1;
1299 float taperX;
1300 float taperY;
1301 float pathBegin;
1302 float pathEnd;
1303 float profileBegin;
1304 float profileEnd;
1305
1306 if (BaseShape.PathCurve == (byte)Extrusion.Straight || BaseShape.PathCurve == (byte)Extrusion.Flexible)
1307 {
1308 taperX1 = BaseShape.PathScaleX * 0.01f;
1309 if (taperX1 > 1.0f)
1310 taperX1 = 2.0f - taperX1;
1311 taperX = 1.0f - taperX1;
1312
1313 taperY1 = BaseShape.PathScaleY * 0.01f;
1314 if (taperY1 > 1.0f)
1315 taperY1 = 2.0f - taperY1;
1316 taperY = 1.0f - taperY1;
1317 }
1318 else
1319 {
1320 taperX = BaseShape.PathTaperX * 0.01f;
1321 if (taperX < 0.0f)
1322 taperX = -taperX;
1323 taperX1 = 1.0f - taperX;
1324
1325 taperY = BaseShape.PathTaperY * 0.01f;
1326 if (taperY < 0.0f)
1327 taperY = -taperY;
1328 taperY1 = 1.0f - taperY;
1329
1330 }
1331
1332
1333 volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY);
1334
1335 pathBegin = (float)BaseShape.PathBegin * 2.0e-5f;
1336 pathEnd = 1.0f - (float)BaseShape.PathEnd * 2.0e-5f;
1337 volume *= (pathEnd - pathBegin);
1338
1339 // this is crude aproximation
1340 profileBegin = (float)BaseShape.ProfileBegin * 2.0e-5f;
1341 profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f;
1342 volume *= (profileEnd - profileBegin);
1343
1344 returnMass = _density * volume;
1345
1346 /* Comment out code that computes the mass of the linkset. That is done in the Linkset class.
1347 if (IsRootOfLinkset)
1348 {
1349 foreach (BSPrim prim in _childrenPrims)
1350 {
1351 returnMass += prim.CalculateMass();
1352 }
1353 }
1354 */
1355
1356 returnMass = Util.Clamp(returnMass, BSParam.MinimumObjectMass, BSParam.MaximumObjectMass);
1357
1358 return returnMass;
1359 }// end CalculateMass
1360 #endregion Mass Calculation
1361
1362 // Rebuild the geometry and object.
1363 // This is called when the shape changes so we need to recreate the mesh/hull.
1364 // Called at taint-time!!!
1365 public void CreateGeomAndObject(bool forceRebuild)
1366 {
1367 // If this prim is part of a linkset, we must remove and restore the physical
1368 // links if the body is rebuilt.
1369 bool needToRestoreLinkset = false;
1370 bool needToRestoreVehicle = false;
1371
1372 // Create the correct physical representation for this type of object.
1373 // Updates PhysBody and PhysShape with the new information.
1374 // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary.
1375 PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, null, delegate(BulletBody dBody)
1376 {
1377 // Called if the current prim body is about to be destroyed.
1378 // Remove all the physical dependencies on the old body.
1379 // (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...)
1380 needToRestoreLinkset = Linkset.RemoveBodyDependencies(this);
1381 needToRestoreVehicle = _vehicle.RemoveBodyDependencies(this);
1382 });
1383
1384 if (needToRestoreLinkset)
1385 {
1386 // If physical body dependencies were removed, restore them
1387 Linkset.RestoreBodyDependencies(this);
1388 }
1389 if (needToRestoreVehicle)
1390 {
1391 // If physical body dependencies were removed, restore them
1392 _vehicle.RestoreBodyDependencies(this);
1393 }
1394
1395 // Make sure the properties are set on the new object
1396 UpdatePhysicalParameters();
1397 return;
1398 }
1399
1400 // The physics engine says that properties have updated. Update same and inform
1401 // the world that things have changed.
1402 // TODO: do we really need to check for changed? Maybe just copy values and call RequestPhysicsterseUpdate()
1403 enum UpdatedProperties {
1404 Position = 1 << 0,
1405 Rotation = 1 << 1,
1406 Velocity = 1 << 2,
1407 Acceleration = 1 << 3,
1408 RotationalVel = 1 << 4
1409 }
1410
1411 const float ROTATION_TOLERANCE = 0.01f;
1412 const float VELOCITY_TOLERANCE = 0.001f;
1413 const float POSITION_TOLERANCE = 0.05f;
1414 const float ACCELERATION_TOLERANCE = 0.01f;
1415 const float ROTATIONAL_VELOCITY_TOLERANCE = 0.01f;
1416
1417 public override void UpdateProperties(EntityProperties entprop)
1418 {
1419 // Updates only for individual prims and for the root object of a linkset.
1420 if (Linkset.IsRoot(this))
1421 {
1422 // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet
1423 // TODO: handle physics introduced by Bullet with computed vehicle physics.
1424 if (_vehicle.IsActive)
1425 {
1426 entprop.RotationalVelocity = OMV.Vector3.Zero;
1427 }
1428
1429 // Assign directly to the local variables so the normal set action does not happen
1430 _position = entprop.Position;
1431 _orientation = entprop.Rotation;
1432 _velocity = entprop.Velocity;
1433 _acceleration = entprop.Acceleration;
1434 _rotationalVelocity = entprop.RotationalVelocity;
1435
1436 // The sanity check can change the velocity and/or position.
1437 if (IsPhysical && PositionSanityCheck(true))
1438 {
1439 entprop.Position = _position;
1440 entprop.Velocity = _velocity;
1441 }
1442
1443 OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; // DEBUG DEBUG DEBUG
1444 DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}",
1445 LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity);
1446
1447 // remember the current and last set values
1448 LastEntityProperties = CurrentEntityProperties;
1449 CurrentEntityProperties = entprop;
1450
1451 base.RequestPhysicsterseUpdate();
1452 }
1453 /*
1454 else
1455 {
1456 // For debugging, report the movement of children
1457 DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
1458 LocalID, entprop.Position, entprop.Rotation, entprop.Velocity,
1459 entprop.Acceleration, entprop.RotationalVelocity);
1460 }
1461 */
1462
1463 // The linkset implimentation might want to know about this.
1464 Linkset.UpdateProperties(this, true);
1465 }
1466}
1467}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSScene.cs
new file mode 100644
index 0000000..6bcea3f
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSScene.cs
@@ -0,0 +1,954 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Runtime.InteropServices;
30using System.Text;
31using System.Threading;
32using OpenSim.Framework;
33using OpenSim.Region.Framework;
34using OpenSim.Region.CoreModules;
35using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging;
36using OpenSim.Region.Physics.Manager;
37using Nini.Config;
38using log4net;
39using OpenMetaverse;
40
41// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
42// Based on material, set density and friction
43// More efficient memory usage when passing hull information from BSPrim to BulletSim
44// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
45// Implement LockAngularMotion
46// Add PID movement operations. What does ScenePresence.MoveToTarget do?
47// Check terrain size. 128 or 127?
48// Raycast
49//
50namespace OpenSim.Region.Physics.BulletSNPlugin
51{
52public sealed class BSScene : PhysicsScene, IPhysicsParameters
53{
54 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
55 private static readonly string LogHeader = "[BULLETS SCENE]";
56
57 // The name of the region we're working for.
58 public string RegionName { get; private set; }
59
60 public string BulletSimVersion = "?";
61
62 public Dictionary<uint, BSPhysObject> PhysObjects;
63 public BSShapeCollection Shapes;
64
65 // Keeping track of the objects with collisions so we can report begin and end of a collision
66 public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>();
67 public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>();
68 // Keep track of all the avatars so we can send them a collision event
69 // every tick so OpenSim will update its animation.
70 private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>();
71
72 // let my minuions use my logger
73 public ILog Logger { get { return m_log; } }
74
75 public IMesher mesher;
76 public uint WorldID { get; private set; }
77 public BulletSim World { get; private set; }
78
79 // All the constraints that have been allocated in this instance.
80 public BSConstraintCollection Constraints { get; private set; }
81
82 // Simulation parameters
83 internal int m_maxSubSteps;
84 internal float m_fixedTimeStep;
85 internal long m_simulationStep = 0;
86 public long SimulationStep { get { return m_simulationStep; } }
87 internal int m_taintsToProcessPerStep;
88 internal float LastTimeStep { get; private set; }
89
90 // Physical objects can register for prestep or poststep events
91 public delegate void PreStepAction(float timeStep);
92 public delegate void PostStepAction(float timeStep);
93 public event PreStepAction BeforeStep;
94 public event PreStepAction AfterStep;
95
96 // A value of the time now so all the collision and update routines do not have to get their own
97 // Set to 'now' just before all the prims and actors are called for collisions and updates
98 public int SimulationNowTime { get; private set; }
99
100 // True if initialized and ready to do simulation steps
101 private bool m_initialized = false;
102
103 // Flag which is true when processing taints.
104 // Not guaranteed to be correct all the time (don't depend on this) but good for debugging.
105 public bool InTaintTime { get; private set; }
106
107 // Pinned memory used to pass step information between managed and unmanaged
108 internal int m_maxCollisionsPerFrame;
109 private List<BulletXNA.CollisionDesc> m_collisionArray;
110 //private GCHandle m_collisionArrayPinnedHandle;
111
112 internal int m_maxUpdatesPerFrame;
113 private List<BulletXNA.EntityProperties> m_updateArray;
114 //private GCHandle m_updateArrayPinnedHandle;
115
116
117 public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero
118 public const uint GROUNDPLANE_ID = 1;
119 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
120
121 public float SimpleWaterLevel { get; set; }
122 public BSTerrainManager TerrainManager { get; private set; }
123
124 public ConfigurationParameters Params
125 {
126 get { return UnmanagedParams[0]; }
127 }
128 public Vector3 DefaultGravity
129 {
130 get { return new Vector3(0f, 0f, Params.gravity); }
131 }
132 // Just the Z value of the gravity
133 public float DefaultGravityZ
134 {
135 get { return Params.gravity; }
136 }
137
138 // When functions in the unmanaged code must be called, it is only
139 // done at a known time just before the simulation step. The taint
140 // system saves all these function calls and executes them in
141 // order before the simulation.
142 public delegate void TaintCallback();
143 private struct TaintCallbackEntry
144 {
145 public String ident;
146 public TaintCallback callback;
147 public TaintCallbackEntry(string i, TaintCallback c)
148 {
149 ident = i;
150 callback = c;
151 }
152 }
153 private Object _taintLock = new Object(); // lock for using the next object
154 private List<TaintCallbackEntry> _taintOperations;
155 private Dictionary<string, TaintCallbackEntry> _postTaintOperations;
156 private List<TaintCallbackEntry> _postStepOperations;
157
158 // A pointer to an instance if this structure is passed to the C++ code
159 // Used to pass basic configuration values to the unmanaged code.
160 internal ConfigurationParameters[] UnmanagedParams;
161 //GCHandle m_paramsHandle;
162
163 // Handle to the callback used by the unmanaged code to call into the managed code.
164 // Used for debug logging.
165 // Need to store the handle in a persistant variable so it won't be freed.
166 private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle;
167
168 // Sometimes you just have to log everything.
169 public Logging.LogWriter PhysicsLogging;
170 private bool m_physicsLoggingEnabled;
171 private string m_physicsLoggingDir;
172 private string m_physicsLoggingPrefix;
173 private int m_physicsLoggingFileMinutes;
174 private bool m_physicsLoggingDoFlush;
175 // 'true' of the vehicle code is to log lots of details
176 public bool VehicleLoggingEnabled { get; private set; }
177 public bool VehiclePhysicalLoggingEnabled { get; private set; }
178
179 #region Construction and Initialization
180 public BSScene(string identifier)
181 {
182 m_initialized = false;
183 // we are passed the name of the region we're working for.
184 RegionName = identifier;
185 }
186
187 public override void Initialise(IMesher meshmerizer, IConfigSource config)
188 {
189 mesher = meshmerizer;
190 _taintOperations = new List<TaintCallbackEntry>();
191 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
192 _postStepOperations = new List<TaintCallbackEntry>();
193 PhysObjects = new Dictionary<uint, BSPhysObject>();
194 Shapes = new BSShapeCollection(this);
195
196 // Allocate pinned memory to pass parameters.
197 UnmanagedParams = new ConfigurationParameters[1];
198 //m_paramsHandle = GCHandle.Alloc(UnmanagedParams, GCHandleType.Pinned);
199
200 // Set default values for physics parameters plus any overrides from the ini file
201 GetInitialParameterValues(config);
202
203 // allocate more pinned memory close to the above in an attempt to get the memory all together
204 m_collisionArray = new List<BulletXNA.CollisionDesc>();
205 //m_collisionArrayPinnedHandle = GCHandle.Alloc(m_collisionArray, GCHandleType.Pinned);
206 m_updateArray = new List<BulletXNA.EntityProperties>();
207 //m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned);
208
209 // Enable very detailed logging.
210 // By creating an empty logger when not logging, the log message invocation code
211 // can be left in and every call doesn't have to check for null.
212 if (m_physicsLoggingEnabled)
213 {
214 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes);
215 PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output error messages.
216 }
217 else
218 {
219 PhysicsLogging = new Logging.LogWriter();
220 }
221
222 // If Debug logging level, enable logging from the unmanaged code
223 m_DebugLogCallbackHandle = null;
224 if (m_log.IsDebugEnabled || PhysicsLogging.Enabled)
225 {
226 m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader);
227 if (PhysicsLogging.Enabled)
228 // The handle is saved in a variable to make sure it doesn't get freed after this call
229 m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLoggerPhysLog);
230 else
231 m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger);
232 }
233
234 // Get the version of the DLL
235 // TODO: this doesn't work yet. Something wrong with marshaling the returned string.
236 // BulletSimVersion = BulletSimAPI.GetVersion();
237 // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion);
238
239 // The bounding box for the simulated world. The origin is 0,0,0 unless we're
240 // a child in a mega-region.
241 // Bullet actually doesn't care about the extents of the simulated
242 // area. It tracks active objects no matter where they are.
243 Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
244
245 // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
246
247 World = new BulletSim(0, this, BulletSimAPI.Initialize2(worldExtent, UnmanagedParams,
248 m_maxCollisionsPerFrame, ref m_collisionArray,
249 m_maxUpdatesPerFrame,ref m_updateArray,
250 m_DebugLogCallbackHandle));
251
252 Constraints = new BSConstraintCollection(World);
253
254 TerrainManager = new BSTerrainManager(this);
255 TerrainManager.CreateInitialGroundPlaneAndTerrain();
256
257 m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation);
258
259 InTaintTime = false;
260 m_initialized = true;
261 }
262
263 // All default parameter values are set here. There should be no values set in the
264 // variable definitions.
265 private void GetInitialParameterValues(IConfigSource config)
266 {
267 ConfigurationParameters parms = new ConfigurationParameters();
268 UnmanagedParams[0] = parms;
269
270 BSParam.SetParameterDefaultValues(this);
271
272 if (config != null)
273 {
274 // If there are specifications in the ini file, use those values
275 IConfig pConfig = config.Configs["BulletSim"];
276 if (pConfig != null)
277 {
278 BSParam.SetParameterConfigurationValues(this, pConfig);
279
280 // Very detailed logging for physics debugging
281 m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false);
282 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
283 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
284 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
285 m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
286 // Very detailed logging for vehicle debugging
287 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
288 VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);
289
290 // Do any replacements in the parameters
291 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
292 }
293
294 // The material characteristics.
295 BSMaterials.InitializeFromDefaults(Params);
296 if (pConfig != null)
297 {
298 // Let the user add new and interesting material property values.
299 BSMaterials.InitializefromParameters(pConfig);
300 }
301 }
302 }
303
304 // A helper function that handles a true/false parameter and returns the proper float number encoding
305 float ParamBoolean(IConfig config, string parmName, float deflt)
306 {
307 float ret = deflt;
308 if (config.Contains(parmName))
309 {
310 ret = ConfigurationParameters.numericFalse;
311 if (config.GetBoolean(parmName, false))
312 {
313 ret = ConfigurationParameters.numericTrue;
314 }
315 }
316 return ret;
317 }
318
319 // Called directly from unmanaged code so don't do much
320 private void BulletLogger(string msg)
321 {
322 m_log.Debug("[BULLETS UNMANAGED]:" + msg);
323 }
324
325 // Called directly from unmanaged code so don't do much
326 private void BulletLoggerPhysLog(string msg)
327 {
328 DetailLog("[BULLETS UNMANAGED]:" + msg);
329 }
330
331 public override void Dispose()
332 {
333 // m_log.DebugFormat("{0}: Dispose()", LogHeader);
334
335 // make sure no stepping happens while we're deleting stuff
336 m_initialized = false;
337
338 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
339 {
340 kvp.Value.Destroy();
341 }
342 PhysObjects.Clear();
343
344 // Now that the prims are all cleaned up, there should be no constraints left
345 if (Constraints != null)
346 {
347 Constraints.Dispose();
348 Constraints = null;
349 }
350
351 if (Shapes != null)
352 {
353 Shapes.Dispose();
354 Shapes = null;
355 }
356
357 if (TerrainManager != null)
358 {
359 TerrainManager.ReleaseGroundPlaneAndTerrain();
360 TerrainManager.Dispose();
361 TerrainManager = null;
362 }
363
364 // Anything left in the unmanaged code should be cleaned out
365 BulletSimAPI.Shutdown2(World.ptr);
366
367 // Not logging any more
368 PhysicsLogging.Close();
369 }
370 #endregion // Construction and Initialization
371
372 #region Prim and Avatar addition and removal
373
374 public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
375 {
376 m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
377 return null;
378 }
379
380 public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying)
381 {
382 // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName);
383
384 if (!m_initialized) return null;
385
386 BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying);
387 lock (PhysObjects) PhysObjects.Add(localID, actor);
388
389 // TODO: Remove kludge someday.
390 // We must generate a collision for avatars whether they collide or not.
391 // This is required by OpenSim to update avatar animations, etc.
392 lock (m_avatars) m_avatars.Add(actor);
393
394 return actor;
395 }
396
397 public override void RemoveAvatar(PhysicsActor actor)
398 {
399 // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader);
400
401 if (!m_initialized) return;
402
403 BSCharacter bsactor = actor as BSCharacter;
404 if (bsactor != null)
405 {
406 try
407 {
408 lock (PhysObjects) PhysObjects.Remove(actor.LocalID);
409 // Remove kludge someday
410 lock (m_avatars) m_avatars.Remove(bsactor);
411 }
412 catch (Exception e)
413 {
414 m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e);
415 }
416 bsactor.Destroy();
417 // bsactor.dispose();
418 }
419 }
420
421 public override void RemovePrim(PhysicsActor prim)
422 {
423 if (!m_initialized) return;
424
425 BSPrim bsprim = prim as BSPrim;
426 if (bsprim != null)
427 {
428 DetailLog("{0},RemovePrim,call", bsprim.LocalID);
429 // m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID);
430 try
431 {
432 lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID);
433 }
434 catch (Exception e)
435 {
436 m_log.ErrorFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e);
437 }
438 bsprim.Destroy();
439 // bsprim.dispose();
440 }
441 else
442 {
443 m_log.ErrorFormat("{0}: Attempt to remove prim that is not a BSPrim type.", LogHeader);
444 }
445 }
446
447 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
448 Vector3 size, Quaternion rotation, bool isPhysical, uint localID)
449 {
450 // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName);
451
452 if (!m_initialized) return null;
453
454 DetailLog("{0},AddPrimShape,call", localID);
455
456 BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical);
457 lock (PhysObjects) PhysObjects.Add(localID, prim);
458 return prim;
459 }
460
461 // This is a call from the simulator saying that some physical property has been updated.
462 // The BulletSim driver senses the changing of relevant properties so this taint
463 // information call is not needed.
464 public override void AddPhysicsActorTaint(PhysicsActor prim) { }
465
466 #endregion // Prim and Avatar addition and removal
467
468 #region Simulation
469 // Simulate one timestep
470 public override float Simulate(float timeStep)
471 {
472 // prevent simulation until we've been initialized
473 if (!m_initialized) return 5.0f;
474
475 LastTimeStep = timeStep;
476
477 int updatedEntityCount = 0;
478 //Object updatedEntitiesPtr;
479 int collidersCount = 0;
480 //Object collidersPtr;
481
482 int beforeTime = 0;
483 int simTime = 0;
484
485 // update the prim states while we know the physics engine is not busy
486 int numTaints = _taintOperations.Count;
487
488 InTaintTime = true; // Only used for debugging so locking is not necessary.
489
490 ProcessTaints();
491
492 // Some of the physical objects requre individual, pre-step calls
493 TriggerPreStepEvent(timeStep);
494
495 // the prestep actions might have added taints
496 ProcessTaints();
497
498 InTaintTime = false; // Only used for debugging so locking is not necessary.
499
500 // step the physical world one interval
501 m_simulationStep++;
502 int numSubSteps = 0;
503
504 try
505 {
506 //if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG
507 if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
508
509 numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
510 out updatedEntityCount, out m_updateArray, out collidersCount, out m_collisionArray);
511
512 if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
513 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
514 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
515 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
516 }
517 catch (Exception e)
518 {
519 m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}",
520 LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e);
521 DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}",
522 DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
523 updatedEntityCount = 0;
524 collidersCount = 0;
525 }
526
527 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in.
528
529 // Get a value for 'now' so all the collision and update routines don't have to get their own.
530 SimulationNowTime = Util.EnvironmentTickCount();
531
532 // If there were collisions, process them by sending the event to the prim.
533 // Collisions must be processed before updates.
534 if (collidersCount > 0)
535 {
536 for (int ii = 0; ii < collidersCount; ii++)
537 {
538 uint cA = m_collisionArray[ii].aID;
539 uint cB = m_collisionArray[ii].bID;
540 Vector3 point = new Vector3(m_collisionArray[ii].point.X, m_collisionArray[ii].point.Y,
541 m_collisionArray[ii].point.Z);
542 Vector3 normal = new Vector3(m_collisionArray[ii].normal.X, m_collisionArray[ii].normal.Y,
543 m_collisionArray[ii].normal.Z);
544 SendCollision(cA, cB, point, normal, 0.01f);
545 SendCollision(cB, cA, point, -normal, 0.01f);
546 }
547 }
548
549 // The above SendCollision's batch up the collisions on the objects.
550 // Now push the collisions into the simulator.
551 if (ObjectsWithCollisions.Count > 0)
552 {
553 foreach (BSPhysObject bsp in ObjectsWithCollisions)
554 if (!bsp.SendCollisions())
555 {
556 // If the object is done colliding, see that it's removed from the colliding list
557 ObjectsWithNoMoreCollisions.Add(bsp);
558 }
559 }
560
561 // This is a kludge to get avatar movement updates.
562 // The simulator expects collisions for avatars even if there are have been no collisions.
563 // The event updates avatar animations and stuff.
564 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
565 foreach (BSPhysObject bsp in m_avatars)
566 if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
567 bsp.SendCollisions();
568
569 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
570 // Not done above because it is inside an iteration of ObjectWithCollisions.
571 // This complex collision processing is required to create an empty collision
572 // event call after all collisions have happened on an object. This enables
573 // the simulator to generate the 'collision end' event.
574 if (ObjectsWithNoMoreCollisions.Count > 0)
575 {
576 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
577 ObjectsWithCollisions.Remove(po);
578 ObjectsWithNoMoreCollisions.Clear();
579 }
580 // Done with collisions.
581
582 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
583 if (updatedEntityCount > 0)
584 {
585 for (int ii = 0; ii < updatedEntityCount; ii++)
586 {
587
588 BulletXNA.EntityProperties entprop = m_updateArray[ii];
589 BSPhysObject pobj;
590 if (PhysObjects.TryGetValue(entprop.ID, out pobj))
591 {
592 EntityProperties prop = new EntityProperties()
593 {
594 Acceleration = new Vector3(entprop.Acceleration.X, entprop.Acceleration.Y, entprop.Acceleration.Z),
595 ID = entprop.ID,
596 Position = new Vector3(entprop.Position.X,entprop.Position.Y,entprop.Position.Z),
597 Rotation = new Quaternion(entprop.Rotation.X,entprop.Rotation.Y,entprop.Rotation.Z,entprop.Rotation.W),
598 RotationalVelocity = new Vector3(entprop.AngularVelocity.X,entprop.AngularVelocity.Y,entprop.AngularVelocity.Z),
599 Velocity = new Vector3(entprop.Velocity.X,entprop.Velocity.Y,entprop.Velocity.Z)
600 };
601 //m_log.Debug(pobj.Name + ":" + prop.ToString() + "\n");
602 pobj.UpdateProperties(prop);
603 }
604 }
605 }
606
607 TriggerPostStepEvent(timeStep);
608
609 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
610 // Only enable this in a limited test world with few objects.
611 // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
612
613 // The physics engine returns the number of milliseconds it simulated this call.
614 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
615 // Multiply by 55 to give a nominal frame rate of 55.
616 return (float)numSubSteps * m_fixedTimeStep * 1000f * 55f;
617 }
618
619 // Something has collided
620 private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration)
621 {
622 if (localID <= TerrainManager.HighestTerrainID)
623 {
624 return; // don't send collisions to the terrain
625 }
626
627 BSPhysObject collider;
628 if (!PhysObjects.TryGetValue(localID, out collider))
629 {
630 // If the object that is colliding cannot be found, just ignore the collision.
631 DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith);
632 return;
633 }
634
635 // The terrain is not in the physical object list so 'collidee' can be null when Collide() is called.
636 BSPhysObject collidee = null;
637 PhysObjects.TryGetValue(collidingWith, out collidee);
638
639 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
640
641 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
642 {
643 // If a collision was posted, remember to send it to the simulator
644 ObjectsWithCollisions.Add(collider);
645 }
646
647 return;
648 }
649
650 #endregion // Simulation
651
652 public override void GetResults() { }
653
654 #region Terrain
655
656 public override void SetTerrain(float[] heightMap) {
657 TerrainManager.SetTerrain(heightMap);
658 }
659
660 public override void SetWaterLevel(float baseheight)
661 {
662 SimpleWaterLevel = baseheight;
663 }
664
665 public override void DeleteTerrain()
666 {
667 // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
668 }
669
670 // Although no one seems to check this, I do support combining.
671 public override bool SupportsCombining()
672 {
673 return TerrainManager.SupportsCombining();
674 }
675 // This call says I am a child to region zero in a mega-region. 'pScene' is that
676 // of region zero, 'offset' is my offset from regions zero's origin, and
677 // 'extents' is the largest XY that is handled in my region.
678 public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
679 {
680 TerrainManager.Combine(pScene, offset, extents);
681 }
682
683 // Unhook all the combining that I know about.
684 public override void UnCombine(PhysicsScene pScene)
685 {
686 TerrainManager.UnCombine(pScene);
687 }
688
689 #endregion // Terrain
690
691 public override Dictionary<uint, float> GetTopColliders()
692 {
693 return new Dictionary<uint, float>();
694 }
695
696 public override bool IsThreaded { get { return false; } }
697
698 #region Taints
699 // The simulation execution order is:
700 // Simulate()
701 // DoOneTimeTaints
702 // TriggerPreStepEvent
703 // DoOneTimeTaints
704 // Step()
705 // ProcessAndForwardCollisions
706 // ProcessAndForwardPropertyUpdates
707 // TriggerPostStepEvent
708
709 // Calls to the PhysicsActors can't directly call into the physics engine
710 // because it might be busy. We delay changes to a known time.
711 // We rely on C#'s closure to save and restore the context for the delegate.
712 public void TaintedObject(String ident, TaintCallback callback)
713 {
714 if (!m_initialized) return;
715
716 lock (_taintLock)
717 {
718 _taintOperations.Add(new TaintCallbackEntry(ident, callback));
719 }
720
721 return;
722 }
723
724 // Sometimes a potentially tainted operation can be used in and out of taint time.
725 // This routine executes the command immediately if in taint-time otherwise it is queued.
726 public void TaintedObject(bool inTaintTime, string ident, TaintCallback callback)
727 {
728 if (inTaintTime)
729 callback();
730 else
731 TaintedObject(ident, callback);
732 }
733
734 private void TriggerPreStepEvent(float timeStep)
735 {
736 PreStepAction actions = BeforeStep;
737 if (actions != null)
738 actions(timeStep);
739
740 }
741
742 private void TriggerPostStepEvent(float timeStep)
743 {
744 PreStepAction actions = AfterStep;
745 if (actions != null)
746 actions(timeStep);
747
748 }
749
750 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues
751 // a callback into itself to do the actual property change. That callback is called
752 // here just before the physics engine is called to step the simulation.
753 public void ProcessTaints()
754 {
755 ProcessRegularTaints();
756 ProcessPostTaintTaints();
757 }
758
759 private void ProcessRegularTaints()
760 {
761 if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process
762 {
763 // swizzle a new list into the list location so we can process what's there
764 List<TaintCallbackEntry> oldList;
765 lock (_taintLock)
766 {
767 oldList = _taintOperations;
768 _taintOperations = new List<TaintCallbackEntry>();
769 }
770
771 foreach (TaintCallbackEntry tcbe in oldList)
772 {
773 try
774 {
775 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG
776 tcbe.callback();
777 }
778 catch (Exception e)
779 {
780 m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e);
781 }
782 }
783 oldList.Clear();
784 }
785 }
786
787 // Schedule an update to happen after all the regular taints are processed.
788 // Note that new requests for the same operation ("ident") for the same object ("ID")
789 // will replace any previous operation by the same object.
790 public void PostTaintObject(String ident, uint ID, TaintCallback callback)
791 {
792 string uniqueIdent = ident + "-" + ID.ToString();
793 lock (_taintLock)
794 {
795 _postTaintOperations[uniqueIdent] = new TaintCallbackEntry(uniqueIdent, callback);
796 }
797
798 return;
799 }
800
801 // Taints that happen after the normal taint processing but before the simulation step.
802 private void ProcessPostTaintTaints()
803 {
804 if (_postTaintOperations.Count > 0)
805 {
806 Dictionary<string, TaintCallbackEntry> oldList;
807 lock (_taintLock)
808 {
809 oldList = _postTaintOperations;
810 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
811 }
812
813 foreach (KeyValuePair<string,TaintCallbackEntry> kvp in oldList)
814 {
815 try
816 {
817 DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG
818 kvp.Value.callback();
819 }
820 catch (Exception e)
821 {
822 m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e);
823 }
824 }
825 oldList.Clear();
826 }
827 }
828
829 // Only used for debugging. Does not change state of anything so locking is not necessary.
830 public bool AssertInTaintTime(string whereFrom)
831 {
832 if (!InTaintTime)
833 {
834 DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
835 m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
836 Util.PrintCallStack(); // Prints the stack into the DEBUG log file.
837 }
838 return InTaintTime;
839 }
840
841 #endregion // Taints
842
843 #region INI and command line parameter processing
844
845 #region IPhysicsParameters
846 // Get the list of parameters this physics engine supports
847 public PhysParameterEntry[] GetParameterList()
848 {
849 BSParam.BuildParameterTable();
850 return BSParam.SettableParameters;
851 }
852
853 // Set parameter on a specific or all instances.
854 // Return 'false' if not able to set the parameter.
855 // Setting the value in the m_params block will change the value the physics engine
856 // will use the next time since it's pinned and shared memory.
857 // Some of the values require calling into the physics engine to get the new
858 // value activated ('terrainFriction' for instance).
859 public bool SetPhysicsParameter(string parm, float val, uint localID)
860 {
861 bool ret = false;
862 BSParam.ParameterDefn theParam;
863 if (BSParam.TryGetParameter(parm, out theParam))
864 {
865 theParam.setter(this, parm, localID, val);
866 ret = true;
867 }
868 return ret;
869 }
870
871 // update all the localIDs specified
872 // If the local ID is APPLY_TO_NONE, just change the default value
873 // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
874 // If the localID is a specific object, apply the parameter change to only that object
875 internal delegate void AssignVal(float x);
876 internal void UpdateParameterObject(AssignVal setDefault, string parm, uint localID, float val)
877 {
878 List<uint> objectIDs = new List<uint>();
879 switch (localID)
880 {
881 case PhysParameterEntry.APPLY_TO_NONE:
882 setDefault(val); // setting only the default value
883 // This will cause a call into the physical world if some operation is specified (SetOnObject).
884 objectIDs.Add(TERRAIN_ID);
885 TaintedUpdateParameter(parm, objectIDs, val);
886 break;
887 case PhysParameterEntry.APPLY_TO_ALL:
888 setDefault(val); // setting ALL also sets the default value
889 lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
890 TaintedUpdateParameter(parm, objectIDs, val);
891 break;
892 default:
893 // setting only one localID
894 objectIDs.Add(localID);
895 TaintedUpdateParameter(parm, objectIDs, val);
896 break;
897 }
898 }
899
900 // schedule the actual updating of the paramter to when the phys engine is not busy
901 private void TaintedUpdateParameter(string parm, List<uint> lIDs, float val)
902 {
903 float xval = val;
904 List<uint> xlIDs = lIDs;
905 string xparm = parm;
906 TaintedObject("BSScene.UpdateParameterSet", delegate() {
907 BSParam.ParameterDefn thisParam;
908 if (BSParam.TryGetParameter(xparm, out thisParam))
909 {
910 if (thisParam.onObject != null)
911 {
912 foreach (uint lID in xlIDs)
913 {
914 BSPhysObject theObject = null;
915 PhysObjects.TryGetValue(lID, out theObject);
916 thisParam.onObject(this, theObject, xval);
917 }
918 }
919 }
920 });
921 }
922
923 // Get parameter.
924 // Return 'false' if not able to get the parameter.
925 public bool GetPhysicsParameter(string parm, out float value)
926 {
927 float val = 0f;
928 bool ret = false;
929 BSParam.ParameterDefn theParam;
930 if (BSParam.TryGetParameter(parm, out theParam))
931 {
932 val = theParam.getter(this);
933 ret = true;
934 }
935 value = val;
936 return ret;
937 }
938
939 #endregion IPhysicsParameters
940
941 #endregion Runtime settable parameters
942
943 // Invoke the detailed logger and output something if it's enabled.
944 public void DetailLog(string msg, params Object[] args)
945 {
946 PhysicsLogging.Write(msg, args);
947 // Add the Flush() if debugging crashes. Gets all the messages written out.
948 if (m_physicsLoggingDoFlush) PhysicsLogging.Flush();
949 }
950 // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
951 public const string DetailLogZero = "0000000000";
952
953}
954}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSShapeCollection.cs
new file mode 100644
index 0000000..398ece0
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSShapeCollection.cs
@@ -0,0 +1,1015 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using OMV = OpenMetaverse;
31using OpenSim.Framework;
32using OpenSim.Region.Physics.Manager;
33using OpenSim.Region.Physics.ConvexDecompositionDotNet;
34
35namespace OpenSim.Region.Physics.BulletSNPlugin
36{
37public sealed class BSShapeCollection : IDisposable
38{
39 private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]";
40
41 private BSScene PhysicsScene { get; set; }
42
43 private Object m_collectionActivityLock = new Object();
44
45 // Description of a Mesh
46 private struct MeshDesc
47 {
48 public Object ptr;
49 public int referenceCount;
50 public DateTime lastReferenced;
51 public UInt64 shapeKey;
52 }
53
54 // Description of a hull.
55 // Meshes and hulls have the same shape hash key but we only need hulls for efficient collision calculations.
56 private struct HullDesc
57 {
58 public Object ptr;
59 public int referenceCount;
60 public DateTime lastReferenced;
61 public UInt64 shapeKey;
62 }
63
64 // The sharable set of meshes and hulls. Indexed by their shape hash.
65 private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>();
66 private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>();
67
68 private bool DDetail = false;
69
70 public BSShapeCollection(BSScene physScene)
71 {
72 PhysicsScene = physScene;
73 // Set the next to 'true' for very detailed shape update detailed logging (detailed details?)
74 // While detailed debugging is still active, this is better than commenting out all the
75 // DetailLog statements. When debugging slows down, this and the protected logging
76 // statements can be commented/removed.
77 DDetail = true;
78 }
79
80 public void Dispose()
81 {
82 // TODO!!!!!!!!!
83 }
84
85 // Callbacks called just before either the body or shape is destroyed.
86 // Mostly used for changing bodies out from under Linksets.
87 // Useful for other cases where parameters need saving.
88 // Passing 'null' says no callback.
89 public delegate void ShapeDestructionCallback(BulletShape shape);
90 public delegate void BodyDestructionCallback(BulletBody body);
91
92 // Called to update/change the body and shape for an object.
93 // First checks the shape and updates that if necessary then makes
94 // sure the body is of the right type.
95 // Return 'true' if either the body or the shape changed.
96 // 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before
97 // the current shape or body is destroyed. This allows the caller to remove any
98 // higher level dependencies on the shape or body. Mostly used for LinkSets to
99 // remove the physical constraints before the body is destroyed.
100 // Called at taint-time!!
101 public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim,
102 ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
103 {
104 PhysicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape");
105
106 bool ret = false;
107
108 // This lock could probably be pushed down lower but building shouldn't take long
109 lock (m_collectionActivityLock)
110 {
111 // Do we have the correct geometry for this type of object?
112 // Updates prim.BSShape with information/pointers to shape.
113 // Returns 'true' of BSShape is changed to a new shape.
114 bool newGeom = CreateGeom(forceRebuild, prim, shapeCallback);
115 // If we had to select a new shape geometry for the object,
116 // rebuild the body around it.
117 // Updates prim.BSBody with information/pointers to requested body
118 // Returns 'true' if BSBody was changed.
119 bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World,
120 prim.PhysShape, bodyCallback);
121 ret = newGeom || newBody;
122 }
123 DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}",
124 prim.LocalID, forceRebuild, ret, prim.PhysBody, prim.PhysShape);
125
126 return ret;
127 }
128
129 public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim)
130 {
131 return GetBodyAndShape(forceRebuild, sim, prim, null, null);
132 }
133
134 // Track another user of a body.
135 // We presume the caller has allocated the body.
136 // Bodies only have one user so the body is just put into the world if not already there.
137 public void ReferenceBody(BulletBody body, bool inTaintTime)
138 {
139 lock (m_collectionActivityLock)
140 {
141 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body);
142 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.ReferenceBody", delegate()
143 {
144 if (!BulletSimAPI.IsInWorld2(PhysicsScene.World.ptr, body.ptr))
145 {
146 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr);
147 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
148 }
149 });
150 }
151 }
152
153 // Release the usage of a body.
154 // Called when releasing use of a BSBody. BSShape is handled separately.
155 public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback )
156 {
157 if (!body.HasPhysicalBody)
158 return;
159
160 lock (m_collectionActivityLock)
161 {
162 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceBody", delegate()
163 {
164 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1},inTaintTime={2}",
165 body.ID, body, inTaintTime);
166 // If the caller needs to know the old body is going away, pass the event up.
167 if (bodyCallback != null) bodyCallback(body);
168
169 if (BulletSimAPI.IsInWorld2(PhysicsScene.World.ptr, body.ptr))
170 {
171 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
172 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body);
173 }
174
175 // Zero any reference to the shape so it is not freed when the body is deleted.
176 BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, null);
177 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
178 });
179 }
180 }
181
182 // Track the datastructures and use count for a shape.
183 // When creating a hull, this is called first to reference the mesh
184 // and then again to reference the hull.
185 // Meshes and hulls for the same shape have the same hash key.
186 // NOTE that native shapes are not added to the mesh list or removed.
187 // Returns 'true' if this is the initial reference to the shape. Otherwise reused.
188 public bool ReferenceShape(BulletShape shape)
189 {
190 bool ret = false;
191 switch (shape.type)
192 {
193 case BSPhysicsShapeType.SHAPE_MESH:
194 MeshDesc meshDesc;
195 if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
196 {
197 // There is an existing instance of this mesh.
198 meshDesc.referenceCount++;
199 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}",
200 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
201 }
202 else
203 {
204 // This is a new reference to a mesh
205 meshDesc.ptr = shape.ptr;
206 meshDesc.shapeKey = shape.shapeKey;
207 // We keep a reference to the underlying IMesh data so a hull can be built
208 meshDesc.referenceCount = 1;
209 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}",
210 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
211 ret = true;
212 }
213 meshDesc.lastReferenced = System.DateTime.Now;
214 Meshes[shape.shapeKey] = meshDesc;
215 break;
216 case BSPhysicsShapeType.SHAPE_HULL:
217 HullDesc hullDesc;
218 if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
219 {
220 // There is an existing instance of this hull.
221 hullDesc.referenceCount++;
222 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}",
223 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
224 }
225 else
226 {
227 // This is a new reference to a hull
228 hullDesc.ptr = shape.ptr;
229 hullDesc.shapeKey = shape.shapeKey;
230 hullDesc.referenceCount = 1;
231 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}",
232 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
233 ret = true;
234
235 }
236 hullDesc.lastReferenced = System.DateTime.Now;
237 Hulls[shape.shapeKey] = hullDesc;
238 break;
239 case BSPhysicsShapeType.SHAPE_UNKNOWN:
240 break;
241 default:
242 // Native shapes are not tracked and they don't go into any list
243 break;
244 }
245 return ret;
246 }
247
248 // Release the usage of a shape.
249 public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback)
250 {
251 if (!shape.HasPhysicalShape)
252 return;
253
254 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceShape", delegate()
255 {
256 if (shape.HasPhysicalShape)
257 {
258 if (shape.isNativeShape)
259 {
260 // Native shapes are not tracked and are released immediately
261 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
262 BSScene.DetailLogZero, shape.ptr.ToString(), inTaintTime);
263 if (shapeCallback != null) shapeCallback(shape);
264 BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
265 }
266 else
267 {
268 switch (shape.type)
269 {
270 case BSPhysicsShapeType.SHAPE_HULL:
271 DereferenceHull(shape, shapeCallback);
272 break;
273 case BSPhysicsShapeType.SHAPE_MESH:
274 DereferenceMesh(shape, shapeCallback);
275 break;
276 case BSPhysicsShapeType.SHAPE_COMPOUND:
277 DereferenceCompound(shape, shapeCallback);
278 break;
279 case BSPhysicsShapeType.SHAPE_UNKNOWN:
280 break;
281 default:
282 break;
283 }
284 }
285 }
286 });
287 }
288
289 // Count down the reference count for a mesh shape
290 // Called at taint-time.
291 private void DereferenceMesh(BulletShape shape, ShapeDestructionCallback shapeCallback)
292 {
293 MeshDesc meshDesc;
294 if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
295 {
296 meshDesc.referenceCount--;
297 // TODO: release the Bullet storage
298 if (shapeCallback != null) shapeCallback(shape);
299 meshDesc.lastReferenced = System.DateTime.Now;
300 Meshes[shape.shapeKey] = meshDesc;
301 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}",
302 BSScene.DetailLogZero, shape, meshDesc.referenceCount);
303
304 }
305 }
306
307 // Count down the reference count for a hull shape
308 // Called at taint-time.
309 private void DereferenceHull(BulletShape shape, ShapeDestructionCallback shapeCallback)
310 {
311 HullDesc hullDesc;
312 if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
313 {
314 hullDesc.referenceCount--;
315 // TODO: release the Bullet storage (aging old entries?)
316
317 // Tell upper layers that, if they have dependencies on this shape, this link is going away
318 if (shapeCallback != null) shapeCallback(shape);
319
320 hullDesc.lastReferenced = System.DateTime.Now;
321 Hulls[shape.shapeKey] = hullDesc;
322 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}",
323 BSScene.DetailLogZero, shape, hullDesc.referenceCount);
324 }
325 }
326
327 // Remove a reference to a compound shape.
328 // Taking a compound shape apart is a little tricky because if you just delete the
329 // physical shape, it will free all the underlying children. We can't do that because
330 // they could be shared. So, this removes each of the children from the compound and
331 // dereferences them separately before destroying the compound collision object itself.
332 // Called at taint-time.
333 private void DereferenceCompound(BulletShape shape, ShapeDestructionCallback shapeCallback)
334 {
335 if (!BulletSimAPI.IsCompound2(shape.ptr))
336 {
337 // Failed the sanity check!!
338 PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}",
339 LogHeader, shape.type, shape.ptr.ToString());
340 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}",
341 BSScene.DetailLogZero, shape.type, shape.ptr.ToString());
342 return;
343 }
344
345 int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr);
346 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren);
347
348 for (int ii = numChildren - 1; ii >= 0; ii--)
349 {
350 Object childShape = BulletSimAPI.RemoveChildShapeFromCompoundShapeIndex2(shape.ptr, ii);
351 DereferenceAnonCollisionShape(childShape);
352 }
353 BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
354 }
355
356 // Sometimes we have a pointer to a collision shape but don't know what type it is.
357 // Figure out type and call the correct dereference routine.
358 // Called at taint-time.
359 private void DereferenceAnonCollisionShape(Object cShape)
360 {
361 MeshDesc meshDesc;
362 HullDesc hullDesc;
363
364 BulletShape shapeInfo = new BulletShape(cShape);
365 if (TryGetMeshByPtr(cShape, out meshDesc))
366 {
367 shapeInfo.type = BSPhysicsShapeType.SHAPE_MESH;
368 shapeInfo.shapeKey = meshDesc.shapeKey;
369 }
370 else
371 {
372 if (TryGetHullByPtr(cShape, out hullDesc))
373 {
374 shapeInfo.type = BSPhysicsShapeType.SHAPE_HULL;
375 shapeInfo.shapeKey = hullDesc.shapeKey;
376 }
377 else
378 {
379 if (BulletSimAPI.IsCompound2(cShape))
380 {
381 shapeInfo.type = BSPhysicsShapeType.SHAPE_COMPOUND;
382 }
383 else
384 {
385 if (BulletSimAPI.IsNativeShape2(cShape))
386 {
387 shapeInfo.isNativeShape = true;
388 shapeInfo.type = BSPhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter)
389 }
390 }
391 }
392 }
393
394 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo);
395
396 if (shapeInfo.type != BSPhysicsShapeType.SHAPE_UNKNOWN)
397 {
398 DereferenceShape(shapeInfo, true, null);
399 }
400 else
401 {
402 PhysicsScene.Logger.ErrorFormat("{0} Could not decypher shape type. Region={1}, addr={2}",
403 LogHeader, PhysicsScene.RegionName, cShape.ToString());
404 }
405 }
406
407 // Create the geometry information in Bullet for later use.
408 // The objects needs a hull if it's physical otherwise a mesh is enough.
409 // if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls,
410 // shared geometries will be used. If the parameters of the existing shape are the same
411 // as this request, the shape is not rebuilt.
412 // Info in prim.BSShape is updated to the new shape.
413 // Returns 'true' if the geometry was rebuilt.
414 // Called at taint-time!
415 private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
416 {
417 bool ret = false;
418 bool haveShape = false;
419
420 if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE)
421 {
422 // an avatar capsule is close to a native shape (it is not shared)
423 GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_CAPSULE,
424 FixedShapeKey.KEY_CAPSULE, shapeCallback);
425 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape);
426 ret = true;
427 haveShape = true;
428 }
429
430 // Compound shapes are handled special as they are rebuilt from scratch.
431 // This isn't too great a hardship since most of the child shapes will have already been created.
432 if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND)
433 {
434 ret = GetReferenceToCompoundShape(prim, shapeCallback);
435 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape);
436 haveShape = true;
437 }
438
439 if (!haveShape)
440 {
441 ret = CreateGeomNonSpecial(forceRebuild, prim, shapeCallback);
442 }
443
444 return ret;
445 }
446
447 // Create a mesh/hull shape or a native shape if 'nativeShapePossible' is 'true'.
448 public bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
449 {
450 bool ret = false;
451 bool haveShape = false;
452 bool nativeShapePossible = true;
453 PrimitiveBaseShape pbs = prim.BaseShape;
454
455 // If the prim attributes are simple, this could be a simple Bullet native shape
456 if (!haveShape
457 && pbs != null
458 && nativeShapePossible
459 && ((pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim)
460 || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
461 && pbs.ProfileHollow == 0
462 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
463 && pbs.PathBegin == 0 && pbs.PathEnd == 0
464 && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
465 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
466 && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) )
467 {
468 // Get the scale of any existing shape so we can see if the new shape is same native type and same size.
469 OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero;
470 if (prim.PhysShape.HasPhysicalShape)
471 scaleOfExistingShape = BulletSimAPI.GetLocalScaling2(prim.PhysShape.ptr);
472
473 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}",
474 prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.type);
475
476 // It doesn't look like Bullet scales spheres so make sure the scales are all equal
477 if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
478 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)
479 {
480 haveShape = true;
481 if (forceRebuild
482 || prim.Scale != scaleOfExistingShape
483 || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_SPHERE
484 )
485 {
486 ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE,
487 FixedShapeKey.KEY_SPHERE, shapeCallback);
488 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
489 prim.LocalID, forceRebuild, prim.PhysShape);
490 }
491 }
492 if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
493 {
494 haveShape = true;
495 if (forceRebuild
496 || prim.Scale != scaleOfExistingShape
497 || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_BOX
498 )
499 {
500 ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX,
501 FixedShapeKey.KEY_BOX, shapeCallback);
502 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
503 prim.LocalID, forceRebuild, prim.PhysShape);
504 }
505 }
506 }
507
508 // If a simple shape is not happening, create a mesh and possibly a hull.
509 if (!haveShape && pbs != null)
510 {
511 ret = CreateGeomMeshOrHull(prim, shapeCallback);
512 }
513
514 return ret;
515 }
516
517 public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
518 {
519
520 bool ret = false;
521 // Note that if it's a native shape, the check for physical/non-physical is not
522 // made. Native shapes work in either case.
523 if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects)
524 {
525 // Update prim.BSShape to reference a hull of this shape.
526 ret = GetReferenceToHull(prim,shapeCallback);
527 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}",
528 prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
529 }
530 else
531 {
532 ret = GetReferenceToMesh(prim, shapeCallback);
533 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}",
534 prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
535 }
536 return ret;
537 }
538
539 // Creates a native shape and assignes it to prim.BSShape.
540 // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape().
541 private bool GetReferenceToNativeShape(BSPhysObject prim,
542 BSPhysicsShapeType shapeType, FixedShapeKey shapeKey,
543 ShapeDestructionCallback shapeCallback)
544 {
545 // release any previous shape
546 DereferenceShape(prim.PhysShape, true, shapeCallback);
547
548 BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey);
549
550 // Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
551 if (DDetail) DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
552 prim.LocalID, newShape, prim.Scale);
553
554 // native shapes are scaled by Bullet
555 prim.PhysShape = newShape;
556 return true;
557 }
558
559 private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, BSPhysicsShapeType shapeType,
560 FixedShapeKey shapeKey)
561 {
562 BulletShape newShape;
563 // Need to make sure the passed shape information is for the native type.
564 ShapeData nativeShapeData = new ShapeData();
565 nativeShapeData.Type = shapeType;
566 nativeShapeData.ID = prim.LocalID;
567 nativeShapeData.Scale = prim.Scale;
568 nativeShapeData.Size = prim.Scale; // unneeded, I think.
569 nativeShapeData.MeshKey = (ulong)shapeKey;
570 nativeShapeData.HullKey = (ulong)shapeKey;
571
572 if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
573 {
574 // The proper scale has been calculated in the prim.
575 newShape = new BulletShape(
576 BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale)
577 , shapeType);
578 if (DDetail) DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
579 }
580 else
581 {
582 // Native shapes are scaled in Bullet so set the scaling to the size
583 newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType);
584 }
585 if (!newShape.HasPhysicalShape)
586 {
587 PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
588 LogHeader, prim.LocalID, shapeType);
589 }
590 newShape.shapeKey = (System.UInt64)shapeKey;
591 newShape.isNativeShape = true;
592
593 return newShape;
594 }
595
596 // Builds a mesh shape in the physical world and updates prim.BSShape.
597 // Dereferences previous shape in BSShape and adds a reference for this new shape.
598 // Returns 'true' of a mesh was actually built. Otherwise .
599 // Called at taint-time!
600 private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
601 {
602 BulletShape newShape = new BulletShape();
603
604 float lod;
605 System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
606
607 // if this new shape is the same as last time, don't recreate the mesh
608 if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_MESH)
609 return false;
610
611 if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}",
612 prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"));
613
614 // Since we're recreating new, get rid of the reference to the previous shape
615 DereferenceShape(prim.PhysShape, true, shapeCallback);
616
617 newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, prim.BaseShape, prim.Size, lod);
618 // Take evasive action if the mesh was not constructed.
619 newShape = VerifyMeshCreated(newShape, prim);
620
621 ReferenceShape(newShape);
622
623 prim.PhysShape = newShape;
624
625 return true; // 'true' means a new shape has been added to this prim
626 }
627
628 private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
629 {
630 IMesh meshData = null;
631 Object meshPtr = null;
632 MeshDesc meshDesc;
633 if (Meshes.TryGetValue(newMeshKey, out meshDesc))
634 {
635 // If the mesh has already been built just use it.
636 meshPtr = meshDesc.ptr;
637 }
638 else
639 {
640 meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false);
641
642 if (meshData != null)
643 {
644 int[] indices = meshData.getIndexListAsInt();
645 List<OMV.Vector3> vertices = meshData.getVertexList();
646
647 float[] verticesAsFloats = new float[vertices.Count * 3];
648 int vi = 0;
649 foreach (OMV.Vector3 vv in vertices)
650 {
651 verticesAsFloats[vi++] = vv.X;
652 verticesAsFloats[vi++] = vv.Y;
653 verticesAsFloats[vi++] = vv.Z;
654 }
655
656 // m_log.DebugFormat("{0}: BSShapeCollection.CreatePhysicalMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}",
657 // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count);
658
659 meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
660 indices.GetLength(0), indices, vertices.Count, verticesAsFloats);
661 }
662 }
663 BulletShape newShape = new BulletShape(meshPtr, BSPhysicsShapeType.SHAPE_MESH);
664 newShape.shapeKey = newMeshKey;
665
666 return newShape;
667 }
668
669 // See that hull shape exists in the physical world and update prim.BSShape.
670 // We could be creating the hull because scale changed or whatever.
671 private bool GetReferenceToHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
672 {
673 BulletShape newShape;
674
675 float lod;
676 System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
677
678 // if the hull hasn't changed, don't rebuild it
679 if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_HULL)
680 return false;
681
682 if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}",
683 prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X"));
684
685 // Remove usage of the previous shape.
686 DereferenceShape(prim.PhysShape, true, shapeCallback);
687
688 newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, prim.BaseShape, prim.Size, lod);
689 newShape = VerifyMeshCreated(newShape, prim);
690
691 ReferenceShape(newShape);
692
693 prim.PhysShape = newShape;
694 return true; // 'true' means a new shape has been added to this prim
695 }
696
697 List<ConvexResult> m_hulls;
698 private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
699 {
700
701 Object hullPtr = null;
702 HullDesc hullDesc;
703 if (Hulls.TryGetValue(newHullKey, out hullDesc))
704 {
705 // If the hull shape already is created, just use it.
706 hullPtr = hullDesc.ptr;
707 }
708 else
709 {
710 // Build a new hull in the physical world
711 // Pass true for physicalness as this creates some sort of bounding box which we don't need
712 IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false);
713 if (meshData != null)
714 {
715
716 int[] indices = meshData.getIndexListAsInt();
717 List<OMV.Vector3> vertices = meshData.getVertexList();
718
719 //format conversion from IMesh format to DecompDesc format
720 List<int> convIndices = new List<int>();
721 List<float3> convVertices = new List<float3>();
722 for (int ii = 0; ii < indices.GetLength(0); ii++)
723 {
724 convIndices.Add(indices[ii]);
725 }
726 foreach (OMV.Vector3 vv in vertices)
727 {
728 convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
729 }
730
731 // setup and do convex hull conversion
732 m_hulls = new List<ConvexResult>();
733 DecompDesc dcomp = new DecompDesc();
734 dcomp.mIndices = convIndices;
735 dcomp.mVertices = convVertices;
736 ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
737 // create the hull into the _hulls variable
738 convexBuilder.process(dcomp);
739
740 // Convert the vertices and indices for passing to unmanaged.
741 // The hull information is passed as a large floating point array.
742 // The format is:
743 // convHulls[0] = number of hulls
744 // convHulls[1] = number of vertices in first hull
745 // convHulls[2] = hull centroid X coordinate
746 // convHulls[3] = hull centroid Y coordinate
747 // convHulls[4] = hull centroid Z coordinate
748 // convHulls[5] = first hull vertex X
749 // convHulls[6] = first hull vertex Y
750 // convHulls[7] = first hull vertex Z
751 // convHulls[8] = second hull vertex X
752 // ...
753 // convHulls[n] = number of vertices in second hull
754 // convHulls[n+1] = second hull centroid X coordinate
755 // ...
756 //
757 // TODO: is is very inefficient. Someday change the convex hull generator to return
758 // data structures that do not need to be converted in order to pass to Bullet.
759 // And maybe put the values directly into pinned memory rather than marshaling.
760 int hullCount = m_hulls.Count;
761 int totalVertices = 1; // include one for the count of the hulls
762 foreach (ConvexResult cr in m_hulls)
763 {
764 totalVertices += 4; // add four for the vertex count and centroid
765 totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
766 }
767 float[] convHulls = new float[totalVertices];
768
769 convHulls[0] = (float)hullCount;
770 int jj = 1;
771 foreach (ConvexResult cr in m_hulls)
772 {
773 // copy vertices for index access
774 float3[] verts = new float3[cr.HullVertices.Count];
775 int kk = 0;
776 foreach (float3 ff in cr.HullVertices)
777 {
778 verts[kk++] = ff;
779 }
780
781 // add to the array one hull's worth of data
782 convHulls[jj++] = cr.HullIndices.Count;
783 convHulls[jj++] = 0f; // centroid x,y,z
784 convHulls[jj++] = 0f;
785 convHulls[jj++] = 0f;
786 foreach (int ind in cr.HullIndices)
787 {
788 convHulls[jj++] = verts[ind].x;
789 convHulls[jj++] = verts[ind].y;
790 convHulls[jj++] = verts[ind].z;
791 }
792 }
793 // create the hull data structure in Bullet
794 hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
795 }
796 }
797
798 BulletShape newShape = new BulletShape(hullPtr, BSPhysicsShapeType.SHAPE_HULL);
799 newShape.shapeKey = newHullKey;
800
801 return newShape;
802 }
803
804 // Callback from convex hull creater with a newly created hull.
805 // Just add it to our collection of hulls for this shape.
806 private void HullReturn(ConvexResult result)
807 {
808 m_hulls.Add(result);
809 return;
810 }
811
812 // Compound shapes are always built from scratch.
813 // This shouldn't be to bad since most of the parts will be meshes that had been built previously.
814 private bool GetReferenceToCompoundShape(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
815 {
816 // Remove reference to the old shape
817 // Don't need to do this as the shape is freed when the new root shape is created below.
818 // DereferenceShape(prim.PhysShape, true, shapeCallback);
819
820 BulletShape cShape = new BulletShape(
821 BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr, false), BSPhysicsShapeType.SHAPE_COMPOUND);
822
823 // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape.
824 CreateGeomMeshOrHull(prim, shapeCallback);
825 BulletSimAPI.AddChildShapeToCompoundShape2(cShape.ptr, prim.PhysShape.ptr, OMV.Vector3.Zero, OMV.Quaternion.Identity);
826 if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}",
827 prim.LocalID, cShape, prim.PhysShape);
828
829 prim.PhysShape = cShape;
830
831 return true;
832 }
833
834 // Create a hash of all the shape parameters to be used as a key
835 // for this particular shape.
836 private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod)
837 {
838 // level of detail based on size and type of the object
839 float lod = BSParam.MeshLOD;
840 if (pbs.SculptEntry)
841 lod = BSParam.SculptLOD;
842
843 // Mega prims usually get more detail because one can interact with shape approximations at this size.
844 float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z));
845 if (maxAxis > BSParam.MeshMegaPrimThreshold)
846 lod = BSParam.MeshMegaPrimLOD;
847
848 retLod = lod;
849 return pbs.GetMeshKey(size, lod);
850 }
851 // For those who don't want the LOD
852 private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs)
853 {
854 float lod;
855 return ComputeShapeKey(size, pbs, out lod);
856 }
857
858 // The creation of a mesh or hull can fail if an underlying asset is not available.
859 // There are two cases: 1) the asset is not in the cache and it needs to be fetched;
860 // and 2) the asset cannot be converted (like failed decompression of JPEG2000s).
861 // The first case causes the asset to be fetched. The second case requires
862 // us to not loop forever.
863 // Called after creating a physical mesh or hull. If the physical shape was created,
864 // just return.
865 private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim)
866 {
867 // If the shape was successfully created, nothing more to do
868 if (newShape.HasPhysicalShape)
869 return newShape;
870
871 // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
872 if (prim.BaseShape.SculptEntry && !prim.LastAssetBuildFailed && prim.BaseShape.SculptTexture != OMV.UUID.Zero)
873 {
874 prim.LastAssetBuildFailed = true;
875 BSPhysObject xprim = prim;
876 DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lID={1},lastFailed={2}",
877 LogHeader, prim.LocalID, prim.LastAssetBuildFailed);
878 Util.FireAndForget(delegate
879 {
880 RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod;
881 if (assetProvider != null)
882 {
883 BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
884 assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
885 {
886 if (!yprim.BaseShape.SculptEntry)
887 return;
888 if (yprim.BaseShape.SculptTexture.ToString() != asset.ID)
889 return;
890
891 yprim.BaseShape.SculptData = asset.Data;
892 // This will cause the prim to see that the filler shape is not the right
893 // one and try again to build the object.
894 // No race condition with the normal shape setting since the rebuild is at taint time.
895 yprim.ForceBodyShapeRebuild(false);
896
897 });
898 }
899 });
900 }
901 else
902 {
903 if (prim.LastAssetBuildFailed)
904 {
905 PhysicsScene.Logger.ErrorFormat("{0} Mesh failed to fetch asset. lID={1}, texture={2}",
906 LogHeader, prim.LocalID, prim.BaseShape.SculptTexture);
907 }
908 }
909
910 // While we figure out the real problem, stick a simple native shape on the object.
911 BulletShape fillinShape =
912 BuildPhysicalNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
913
914 return fillinShape;
915 }
916
917 // Create a body object in Bullet.
918 // Updates prim.BSBody with the information about the new body if one is created.
919 // Returns 'true' if an object was actually created.
920 // Called at taint-time.
921 private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape,
922 BodyDestructionCallback bodyCallback)
923 {
924 bool ret = false;
925
926 // the mesh, hull or native shape must have already been created in Bullet
927 bool mustRebuild = !prim.PhysBody.HasPhysicalBody;
928
929 // If there is an existing body, verify it's of an acceptable type.
930 // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
931 if (!mustRebuild)
932 {
933 CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.PhysBody.ptr);
934 if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY
935 || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT)
936 {
937 // If the collisionObject is not the correct type for solidness, rebuild what's there
938 mustRebuild = true;
939 }
940 }
941
942 if (mustRebuild || forceRebuild)
943 {
944 // Free any old body
945 DereferenceBody(prim.PhysBody, true, bodyCallback);
946
947 BulletBody aBody;
948 Object bodyPtr = null;
949 if (prim.IsSolid)
950 {
951 bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
952 prim.LocalID, prim.RawPosition, prim.RawOrientation);
953 if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString());
954 }
955 else
956 {
957 bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
958 prim.LocalID, prim.RawPosition, prim.RawOrientation);
959 if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString());
960 }
961 aBody = new BulletBody(prim.LocalID, bodyPtr);
962
963 ReferenceBody(aBody, true);
964
965 prim.PhysBody = aBody;
966
967 ret = true;
968 }
969
970 return ret;
971 }
972
973 private bool TryGetMeshByPtr(Object addr, out MeshDesc outDesc)
974 {
975 bool ret = false;
976 MeshDesc foundDesc = new MeshDesc();
977 foreach (MeshDesc md in Meshes.Values)
978 {
979 if (md.ptr == addr)
980 {
981 foundDesc = md;
982 ret = true;
983 break;
984 }
985
986 }
987 outDesc = foundDesc;
988 return ret;
989 }
990
991 private bool TryGetHullByPtr(Object addr, out HullDesc outDesc)
992 {
993 bool ret = false;
994 HullDesc foundDesc = new HullDesc();
995 foreach (HullDesc hd in Hulls.Values)
996 {
997 if (hd.ptr == addr)
998 {
999 foundDesc = hd;
1000 ret = true;
1001 break;
1002 }
1003
1004 }
1005 outDesc = foundDesc;
1006 return ret;
1007 }
1008
1009 private void DetailLog(string msg, params Object[] args)
1010 {
1011 if (PhysicsScene.PhysicsLogging.Enabled)
1012 PhysicsScene.DetailLog(msg, args);
1013 }
1014}
1015}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSShapes.cs
new file mode 100644
index 0000000..8ff0275
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSShapes.cs
@@ -0,0 +1,208 @@
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 copyrightD
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
28using System;
29using System.Collections.Generic;
30using System.Linq;
31using System.Text;
32
33namespace OpenSim.Region.Physics.BulletSNPlugin
34{
35public abstract class BSShape
36{
37 public Object ptr { get; set; }
38 public BSPhysicsShapeType type { get; set; }
39 public System.UInt64 key { get; set; }
40 public int referenceCount { get; set; }
41 public DateTime lastReferenced { get; set; }
42
43 public BSShape()
44 {
45 ptr = null;
46 type = BSPhysicsShapeType.SHAPE_UNKNOWN;
47 key = 0;
48 referenceCount = 0;
49 lastReferenced = DateTime.Now;
50 }
51
52 // Get a reference to a physical shape. Create if it doesn't exist
53 public static BSShape GetShapeReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
54 {
55 BSShape ret = null;
56
57 if (prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE)
58 {
59 // an avatar capsule is close to a native shape (it is not shared)
60 ret = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_CAPSULE,
61 FixedShapeKey.KEY_CAPSULE);
62 physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret);
63 }
64
65 // Compound shapes are handled special as they are rebuilt from scratch.
66 // This isn't too great a hardship since most of the child shapes will already been created.
67 if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND)
68 {
69 // Getting a reference to a compound shape gets you the compound shape with the root prim shape added
70 ret = BSShapeCompound.GetReference(prim);
71 physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, ret);
72 }
73
74 if (ret == null)
75 ret = GetShapeReferenceNonSpecial(physicsScene, forceRebuild, prim);
76
77 return ret;
78 }
79 public static BSShape GetShapeReferenceNonSpecial(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
80 {
81 return null;
82 }
83 public static BSShape GetShapeReferenceNonNative(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
84 {
85 return null;
86 }
87
88 // Release the use of a physical shape.
89 public abstract void Dereference(BSScene physicsScene);
90
91 // All shapes have a static call to get a reference to the physical shape
92 // protected abstract static BSShape GetReference();
93
94 public override string ToString()
95 {
96 StringBuilder buff = new StringBuilder();
97 buff.Append("<p=");
98 buff.Append(ptr.ToString());
99 buff.Append(",s=");
100 buff.Append(type.ToString());
101 buff.Append(",k=");
102 buff.Append(key.ToString("X"));
103 buff.Append(",c=");
104 buff.Append(referenceCount.ToString());
105 buff.Append(">");
106 return buff.ToString();
107 }
108}
109
110public class BSShapeNull : BSShape
111{
112 public BSShapeNull() : base()
113 {
114 }
115 public static BSShape GetReference() { return new BSShapeNull(); }
116 public override void Dereference(BSScene physicsScene) { /* The magic of garbage collection will make this go away */ }
117}
118
119public class BSShapeNative : BSShape
120{
121 private static string LogHeader = "[BULLETSIM SHAPE NATIVE]";
122 public BSShapeNative() : base()
123 {
124 }
125 public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim,
126 BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
127 {
128 // Native shapes are not shared and are always built anew.
129 return new BSShapeNative(physicsScene, prim, shapeType, shapeKey);
130 }
131
132 private BSShapeNative(BSScene physicsScene, BSPhysObject prim,
133 BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
134 {
135 ShapeData nativeShapeData = new ShapeData();
136 nativeShapeData.Type = shapeType;
137 nativeShapeData.ID = prim.LocalID;
138 nativeShapeData.Scale = prim.Scale;
139 nativeShapeData.Size = prim.Scale;
140 nativeShapeData.MeshKey = (ulong)shapeKey;
141 nativeShapeData.HullKey = (ulong)shapeKey;
142
143
144 if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
145 {
146 ptr = BulletSimAPI.BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale);
147 physicsScene.DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
148 }
149 else
150 {
151 ptr = BulletSimAPI.BuildNativeShape2(physicsScene.World.ptr, nativeShapeData);
152 }
153 if (ptr == null)
154 {
155 physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
156 LogHeader, prim.LocalID, shapeType);
157 }
158 type = shapeType;
159 key = (UInt64)shapeKey;
160 }
161 // Make this reference to the physical shape go away since native shapes are not shared.
162 public override void Dereference(BSScene physicsScene)
163 {
164 // Native shapes are not tracked and are released immediately
165 physicsScene.DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this);
166 BulletSimAPI.DeleteCollisionShape2(physicsScene.World.ptr, ptr);
167 ptr = null;
168 // Garbage collection will free up this instance.
169 }
170}
171
172public class BSShapeMesh : BSShape
173{
174 private static string LogHeader = "[BULLETSIM SHAPE MESH]";
175 private static Dictionary<System.UInt64, BSShapeMesh> Meshes = new Dictionary<System.UInt64, BSShapeMesh>();
176
177 public BSShapeMesh() : base()
178 {
179 }
180 public static BSShape GetReference() { return new BSShapeNull(); }
181 public override void Dereference(BSScene physicsScene) { }
182}
183
184public class BSShapeHull : BSShape
185{
186 private static string LogHeader = "[BULLETSIM SHAPE HULL]";
187 private static Dictionary<System.UInt64, BSShapeHull> Hulls = new Dictionary<System.UInt64, BSShapeHull>();
188
189 public BSShapeHull() : base()
190 {
191 }
192 public static BSShape GetReference() { return new BSShapeNull(); }
193 public override void Dereference(BSScene physicsScene) { }
194}
195
196public class BSShapeCompound : BSShape
197{
198 private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]";
199 public BSShapeCompound() : base()
200 {
201 }
202 public static BSShape GetReference(BSPhysObject prim)
203 {
204 return new BSShapeNull();
205 }
206 public override void Dereference(BSScene physicsScene) { }
207}
208}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainHeightmap.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainHeightmap.cs
new file mode 100644
index 0000000..252953b
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainHeightmap.cs
@@ -0,0 +1,175 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OpenSim.Framework;
32using OpenSim.Region.Framework;
33using OpenSim.Region.CoreModules;
34using OpenSim.Region.Physics.Manager;
35
36using Nini.Config;
37using log4net;
38
39using OpenMetaverse;
40
41namespace OpenSim.Region.Physics.BulletSNPlugin
42{
43public sealed class BSTerrainHeightmap : BSTerrainPhys
44{
45 static string LogHeader = "[BULLETSIM TERRAIN HEIGHTMAP]";
46
47 BulletHeightMapInfo m_mapInfo = null;
48
49 // Constructor to build a default, flat heightmap terrain.
50 public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
51 : base(physicsScene, regionBase, id)
52 {
53 Vector3 minTerrainCoords = new Vector3(0f, 0f, BSTerrainManager.HEIGHT_INITIALIZATION - BSTerrainManager.HEIGHT_EQUAL_FUDGE);
54 Vector3 maxTerrainCoords = new Vector3(regionSize.X, regionSize.Y, BSTerrainManager.HEIGHT_INITIALIZATION);
55 int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y;
56 float[] initialMap = new float[totalHeights];
57 for (int ii = 0; ii < totalHeights; ii++)
58 {
59 initialMap[ii] = BSTerrainManager.HEIGHT_INITIALIZATION;
60 }
61 m_mapInfo = new BulletHeightMapInfo(id, initialMap, null);
62 m_mapInfo.minCoords = minTerrainCoords;
63 m_mapInfo.maxCoords = maxTerrainCoords;
64 m_mapInfo.terrainRegionBase = TerrainBase;
65 // Don't have to free any previous since we just got here.
66 BuildHeightmapTerrain();
67 }
68
69 // This minCoords and maxCoords passed in give the size of the terrain (min and max Z
70 // are the high and low points of the heightmap).
71 public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
72 Vector3 minCoords, Vector3 maxCoords)
73 : base(physicsScene, regionBase, id)
74 {
75 m_mapInfo = new BulletHeightMapInfo(id, initialMap, null);
76 m_mapInfo.minCoords = minCoords;
77 m_mapInfo.maxCoords = maxCoords;
78 m_mapInfo.minZ = minCoords.Z;
79 m_mapInfo.maxZ = maxCoords.Z;
80 m_mapInfo.terrainRegionBase = TerrainBase;
81
82 // Don't have to free any previous since we just got here.
83 BuildHeightmapTerrain();
84 }
85
86 public override void Dispose()
87 {
88 ReleaseHeightMapTerrain();
89 }
90
91 // Using the information in m_mapInfo, create the physical representation of the heightmap.
92 private void BuildHeightmapTerrain()
93 {
94 m_mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, m_mapInfo.ID,
95 m_mapInfo.minCoords, m_mapInfo.maxCoords,
96 m_mapInfo.heightMap, BSParam.TerrainCollisionMargin);
97
98 // Create the terrain shape from the mapInfo
99 m_mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(m_mapInfo.Ptr),
100 BSPhysicsShapeType.SHAPE_TERRAIN);
101
102 // The terrain object initial position is at the center of the object
103 Vector3 centerPos;
104 centerPos.X = m_mapInfo.minCoords.X + (m_mapInfo.sizeX / 2f);
105 centerPos.Y = m_mapInfo.minCoords.Y + (m_mapInfo.sizeY / 2f);
106 centerPos.Z = m_mapInfo.minZ + ((m_mapInfo.maxZ - m_mapInfo.minZ) / 2f + 0.5f);
107
108 m_mapInfo.terrainBody = new BulletBody(m_mapInfo.ID,
109 BulletSimAPI.CreateBodyWithDefaultMotionState2(m_mapInfo.terrainShape.ptr,
110 m_mapInfo.ID, centerPos, Quaternion.Identity));
111
112 // Set current terrain attributes
113 BulletSimAPI.SetFriction2(m_mapInfo.terrainBody.ptr, BSParam.TerrainFriction);
114 BulletSimAPI.SetHitFraction2(m_mapInfo.terrainBody.ptr, BSParam.TerrainHitFraction);
115 BulletSimAPI.SetRestitution2(m_mapInfo.terrainBody.ptr, BSParam.TerrainRestitution);
116 BulletSimAPI.SetCollisionFlags2(m_mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
117
118 // Return the new terrain to the world of physical objects
119 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr, centerPos, Quaternion.Identity);
120
121 // redo its bounding box now that it is in the world
122 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
123
124 m_mapInfo.terrainBody.collisionType = CollisionType.Terrain;
125 m_mapInfo.terrainBody.ApplyCollisionMask();
126
127 // Make it so the terrain will not move or be considered for movement.
128 BulletSimAPI.ForceActivationState2(m_mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
129
130 return;
131 }
132
133 // If there is information in m_mapInfo pointing to physical structures, release same.
134 private void ReleaseHeightMapTerrain()
135 {
136 if (m_mapInfo != null)
137 {
138 if (m_mapInfo.terrainBody.HasPhysicalBody)
139 {
140 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
141 // Frees both the body and the shape.
142 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
143 BulletSimAPI.ReleaseHeightMapInfo2(m_mapInfo.Ptr);
144 }
145 }
146 m_mapInfo = null;
147 }
148
149 // The passed position is relative to the base of the region.
150 public override float GetTerrainHeightAtXYZ(Vector3 pos)
151 {
152 float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
153
154 int mapIndex = (int)pos.Y * (int)m_mapInfo.sizeY + (int)pos.X;
155 try
156 {
157 ret = m_mapInfo.heightMap[mapIndex];
158 }
159 catch
160 {
161 // Sometimes they give us wonky values of X and Y. Give a warning and return something.
162 PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
163 LogHeader, m_mapInfo.terrainRegionBase, pos);
164 ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
165 }
166 return ret;
167 }
168
169 // The passed position is relative to the base of the region.
170 public override float GetWaterLevelAtXYZ(Vector3 pos)
171 {
172 return PhysicsScene.SimpleWaterLevel;
173 }
174}
175}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainManager.cs
new file mode 100644
index 0000000..dfad70e
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainManager.cs
@@ -0,0 +1,460 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OpenSim.Framework;
32using OpenSim.Region.Framework;
33using OpenSim.Region.CoreModules;
34using OpenSim.Region.Physics.Manager;
35
36using Nini.Config;
37using log4net;
38
39using OpenMetaverse;
40
41namespace OpenSim.Region.Physics.BulletSNPlugin
42{
43
44// The physical implementation of the terrain is wrapped in this class.
45public abstract class BSTerrainPhys : IDisposable
46{
47 public enum TerrainImplementation
48 {
49 Heightmap = 0,
50 Mesh = 1
51 }
52
53 public BSScene PhysicsScene { get; private set; }
54 // Base of the region in world coordinates. Coordinates inside the region are relative to this.
55 public Vector3 TerrainBase { get; private set; }
56 public uint ID { get; private set; }
57
58 public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id)
59 {
60 PhysicsScene = physicsScene;
61 TerrainBase = regionBase;
62 ID = id;
63 }
64 public abstract void Dispose();
65 public abstract float GetTerrainHeightAtXYZ(Vector3 pos);
66 public abstract float GetWaterLevelAtXYZ(Vector3 pos);
67}
68
69// ==========================================================================================
70public sealed class BSTerrainManager : IDisposable
71{
72 static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
73
74 // These height values are fractional so the odd values will be
75 // noticable when debugging.
76 public const float HEIGHT_INITIALIZATION = 24.987f;
77 public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f;
78 public const float HEIGHT_GETHEIGHT_RET = 24.765f;
79 public const float WATER_HEIGHT_GETHEIGHT_RET = 19.998f;
80
81 // If the min and max height are equal, we reduce the min by this
82 // amount to make sure that a bounding box is built for the terrain.
83 public const float HEIGHT_EQUAL_FUDGE = 0.2f;
84
85 // Until the whole simulator is changed to pass us the region size, we rely on constants.
86 public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
87
88 // The scene that I am part of
89 private BSScene PhysicsScene { get; set; }
90
91 // The ground plane created to keep thing from falling to infinity.
92 private BulletBody m_groundPlane;
93
94 // If doing mega-regions, if we're region zero we will be managing multiple
95 // region terrains since region zero does the physics for the whole mega-region.
96 private Dictionary<Vector3, BSTerrainPhys> m_terrains;
97
98 // Flags used to know when to recalculate the height.
99 private bool m_terrainModified = false;
100
101 // If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount.
102 // This is incremented before assigning to new region so it is the last ID allocated.
103 private uint m_terrainCount = BSScene.CHILDTERRAIN_ID - 1;
104 public uint HighestTerrainID { get {return m_terrainCount; } }
105
106 // If doing mega-regions, this holds our offset from region zero of
107 // the mega-regions. "parentScene" points to the PhysicsScene of region zero.
108 private Vector3 m_worldOffset;
109 // If the parent region (region 0), this is the extent of the combined regions
110 // relative to the origin of region zero
111 private Vector3 m_worldMax;
112 private PhysicsScene MegaRegionParentPhysicsScene { get; set; }
113
114 public BSTerrainManager(BSScene physicsScene)
115 {
116 PhysicsScene = physicsScene;
117 m_terrains = new Dictionary<Vector3,BSTerrainPhys>();
118
119 // Assume one region of default size
120 m_worldOffset = Vector3.Zero;
121 m_worldMax = new Vector3(DefaultRegionSize);
122 MegaRegionParentPhysicsScene = null;
123 }
124
125 public void Dispose()
126 {
127 ReleaseGroundPlaneAndTerrain();
128 }
129
130 // Create the initial instance of terrain and the underlying ground plane.
131 // This is called from the initialization routine so we presume it is
132 // safe to call Bullet in real time. We hope no one is moving prims around yet.
133 public void CreateInitialGroundPlaneAndTerrain()
134 {
135 // The ground plane is here to catch things that are trying to drop to negative infinity
136 BulletShape groundPlaneShape = new BulletShape(
137 BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f,
138 BSParam.TerrainCollisionMargin),
139 BSPhysicsShapeType.SHAPE_GROUNDPLANE);
140 m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
141 BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
142 Vector3.Zero, Quaternion.Identity));
143 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr);
144 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_groundPlane.ptr);
145 // Ground plane does not move
146 BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION);
147 // Everything collides with the ground plane.
148 m_groundPlane.collisionType = CollisionType.Groundplane;
149 m_groundPlane.ApplyCollisionMask();
150
151 // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
152 BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize);
153 m_terrains.Add(Vector3.Zero, initialTerrain);
154 }
155
156 // Release all the terrain structures we might have allocated
157 public void ReleaseGroundPlaneAndTerrain()
158 {
159 if (m_groundPlane.HasPhysicalBody)
160 {
161 if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr))
162 {
163 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_groundPlane.ptr);
164 }
165 m_groundPlane.Clear();
166 }
167
168 ReleaseTerrain();
169 }
170
171 // Release all the terrain we have allocated
172 public void ReleaseTerrain()
173 {
174 lock (m_terrains)
175 {
176 foreach (KeyValuePair<Vector3, BSTerrainPhys> kvp in m_terrains)
177 {
178 kvp.Value.Dispose();
179 }
180 m_terrains.Clear();
181 }
182 }
183
184 // The simulator wants to set a new heightmap for the terrain.
185 public void SetTerrain(float[] heightMap) {
186 float[] localHeightMap = heightMap;
187 // If there are multiple requests for changes to the same terrain between ticks,
188 // only do that last one.
189 PhysicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate()
190 {
191 if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null)
192 {
193 // If a child of a mega-region, we shouldn't have any terrain allocated for us
194 ReleaseGroundPlaneAndTerrain();
195 // If doing the mega-prim stuff and we are the child of the zero region,
196 // the terrain is added to our parent
197 if (MegaRegionParentPhysicsScene is BSScene)
198 {
199 DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}",
200 BSScene.DetailLogZero, m_worldOffset, m_worldMax);
201 ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateTerrain(
202 BSScene.CHILDTERRAIN_ID, localHeightMap,
203 m_worldOffset, m_worldOffset + DefaultRegionSize, true);
204 }
205 }
206 else
207 {
208 // If not doing the mega-prim thing, just change the terrain
209 DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
210
211 UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap,
212 m_worldOffset, m_worldOffset + DefaultRegionSize, true);
213 }
214 });
215 }
216
217 // If called with no mapInfo for the terrain, this will create a new mapInfo and terrain
218 // based on the passed information. The 'id' should be either the terrain id or
219 // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used.
220 // The latter feature is for creating child terrains for mega-regions.
221 // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new
222 // terrain shape is created and added to the body.
223 // This call is most often used to update the heightMap and parameters of the terrain.
224 // (The above does suggest that some simplification/refactoring is in order.)
225 // Called during taint-time.
226 private void UpdateTerrain(uint id, float[] heightMap,
227 Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
228 {
229 DetailLog("{0},BSTerrainManager.UpdateTerrain,call,minC={1},maxC={2},inTaintTime={3}",
230 BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime);
231
232 // Find high and low points of passed heightmap.
233 // The min and max passed in is usually the area objects can be in (maximum
234 // object height, for instance). The terrain wants the bounding box for the
235 // terrain so replace passed min and max Z with the actual terrain min/max Z.
236 float minZ = float.MaxValue;
237 float maxZ = float.MinValue;
238 foreach (float height in heightMap)
239 {
240 if (height < minZ) minZ = height;
241 if (height > maxZ) maxZ = height;
242 }
243 if (minZ == maxZ)
244 {
245 // If min and max are the same, reduce min a little bit so a good bounding box is created.
246 minZ -= BSTerrainManager.HEIGHT_EQUAL_FUDGE;
247 }
248 minCoords.Z = minZ;
249 maxCoords.Z = maxZ;
250
251 Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f);
252
253 lock (m_terrains)
254 {
255 BSTerrainPhys terrainPhys;
256 if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
257 {
258 // There is already a terrain in this spot. Free the old and build the new.
259 DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}",
260 BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
261
262 // Remove old terrain from the collection
263 m_terrains.Remove(terrainRegionBase);
264 // Release any physical memory it may be using.
265 terrainPhys.Dispose();
266
267 if (MegaRegionParentPhysicsScene == null)
268 {
269 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
270 m_terrains.Add(terrainRegionBase, newTerrainPhys);
271
272 m_terrainModified = true;
273 }
274 else
275 {
276 // It's possible that Combine() was called after this code was queued.
277 // If we are a child of combined regions, we don't create any terrain for us.
278 DetailLog("{0},BSTerrainManager.UpdateTerrain:AmACombineChild,taint", BSScene.DetailLogZero);
279
280 // Get rid of any terrain that may have been allocated for us.
281 ReleaseGroundPlaneAndTerrain();
282
283 // I hate doing this, but just bail
284 return;
285 }
286 }
287 else
288 {
289 // We don't know about this terrain so either we are creating a new terrain or
290 // our mega-prim child is giving us a new terrain to add to the phys world
291
292 // if this is a child terrain, calculate a unique terrain id
293 uint newTerrainID = id;
294 if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
295 newTerrainID = ++m_terrainCount;
296
297 DetailLog("{0},UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}",
298 BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
299 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
300 m_terrains.Add(terrainRegionBase, newTerrainPhys);
301
302 m_terrainModified = true;
303 }
304 }
305 }
306
307 // TODO: redo terrain implementation selection to allow other base types than heightMap.
308 private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords)
309 {
310 PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}",
311 LogHeader, PhysicsScene.RegionName, terrainRegionBase,
312 (BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation);
313 BSTerrainPhys newTerrainPhys = null;
314 switch ((int)BSParam.TerrainImplementation)
315 {
316 case (int)BSTerrainPhys.TerrainImplementation.Heightmap:
317 newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id,
318 heightMap, minCoords, maxCoords);
319 break;
320 case (int)BSTerrainPhys.TerrainImplementation.Mesh:
321 newTerrainPhys = new BSTerrainMesh(PhysicsScene, terrainRegionBase, id,
322 heightMap, minCoords, maxCoords);
323 break;
324 default:
325 PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}",
326 LogHeader,
327 (int)BSParam.TerrainImplementation,
328 BSParam.TerrainImplementation,
329 PhysicsScene.RegionName, terrainRegionBase);
330 break;
331 }
332 return newTerrainPhys;
333 }
334
335 // Return 'true' of this position is somewhere in known physical terrain space
336 public bool IsWithinKnownTerrain(Vector3 pos)
337 {
338 Vector3 terrainBaseXYZ;
339 BSTerrainPhys physTerrain;
340 return GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ);
341 }
342
343 // Given an X and Y, find the height of the terrain.
344 // Since we could be handling multiple terrains for a mega-region,
345 // the base of the region is calcuated assuming all regions are
346 // the same size and that is the default.
347 // Once the heightMapInfo is found, we have all the information to
348 // compute the offset into the array.
349 private float lastHeightTX = 999999f;
350 private float lastHeightTY = 999999f;
351 private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT;
352 public float GetTerrainHeightAtXYZ(Vector3 pos)
353 {
354 float tX = pos.X;
355 float tY = pos.Y;
356 // You'd be surprized at the number of times this routine is called
357 // with the same parameters as last time.
358 if (!m_terrainModified && (lastHeightTX == tX) && (lastHeightTY == tY))
359 return lastHeight;
360 m_terrainModified = false;
361
362 lastHeightTX = tX;
363 lastHeightTY = tY;
364 float ret = HEIGHT_GETHEIGHT_RET;
365
366 Vector3 terrainBaseXYZ;
367 BSTerrainPhys physTerrain;
368 if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
369 {
370 ret = physTerrain.GetTerrainHeightAtXYZ(pos - terrainBaseXYZ);
371 }
372 else
373 {
374 PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
375 LogHeader, PhysicsScene.RegionName, tX, tY);
376 DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,terrainNotFound,pos={1},base={2}",
377 BSScene.DetailLogZero, pos, terrainBaseXYZ);
378 }
379 lastHeight = ret;
380 return ret;
381 }
382
383 public float GetWaterLevelAtXYZ(Vector3 pos)
384 {
385 float ret = WATER_HEIGHT_GETHEIGHT_RET;
386
387 Vector3 terrainBaseXYZ;
388 BSTerrainPhys physTerrain;
389 if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
390 {
391 ret = physTerrain.GetWaterLevelAtXYZ(pos);
392 }
393 else
394 {
395 PhysicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}",
396 LogHeader, PhysicsScene.RegionName, pos, terrainBaseXYZ, ret);
397 }
398 return ret;
399 }
400
401 // Given an address, return 'true' of there is a description of that terrain and output
402 // the descriptor class and the 'base' fo the addresses therein.
403 private bool GetTerrainPhysicalAtXYZ(Vector3 pos, out BSTerrainPhys outPhysTerrain, out Vector3 outTerrainBase)
404 {
405 int offsetX = ((int)(pos.X / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
406 int offsetY = ((int)(pos.Y / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
407 Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
408
409 BSTerrainPhys physTerrain = null;
410 lock (m_terrains)
411 {
412 m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain);
413 }
414 outTerrainBase = terrainBaseXYZ;
415 outPhysTerrain = physTerrain;
416 return (physTerrain != null);
417 }
418
419 // Although no one seems to check this, I do support combining.
420 public bool SupportsCombining()
421 {
422 return true;
423 }
424
425 // This routine is called two ways:
426 // One with 'offset' and 'pScene' zero and null but 'extents' giving the maximum
427 // extent of the combined regions. This is to inform the parent of the size
428 // of the combined regions.
429 // and one with 'offset' as the offset of the child region to the base region,
430 // 'pScene' pointing to the parent and 'extents' of zero. This informs the
431 // child of its relative base and new parent.
432 public void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
433 {
434 m_worldOffset = offset;
435 m_worldMax = extents;
436 MegaRegionParentPhysicsScene = pScene;
437 if (pScene != null)
438 {
439 // We are a child.
440 // We want m_worldMax to be the highest coordinate of our piece of terrain.
441 m_worldMax = offset + DefaultRegionSize;
442 }
443 DetailLog("{0},BSTerrainManager.Combine,offset={1},extents={2},wOffset={3},wMax={4}",
444 BSScene.DetailLogZero, offset, extents, m_worldOffset, m_worldMax);
445 }
446
447 // Unhook all the combining that I know about.
448 public void UnCombine(PhysicsScene pScene)
449 {
450 // Just like ODE, we don't do anything yet.
451 DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero);
452 }
453
454
455 private void DetailLog(string msg, params Object[] args)
456 {
457 PhysicsScene.PhysicsLogging.Write(msg, args);
458 }
459}
460}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainMesh.cs
new file mode 100644
index 0000000..6083dd4
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainMesh.cs
@@ -0,0 +1,267 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OpenSim.Framework;
32using OpenSim.Region.Framework;
33using OpenSim.Region.CoreModules;
34using OpenSim.Region.Physics.Manager;
35
36using Nini.Config;
37using log4net;
38
39using OpenMetaverse;
40
41namespace OpenSim.Region.Physics.BulletSNPlugin
42{
43public sealed class BSTerrainMesh : BSTerrainPhys
44{
45 static string LogHeader = "[BULLETSIM TERRAIN MESH]";
46
47 private float[] m_savedHeightMap;
48 int m_sizeX;
49 int m_sizeY;
50
51 BulletShape m_terrainShape;
52 BulletBody m_terrainBody;
53
54 public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
55 : base(physicsScene, regionBase, id)
56 {
57 }
58
59 public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id /* parameters for making mesh */)
60 : base(physicsScene, regionBase, id)
61 {
62 }
63
64 // Create terrain mesh from a heightmap.
65 public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
66 Vector3 minCoords, Vector3 maxCoords)
67 : base(physicsScene, regionBase, id)
68 {
69 int indicesCount;
70 int[] indices;
71 int verticesCount;
72 float[] vertices;
73
74 m_savedHeightMap = initialMap;
75
76 m_sizeX = (int)(maxCoords.X - minCoords.X);
77 m_sizeY = (int)(maxCoords.Y - minCoords.Y);
78
79 if (!BSTerrainMesh.ConvertHeightmapToMesh(PhysicsScene, initialMap,
80 m_sizeX, m_sizeY,
81 (float)m_sizeX, (float)m_sizeY,
82 Vector3.Zero, 1.0f,
83 out indicesCount, out indices, out verticesCount, out vertices))
84 {
85 // DISASTER!!
86 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap", ID);
87 PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase);
88 // Something is very messed up and a crash is in our future.
89 return;
90 }
91 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,indices={1},indSz={2},vertices={3},vertSz={4}",
92 ID, indicesCount, indices.Length, verticesCount, vertices.Length);
93
94 m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
95 indicesCount, indices, verticesCount, vertices),
96 BSPhysicsShapeType.SHAPE_MESH);
97 if (!m_terrainShape.HasPhysicalShape)
98 {
99 // DISASTER!!
100 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID);
101 physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase);
102 // Something is very messed up and a crash is in our future.
103 return;
104 }
105
106 Vector3 pos = regionBase;
107 Quaternion rot = Quaternion.Identity;
108
109 m_terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2( m_terrainShape.ptr, ID, pos, rot));
110 if (!m_terrainBody.HasPhysicalBody)
111 {
112 // DISASTER!!
113 physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase);
114 // Something is very messed up and a crash is in our future.
115 return;
116 }
117
118 // Set current terrain attributes
119 BulletSimAPI.SetFriction2(m_terrainBody.ptr, BSParam.TerrainFriction);
120 BulletSimAPI.SetHitFraction2(m_terrainBody.ptr, BSParam.TerrainHitFraction);
121 BulletSimAPI.SetRestitution2(m_terrainBody.ptr, BSParam.TerrainRestitution);
122 BulletSimAPI.SetCollisionFlags2(m_terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
123
124 // Static objects are not very massive.
125 BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero);
126
127 // Put the new terrain to the world of physical objects
128 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr, pos, rot);
129
130 // Redo its bounding box now that it is in the world
131 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr);
132
133 m_terrainBody.collisionType = CollisionType.Terrain;
134 m_terrainBody.ApplyCollisionMask();
135
136 // Make it so the terrain will not move or be considered for movement.
137 BulletSimAPI.ForceActivationState2(m_terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
138 }
139
140 public override void Dispose()
141 {
142 if (m_terrainBody.HasPhysicalBody)
143 {
144 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
145 // Frees both the body and the shape.
146 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_terrainBody.ptr);
147 }
148 }
149
150 public override float GetTerrainHeightAtXYZ(Vector3 pos)
151 {
152 // For the moment use the saved heightmap to get the terrain height.
153 // TODO: raycast downward to find the true terrain below the position.
154 float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
155
156 int mapIndex = (int)pos.Y * m_sizeY + (int)pos.X;
157 try
158 {
159 ret = m_savedHeightMap[mapIndex];
160 }
161 catch
162 {
163 // Sometimes they give us wonky values of X and Y. Give a warning and return something.
164 PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
165 LogHeader, TerrainBase, pos);
166 ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
167 }
168 return ret;
169 }
170
171 // The passed position is relative to the base of the region.
172 public override float GetWaterLevelAtXYZ(Vector3 pos)
173 {
174 return PhysicsScene.SimpleWaterLevel;
175 }
176
177 // Convert the passed heightmap to mesh information suitable for CreateMeshShape2().
178 // Return 'true' if successfully created.
179 public static bool ConvertHeightmapToMesh(
180 BSScene physicsScene,
181 float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap
182 float extentX, float extentY, // zero based range for output vertices
183 Vector3 extentBase, // base to be added to all vertices
184 float magnification, // number of vertices to create between heightMap coords
185 out int indicesCountO, out int[] indicesO,
186 out int verticesCountO, out float[] verticesO)
187 {
188 bool ret = false;
189
190 int indicesCount = 0;
191 int verticesCount = 0;
192 int[] indices = new int[0];
193 float[] vertices = new float[0];
194
195 // Simple mesh creation which assumes magnification == 1.
196 // TODO: do a more general solution that scales, adds new vertices and smoothes the result.
197
198 // Create an array of vertices that is sizeX+1 by sizeY+1 (note the loop
199 // from zero to <= sizeX). The triangle indices are then generated as two triangles
200 // per heightmap point. There are sizeX by sizeY of these squares. The extra row and
201 // column of vertices are used to complete the triangles of the last row and column
202 // of the heightmap.
203 try
204 {
205 // One vertice per heightmap value plus the vertices off the top and bottom edge.
206 int totalVertices = (sizeX + 1) * (sizeY + 1);
207 vertices = new float[totalVertices * 3];
208 int totalIndices = sizeX * sizeY * 6;
209 indices = new int[totalIndices];
210
211 float magX = (float)sizeX / extentX;
212 float magY = (float)sizeY / extentY;
213 physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}",
214 BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY);
215 float minHeight = float.MaxValue;
216 // Note that sizeX+1 vertices are created since there is land between this and the next region.
217 for (int yy = 0; yy <= sizeY; yy++)
218 {
219 for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we go around sizeX + 1 times
220 {
221 int offset = yy * sizeX + xx;
222 // Extend the height with the height from the last row or column
223 if (yy == sizeY) offset -= sizeX;
224 if (xx == sizeX) offset -= 1;
225 float height = heightMap[offset];
226 minHeight = Math.Min(minHeight, height);
227 vertices[verticesCount + 0] = (float)xx * magX + extentBase.X;
228 vertices[verticesCount + 1] = (float)yy * magY + extentBase.Y;
229 vertices[verticesCount + 2] = height + extentBase.Z;
230 verticesCount += 3;
231 }
232 }
233 verticesCount = verticesCount / 3;
234
235 for (int yy = 0; yy < sizeY; yy++)
236 {
237 for (int xx = 0; xx < sizeX; xx++)
238 {
239 int offset = yy * (sizeX + 1) + xx;
240 // Each vertices is presumed to be the upper left corner of a box of two triangles
241 indices[indicesCount + 0] = offset;
242 indices[indicesCount + 1] = offset + 1;
243 indices[indicesCount + 2] = offset + sizeX + 1; // accounting for the extra column
244 indices[indicesCount + 3] = offset + 1;
245 indices[indicesCount + 4] = offset + sizeX + 2;
246 indices[indicesCount + 5] = offset + sizeX + 1;
247 indicesCount += 6;
248 }
249 }
250
251 ret = true;
252 }
253 catch (Exception e)
254 {
255 physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}",
256 LogHeader, physicsScene.RegionName, extentBase, e);
257 }
258
259 indicesCountO = indicesCount;
260 indicesO = indices;
261 verticesCountO = verticesCount;
262 verticesO = vertices;
263
264 return ret;
265 }
266}
267}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSNPlugin/BulletSimAPI.cs
new file mode 100644
index 0000000..6af59d6
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BulletSimAPI.cs
@@ -0,0 +1,1604 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.IO;
30using System.Runtime.InteropServices;
31using System.Security;
32using System.Text;
33using BulletXNA;
34using OpenMetaverse;
35using BulletXNA.LinearMath;
36using BulletXNA.BulletCollision;
37using BulletXNA.BulletDynamics;
38using BulletXNA.BulletCollision.CollisionDispatch;
39using OpenSim.Framework;
40
41namespace OpenSim.Region.Physics.BulletSNPlugin {
42
43// Classes to allow some type checking for the API
44// These hold pointers to allocated objects in the unmanaged space.
45
46
47
48 // Constraint type values as defined by Bullet
49public enum ConstraintType : int
50{
51 POINT2POINT_CONSTRAINT_TYPE = 3,
52 HINGE_CONSTRAINT_TYPE,
53 CONETWIST_CONSTRAINT_TYPE,
54 D6_CONSTRAINT_TYPE,
55 SLIDER_CONSTRAINT_TYPE,
56 CONTACT_CONSTRAINT_TYPE,
57 D6_SPRING_CONSTRAINT_TYPE,
58 MAX_CONSTRAINT_TYPE
59}
60
61
62// ===============================================================================
63[StructLayout(LayoutKind.Sequential)]
64public struct ConvexHull
65{
66 Vector3 Offset;
67 int VertexCount;
68 Vector3[] Vertices;
69}
70public enum BSPhysicsShapeType
71{
72 SHAPE_UNKNOWN = 0,
73 SHAPE_CAPSULE = 1,
74 SHAPE_BOX = 2,
75 SHAPE_CONE = 3,
76 SHAPE_CYLINDER = 4,
77 SHAPE_SPHERE = 5,
78 SHAPE_MESH = 6,
79 SHAPE_HULL = 7,
80 // following defined by BulletSim
81 SHAPE_GROUNDPLANE = 20,
82 SHAPE_TERRAIN = 21,
83 SHAPE_COMPOUND = 22,
84 SHAPE_HEIGHTMAP = 23,
85};
86
87// The native shapes have predefined shape hash keys
88public enum FixedShapeKey : ulong
89{
90 KEY_NONE = 0,
91 KEY_BOX = 1,
92 KEY_SPHERE = 2,
93 KEY_CONE = 3,
94 KEY_CYLINDER = 4,
95 KEY_CAPSULE = 5,
96}
97
98[StructLayout(LayoutKind.Sequential)]
99public struct ShapeData
100{
101 public uint ID;
102 public BSPhysicsShapeType Type;
103 public Vector3 Position;
104 public Quaternion Rotation;
105 public Vector3 Velocity;
106 public Vector3 Scale;
107 public float Mass;
108 public float Buoyancy;
109 public System.UInt64 HullKey;
110 public System.UInt64 MeshKey;
111 public float Friction;
112 public float Restitution;
113 public float Collidable; // true of things bump into this
114 public float Static; // true if a static object. Otherwise gravity, etc.
115 public float Solid; // true if object cannot be passed through
116 public Vector3 Size;
117
118 // note that bools are passed as floats since bool size changes by language and architecture
119 public const float numericTrue = 1f;
120 public const float numericFalse = 0f;
121}
122[StructLayout(LayoutKind.Sequential)]
123public struct SweepHit
124{
125 public uint ID;
126 public float Fraction;
127 public Vector3 Normal;
128 public Vector3 Point;
129}
130[StructLayout(LayoutKind.Sequential)]
131public struct RaycastHit
132{
133 public uint ID;
134 public float Fraction;
135 public Vector3 Normal;
136}
137[StructLayout(LayoutKind.Sequential)]
138public struct CollisionDesc
139{
140 public uint aID;
141 public uint bID;
142 public Vector3 point;
143 public Vector3 normal;
144}
145[StructLayout(LayoutKind.Sequential)]
146public struct EntityProperties
147{
148 public uint ID;
149 public Vector3 Position;
150 public Quaternion Rotation;
151 public Vector3 Velocity;
152 public Vector3 Acceleration;
153 public Vector3 RotationalVelocity;
154 public override string ToString()
155 {
156 return string.Format("ID:{0}, Pos:<{1:F},{2:F},{3:F}>, Rot:<{4:F},{5:F},{6:F},{7:F}>, LVel:<{8:F},{9:F},{10:F}>, AVel:<{11:F},{12:F},{13:F}>",
157 ID.ToString(),
158 Position.X,Position.Y,Position.Z,
159 Rotation.X,Rotation.Y,Rotation.Z,Rotation.W,
160 Velocity.X,Velocity.Y,Velocity.Z,
161 RotationalVelocity.X,RotationalVelocity.Y,RotationalVelocity.Z
162 );
163 }
164}
165
166// Format of this structure must match the definition in the C++ code
167// NOTE: adding the X causes compile breaks if used. These are unused symbols
168// that can be removed from both here and the unmanaged definition of this structure.
169[StructLayout(LayoutKind.Sequential)]
170public struct ConfigurationParameters
171{
172 public float defaultFriction;
173 public float defaultDensity;
174 public float defaultRestitution;
175 public float collisionMargin;
176 public float gravity;
177
178 public float XlinearDamping;
179 public float XangularDamping;
180 public float XdeactivationTime;
181 public float XlinearSleepingThreshold;
182 public float XangularSleepingThreshold;
183 public float XccdMotionThreshold;
184 public float XccdSweptSphereRadius;
185 public float XcontactProcessingThreshold;
186
187 public float XterrainImplementation;
188 public float XterrainFriction;
189 public float XterrainHitFraction;
190 public float XterrainRestitution;
191 public float XterrainCollisionMargin;
192
193 public float XavatarFriction;
194 public float XavatarStandingFriction;
195 public float XavatarDensity;
196 public float XavatarRestitution;
197 public float XavatarCapsuleWidth;
198 public float XavatarCapsuleDepth;
199 public float XavatarCapsuleHeight;
200 public float XavatarContactProcessingThreshold;
201
202 public float XvehicleAngularDamping;
203
204 public float maxPersistantManifoldPoolSize;
205 public float maxCollisionAlgorithmPoolSize;
206 public float shouldDisableContactPoolDynamicAllocation;
207 public float shouldForceUpdateAllAabbs;
208 public float shouldRandomizeSolverOrder;
209 public float shouldSplitSimulationIslands;
210 public float shouldEnableFrictionCaching;
211 public float numberOfSolverIterations;
212
213 public float XlinksetImplementation;
214 public float XlinkConstraintUseFrameOffset;
215 public float XlinkConstraintEnableTransMotor;
216 public float XlinkConstraintTransMotorMaxVel;
217 public float XlinkConstraintTransMotorMaxForce;
218 public float XlinkConstraintERP;
219 public float XlinkConstraintCFM;
220 public float XlinkConstraintSolverIterations;
221
222 public float physicsLoggingFrames;
223
224 public const float numericTrue = 1f;
225 public const float numericFalse = 0f;
226}
227
228
229// The states a bullet collision object can have
230
231public enum ActivationState : uint
232{
233 UNDEFINED = 0,
234 ACTIVE_TAG = 1,
235 ISLAND_SLEEPING = 2,
236 WANTS_DEACTIVATION = 3,
237 DISABLE_DEACTIVATION = 4,
238 DISABLE_SIMULATION = 5,
239}
240
241public enum CollisionObjectTypes : int
242{
243 CO_COLLISION_OBJECT = 1 << 0,
244 CO_RIGID_BODY = 1 << 1,
245 CO_GHOST_OBJECT = 1 << 2,
246 CO_SOFT_BODY = 1 << 3,
247 CO_HF_FLUID = 1 << 4,
248 CO_USER_TYPE = 1 << 5,
249}
250
251// Values used by Bullet and BulletSim to control object properties.
252// Bullet's "CollisionFlags" has more to do with operations on the
253// object (if collisions happen, if gravity effects it, ...).
254 [Flags]
255public enum CollisionFlags : uint
256{
257 CF_STATIC_OBJECT = 1 << 0,
258 CF_KINEMATIC_OBJECT = 1 << 1,
259 CF_NO_CONTACT_RESPONSE = 1 << 2,
260 CF_CUSTOM_MATERIAL_CALLBACK = 1 << 3,
261 CF_CHARACTER_OBJECT = 1 << 4,
262 CF_DISABLE_VISUALIZE_OBJECT = 1 << 5,
263 CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6,
264 // Following used by BulletSim to control collisions and updates
265 BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10,
266 BS_FLOATS_ON_WATER = 1 << 11,
267 BS_VEHICLE_COLLISIONS = 1 << 12,
268 BS_NONE = 0,
269 BS_ALL = 0xFFFFFFFF,
270
271 // These are the collision flags switched depending on physical state.
272 // The other flags are used for other things and should not be fooled with.
273 BS_ACTIVE = CF_STATIC_OBJECT
274 | CF_KINEMATIC_OBJECT
275 | CF_NO_CONTACT_RESPONSE
276};
277
278// Values for collisions groups and masks
279public enum CollisionFilterGroups : uint
280{
281 // Don't use the bit definitions!! Define the use in a
282 // filter/mask definition below. This way collision interactions
283 // are more easily debugged.
284 BNoneGroup = 0,
285 BDefaultGroup = 1 << 0,
286 BStaticGroup = 1 << 1,
287 BKinematicGroup = 1 << 2,
288 BDebrisGroup = 1 << 3,
289 BSensorTrigger = 1 << 4,
290 BCharacterGroup = 1 << 5,
291 BAllGroup = 0xFFFFFFFF,
292 // Filter groups defined by BulletSim
293 BGroundPlaneGroup = 1 << 10,
294 BTerrainGroup = 1 << 11,
295 BRaycastGroup = 1 << 12,
296 BSolidGroup = 1 << 13,
297 // BLinksetGroup = xx // a linkset proper is either static or dynamic
298 BLinksetChildGroup = 1 << 14,
299 // The collsion filters and masked are defined in one place -- don't want them scattered
300 AvatarGroup = BCharacterGroup,
301 AvatarMask = BAllGroup,
302 ObjectGroup = BSolidGroup,
303 ObjectMask = BAllGroup,
304 StaticObjectGroup = BStaticGroup,
305 StaticObjectMask = AvatarGroup | ObjectGroup, // static things don't interact with much
306 LinksetGroup = BLinksetChildGroup,
307 LinksetMask = BAllGroup & ~BLinksetChildGroup, // linkset objects don't collide with each other
308 VolumeDetectGroup = BSensorTrigger,
309 VolumeDetectMask = ~BSensorTrigger,
310 TerrainGroup = BTerrainGroup,
311 TerrainMask = BAllGroup & ~BStaticGroup, // static objects on the ground don't collide
312 GroundPlaneGroup = BGroundPlaneGroup,
313 GroundPlaneMask = BAllGroup
314
315};
316
317// CFM controls the 'hardness' of the constraint. 0=fixed, 0..1=violatable. Default=0
318// ERP controls amount of correction per tick. Usable range=0.1..0.8. Default=0.2.
319public enum ConstraintParams : int
320{
321 BT_CONSTRAINT_ERP = 1, // this one is not used in Bullet as of 20120730
322 BT_CONSTRAINT_STOP_ERP,
323 BT_CONSTRAINT_CFM,
324 BT_CONSTRAINT_STOP_CFM,
325};
326public enum ConstraintParamAxis : int
327{
328 AXIS_LINEAR_X = 0,
329 AXIS_LINEAR_Y,
330 AXIS_LINEAR_Z,
331 AXIS_ANGULAR_X,
332 AXIS_ANGULAR_Y,
333 AXIS_ANGULAR_Z,
334 AXIS_LINEAR_ALL = 20, // these last three added by BulletSim so we don't have to do zillions of calls
335 AXIS_ANGULAR_ALL,
336 AXIS_ALL
337};
338
339// ===============================================================================
340static class BulletSimAPI {
341 private static int m_collisionsThisFrame;
342 public delegate void DebugLogCallback(string msg);
343 /// <summary>
344 ///
345 /// </summary>
346 /// <param name="p"></param>
347 /// <param name="p_2"></param>
348 internal static bool RemoveObjectFromWorld2(object pWorld, object pBody)
349 {
350 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
351 RigidBody body = pBody as RigidBody;
352 world.RemoveRigidBody(body);
353 return true;
354 }
355
356 internal static void SetRestitution2(object pBody, float pRestitution)
357 {
358 RigidBody body = pBody as RigidBody;
359 body.SetRestitution(pRestitution);
360 }
361
362 internal static void SetMargin2(object pShape, float pMargin)
363 {
364 CollisionShape shape = pShape as CollisionShape;
365 shape.SetMargin(pMargin);
366 }
367
368 internal static void SetLocalScaling2(object pShape, Vector3 pScale)
369 {
370 CollisionShape shape = pShape as CollisionShape;
371 IndexedVector3 vec = new IndexedVector3(pScale.X, pScale.Y, pScale.Z);
372 shape.SetLocalScaling(ref vec);
373
374 }
375
376 internal static void SetContactProcessingThreshold2(object pBody, float contactprocessingthreshold)
377 {
378 RigidBody body = pBody as RigidBody;
379 body.SetContactProcessingThreshold(contactprocessingthreshold);
380 }
381
382 internal static void SetCcdMotionThreshold2(object pBody, float pccdMotionThreashold)
383 {
384 RigidBody body = pBody as RigidBody;
385 body.SetCcdMotionThreshold(pccdMotionThreashold);
386 }
387
388 internal static void SetCcdSweptSphereRadius2(object pBody, float pCcdSweptSphereRadius)
389 {
390 RigidBody body = pBody as RigidBody;
391 body.SetCcdSweptSphereRadius(pCcdSweptSphereRadius);
392 }
393
394 internal static void SetAngularFactorV2(object pBody, Vector3 pAngularFactor)
395 {
396 RigidBody body = pBody as RigidBody;
397 body.SetAngularFactor(new IndexedVector3(pAngularFactor.X, pAngularFactor.Y, pAngularFactor.Z));
398 }
399
400 internal static CollisionFlags AddToCollisionFlags2(object pBody, CollisionFlags pcollisionFlags)
401 {
402 CollisionObject body = pBody as CollisionObject;
403 CollisionFlags existingcollisionFlags = (CollisionFlags)(uint)body.GetCollisionFlags();
404 existingcollisionFlags |= pcollisionFlags;
405 body.SetCollisionFlags((BulletXNA.BulletCollision.CollisionFlags)(uint)existingcollisionFlags);
406 return (CollisionFlags) (uint) existingcollisionFlags;
407 }
408
409 internal static void AddObjectToWorld2(object pWorld, object pBody)
410 {
411 RigidBody body = pBody as RigidBody;
412 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
413 //if (!(body.GetCollisionShape().GetShapeType() == BroadphaseNativeTypes.STATIC_PLANE_PROXYTYPE && body.GetCollisionShape().GetShapeType() == BroadphaseNativeTypes.TERRAIN_SHAPE_PROXYTYPE))
414
415 world.AddRigidBody(body);
416
417 //if (body.GetBroadphaseHandle() != null)
418 // world.UpdateSingleAabb(body);
419 }
420
421 internal static void AddObjectToWorld2(object pWorld, object pBody, Vector3 _position, Quaternion _orientation)
422 {
423 RigidBody body = pBody as RigidBody;
424 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
425 //if (!(body.GetCollisionShape().GetShapeType() == BroadphaseNativeTypes.STATIC_PLANE_PROXYTYPE && body.GetCollisionShape().GetShapeType() == BroadphaseNativeTypes.TERRAIN_SHAPE_PROXYTYPE))
426
427 world.AddRigidBody(body);
428 IndexedVector3 vposition = new IndexedVector3(_position.X, _position.Y, _position.Z);
429 IndexedQuaternion vquaternion = new IndexedQuaternion(_orientation.X, _orientation.Y, _orientation.Z,
430 _orientation.W);
431 IndexedMatrix mat = IndexedMatrix.CreateFromQuaternion(vquaternion);
432 mat._origin = vposition;
433 body.SetWorldTransform(mat);
434 //if (body.GetBroadphaseHandle() != null)
435 // world.UpdateSingleAabb(body);
436 }
437
438 internal static void ForceActivationState2(object pBody, ActivationState pActivationState)
439 {
440 CollisionObject body = pBody as CollisionObject;
441 body.ForceActivationState((BulletXNA.BulletCollision.ActivationState)(uint)pActivationState);
442 }
443
444 internal static void UpdateSingleAabb2(object pWorld, object pBody)
445 {
446 CollisionObject body = pBody as CollisionObject;
447 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
448 world.UpdateSingleAabb(body);
449 }
450
451 internal static bool SetCollisionGroupMask2(object pBody, uint pGroup, uint pMask)
452 {
453 RigidBody body = pBody as RigidBody;
454 body.GetBroadphaseHandle().m_collisionFilterGroup = (BulletXNA.BulletCollision.CollisionFilterGroups) pGroup;
455 body.GetBroadphaseHandle().m_collisionFilterGroup = (BulletXNA.BulletCollision.CollisionFilterGroups) pGroup;
456 if ((uint) body.GetBroadphaseHandle().m_collisionFilterGroup == 0)
457 return false;
458 return true;
459 }
460
461 internal static void ClearAllForces2(object pBody)
462 {
463 CollisionObject body = pBody as CollisionObject;
464 IndexedVector3 zeroVector = new IndexedVector3(0, 0, 0);
465 body.SetInterpolationLinearVelocity(ref zeroVector);
466 body.SetInterpolationAngularVelocity(ref zeroVector);
467 IndexedMatrix bodytransform = body.GetWorldTransform();
468
469 body.SetInterpolationWorldTransform(ref bodytransform);
470
471 if (body is RigidBody)
472 {
473 RigidBody rigidbody = body as RigidBody;
474 rigidbody.SetLinearVelocity(zeroVector);
475 rigidbody.SetAngularVelocity(zeroVector);
476 rigidbody.ClearForces();
477 }
478 }
479
480 internal static void SetInterpolationAngularVelocity2(object pBody, Vector3 pVector3)
481 {
482 RigidBody body = pBody as RigidBody;
483 IndexedVector3 vec = new IndexedVector3(pVector3.X, pVector3.Y, pVector3.Z);
484 body.SetInterpolationAngularVelocity(ref vec);
485 }
486
487 internal static void SetAngularVelocity2(object pBody, Vector3 pVector3)
488 {
489 RigidBody body = pBody as RigidBody;
490 IndexedVector3 vec = new IndexedVector3(pVector3.X, pVector3.Y, pVector3.Z);
491 body.SetAngularVelocity(ref vec);
492 }
493
494 internal static void ClearForces2(object pBody)
495 {
496 RigidBody body = pBody as RigidBody;
497 body.ClearForces();
498 }
499
500 internal static void SetTranslation2(object pBody, Vector3 _position, Quaternion _orientation)
501 {
502 RigidBody body = pBody as RigidBody;
503 IndexedVector3 vposition = new IndexedVector3(_position.X, _position.Y, _position.Z);
504 IndexedQuaternion vquaternion = new IndexedQuaternion(_orientation.X, _orientation.Y, _orientation.Z,
505 _orientation.W);
506 IndexedMatrix mat = IndexedMatrix.CreateFromQuaternion(vquaternion);
507 mat._origin = vposition;
508 body.SetWorldTransform(mat);
509
510 }
511
512 internal static Vector3 GetPosition2(object pBody)
513 {
514 RigidBody body = pBody as RigidBody;
515 IndexedVector3 pos = body.GetInterpolationWorldTransform()._origin;
516 return new Vector3(pos.X, pos.Y, pos.Z);
517 }
518
519 internal static Vector3 CalculateLocalInertia2(object pShape, float pphysMass)
520 {
521 CollisionShape shape = pShape as CollisionShape;
522 IndexedVector3 inertia = IndexedVector3.Zero;
523 shape.CalculateLocalInertia(pphysMass, out inertia);
524 return new Vector3(inertia.X, inertia.Y, inertia.Z);
525 }
526
527 internal static void SetMassProps2(object pBody, float pphysMass, Vector3 plocalInertia)
528 {
529 RigidBody body = pBody as RigidBody;
530 IndexedVector3 inertia = new IndexedVector3(plocalInertia.X, plocalInertia.Y, plocalInertia.Z);
531 body.SetMassProps(pphysMass, inertia);
532 }
533
534
535 internal static void SetObjectForce2(object pBody, Vector3 _force)
536 {
537 RigidBody body = pBody as RigidBody;
538 IndexedVector3 force = new IndexedVector3(_force.X, _force.Y, _force.Z);
539 body.SetTotalForce(ref force);
540 }
541
542 internal static void SetFriction2(object pBody, float _currentFriction)
543 {
544 RigidBody body = pBody as RigidBody;
545 body.SetFriction(_currentFriction);
546 }
547
548 internal static void SetLinearVelocity2(object pBody, Vector3 _velocity)
549 {
550 RigidBody body = pBody as RigidBody;
551 IndexedVector3 velocity = new IndexedVector3(_velocity.X, _velocity.Y, _velocity.Z);
552 body.SetLinearVelocity(velocity);
553 }
554
555 internal static void Activate2(object pBody, bool pforceactivation)
556 {
557 RigidBody body = pBody as RigidBody;
558 body.Activate(pforceactivation);
559
560 }
561
562 internal static Quaternion GetOrientation2(object pBody)
563 {
564 RigidBody body = pBody as RigidBody;
565 IndexedQuaternion mat = body.GetInterpolationWorldTransform().GetRotation();
566 return new Quaternion(mat.X, mat.Y, mat.Z, mat.W);
567 }
568
569 internal static CollisionFlags RemoveFromCollisionFlags2(object pBody, CollisionFlags pcollisionFlags)
570 {
571 RigidBody body = pBody as RigidBody;
572 CollisionFlags existingcollisionFlags = (CollisionFlags)(uint)body.GetCollisionFlags();
573 existingcollisionFlags &= ~pcollisionFlags;
574 body.SetCollisionFlags((BulletXNA.BulletCollision.CollisionFlags)(uint)existingcollisionFlags);
575 return (CollisionFlags)(uint)existingcollisionFlags;
576 }
577
578 internal static void SetGravity2(object pBody, Vector3 pGravity)
579 {
580 RigidBody body = pBody as RigidBody;
581 IndexedVector3 gravity = new IndexedVector3(pGravity.X, pGravity.Y, pGravity.Z);
582 body.SetGravity(gravity);
583 }
584
585 internal static bool DestroyConstraint2(object pBody, object pConstraint)
586 {
587 RigidBody body = pBody as RigidBody;
588 TypedConstraint constraint = pConstraint as TypedConstraint;
589 body.RemoveConstraintRef(constraint);
590 return true;
591 }
592
593 internal static bool SetLinearLimits2(object pConstraint, Vector3 low, Vector3 high)
594 {
595 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
596 IndexedVector3 lowlimit = new IndexedVector3(low.X, low.Y, low.Z);
597 IndexedVector3 highlimit = new IndexedVector3(high.X, high.Y, high.Z);
598 constraint.SetLinearLowerLimit(lowlimit);
599 constraint.SetLinearUpperLimit(highlimit);
600 return true;
601 }
602
603 internal static bool SetAngularLimits2(object pConstraint, Vector3 low, Vector3 high)
604 {
605 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
606 IndexedVector3 lowlimit = new IndexedVector3(low.X, low.Y, low.Z);
607 IndexedVector3 highlimit = new IndexedVector3(high.X, high.Y, high.Z);
608 constraint.SetAngularLowerLimit(lowlimit);
609 constraint.SetAngularUpperLimit(highlimit);
610 return true;
611 }
612
613 internal static void SetConstraintNumSolverIterations2(object pConstraint, float cnt)
614 {
615 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
616 constraint.SetOverrideNumSolverIterations((int)cnt);
617 }
618
619 internal static void CalculateTransforms2(object pConstraint)
620 {
621 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
622 constraint.CalculateTransforms();
623 }
624
625 internal static void SetConstraintEnable2(object pConstraint, float p_2)
626 {
627 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
628 constraint.SetEnabled((p_2 == 0) ? false : true);
629 }
630
631
632 //BulletSimAPI.Create6DofConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,frame1, frame1rot,frame2, frame2rot,useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
633 internal static object Create6DofConstraint2(object pWorld, object pBody1, object pBody2, Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies)
634
635 {
636 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
637 RigidBody body1 = pBody1 as RigidBody;
638 RigidBody body2 = pBody2 as RigidBody;
639 IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z);
640 IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W);
641 IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot);
642 frame1._origin = frame1v;
643
644 IndexedVector3 frame2v = new IndexedVector3(pframe2.X, pframe2.Y, pframe2.Z);
645 IndexedQuaternion frame2rot = new IndexedQuaternion(pframe2rot.X, pframe2rot.Y, pframe2rot.Z, pframe2rot.W);
646 IndexedMatrix frame2 = IndexedMatrix.CreateFromQuaternion(frame2rot);
647 frame2._origin = frame1v;
648
649 Generic6DofConstraint consttr = new Generic6DofConstraint(body1, body2, ref frame1, ref frame2,
650 puseLinearReferenceFrameA);
651 consttr.CalculateTransforms();
652 world.AddConstraint(consttr,pdisableCollisionsBetweenLinkedBodies);
653
654 return consttr;
655 }
656
657
658 /// <summary>
659 ///
660 /// </summary>
661 /// <param name="pWorld"></param>
662 /// <param name="pBody1"></param>
663 /// <param name="pBody2"></param>
664 /// <param name="pjoinPoint"></param>
665 /// <param name="puseLinearReferenceFrameA"></param>
666 /// <param name="pdisableCollisionsBetweenLinkedBodies"></param>
667 /// <returns></returns>
668 internal static object Create6DofConstraintToPoint2(object pWorld, object pBody1, object pBody2, Vector3 pjoinPoint, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies)
669 {
670 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
671 RigidBody body1 = pBody1 as RigidBody;
672 RigidBody body2 = pBody2 as RigidBody;
673 IndexedMatrix frame1 = new IndexedMatrix(IndexedBasisMatrix.Identity, new IndexedVector3(0, 0, 0));
674 IndexedMatrix frame2 = new IndexedMatrix(IndexedBasisMatrix.Identity, new IndexedVector3(0, 0, 0));
675
676 IndexedVector3 joinPoint = new IndexedVector3(pjoinPoint.X, pjoinPoint.Y, pjoinPoint.Z);
677 IndexedMatrix mat = IndexedMatrix.Identity;
678 mat._origin = new IndexedVector3(pjoinPoint.X, pjoinPoint.Y, pjoinPoint.Z);
679 frame1._origin = body1.GetWorldTransform().Inverse()*joinPoint;
680 frame2._origin = body2.GetWorldTransform().Inverse()*joinPoint;
681
682 Generic6DofConstraint consttr = new Generic6DofConstraint(body1, body2, ref frame1, ref frame2, puseLinearReferenceFrameA);
683 consttr.CalculateTransforms();
684 world.AddConstraint(consttr, pdisableCollisionsBetweenLinkedBodies);
685
686 return consttr;
687 }
688 //SetFrames2(m_constraint.ptr, frameA, frameArot, frameB, frameBrot);
689 internal static void SetFrames2(object pConstraint, Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot)
690 {
691 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
692 IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z);
693 IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W);
694 IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot);
695 frame1._origin = frame1v;
696
697 IndexedVector3 frame2v = new IndexedVector3(pframe2.X, pframe2.Y, pframe2.Z);
698 IndexedQuaternion frame2rot = new IndexedQuaternion(pframe2rot.X, pframe2rot.Y, pframe2rot.Z, pframe2rot.W);
699 IndexedMatrix frame2 = IndexedMatrix.CreateFromQuaternion(frame2rot);
700 frame2._origin = frame1v;
701 constraint.SetFrames(ref frame1, ref frame2);
702 }
703
704
705
706
707 internal static bool IsInWorld2(object pWorld, object pShapeObj)
708 {
709 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
710 CollisionObject shape = pShapeObj as CollisionObject;
711 return world.IsInWorld(shape);
712 }
713
714 internal static void SetInterpolationLinearVelocity2(object pBody, Vector3 VehicleVelocity)
715 {
716 RigidBody body = pBody as RigidBody;
717 IndexedVector3 velocity = new IndexedVector3(VehicleVelocity.X, VehicleVelocity.Y, VehicleVelocity.Z);
718 body.SetInterpolationLinearVelocity(ref velocity);
719 }
720
721 internal static bool UseFrameOffset2(object pConstraint, float onOff)
722 {
723 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
724 constraint.SetUseFrameOffset((onOff == 0) ? false : true);
725 return true;
726 }
727 //SetBreakingImpulseThreshold2(m_constraint.ptr, threshold);
728 internal static bool SetBreakingImpulseThreshold2(object pConstraint, float threshold)
729 {
730 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
731 constraint.SetBreakingImpulseThreshold(threshold);
732 return true;
733 }
734 //BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping);
735 internal static void SetAngularDamping2(object pBody, float angularDamping)
736 {
737 RigidBody body = pBody as RigidBody;
738 float lineardamping = body.GetLinearDamping();
739 body.SetDamping(lineardamping, angularDamping);
740
741 }
742
743 internal static void UpdateInertiaTensor2(object pBody)
744 {
745 RigidBody body = pBody as RigidBody;
746 body.UpdateInertiaTensor();
747 }
748
749 internal static void RecalculateCompoundShapeLocalAabb2( object pCompoundShape)
750 {
751
752 CompoundShape shape = pCompoundShape as CompoundShape;
753 shape.RecalculateLocalAabb();
754 }
755
756 //BulletSimAPI.GetCollisionFlags2(PhysBody.ptr)
757 internal static CollisionFlags GetCollisionFlags2(object pBody)
758 {
759 RigidBody body = pBody as RigidBody;
760 uint flags = (uint)body.GetCollisionFlags();
761 return (CollisionFlags) flags;
762 }
763
764 internal static void SetDamping2(object pBody, float pLinear, float pAngular)
765 {
766 RigidBody body = pBody as RigidBody;
767 body.SetDamping(pLinear, pAngular);
768 }
769 //PhysBody.ptr, PhysicsScene.Params.deactivationTime);
770 internal static void SetDeactivationTime2(object pBody, float pDeactivationTime)
771 {
772 RigidBody body = pBody as RigidBody;
773 body.SetDeactivationTime(pDeactivationTime);
774 }
775 //SetSleepingThresholds2(PhysBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold);
776 internal static void SetSleepingThresholds2(object pBody, float plinearSleepingThreshold, float pangularSleepingThreshold)
777 {
778 RigidBody body = pBody as RigidBody;
779 body.SetSleepingThresholds(plinearSleepingThreshold, pangularSleepingThreshold);
780 }
781
782 internal static CollisionObjectTypes GetBodyType2(object pBody)
783 {
784 RigidBody body = pBody as RigidBody;
785 return (CollisionObjectTypes)(int) body.GetInternalType();
786 }
787
788 //BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, fSum);
789 internal static void ApplyCentralForce2(object pBody, Vector3 pfSum)
790 {
791 RigidBody body = pBody as RigidBody;
792 IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z);
793 body.ApplyCentralForce(ref fSum);
794 }
795 internal static void ApplyCentralImpulse2(object pBody, Vector3 pfSum)
796 {
797 RigidBody body = pBody as RigidBody;
798 IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z);
799 body.ApplyCentralImpulse(ref fSum);
800 }
801 internal static void ApplyTorque2(object pBody, Vector3 pfSum)
802 {
803 RigidBody body = pBody as RigidBody;
804 IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z);
805 body.ApplyTorque(ref fSum);
806 }
807 internal static void ApplyTorqueImpulse2(object pBody, Vector3 pfSum)
808 {
809 RigidBody body = pBody as RigidBody;
810 IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z);
811 body.ApplyTorqueImpulse(ref fSum);
812 }
813
814 internal static void DumpRigidBody2(object p, object p_2)
815 {
816 //TODO:
817 }
818
819 internal static void DumpCollisionShape2(object p, object p_2)
820 {
821 //TODO:
822 }
823
824 internal static void DestroyObject2(object p, object p_2)
825 {
826 //TODO:
827 }
828
829 internal static void Shutdown2(object pWorld)
830 {
831 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
832 world.Cleanup();
833 }
834
835 internal static void DeleteCollisionShape2(object p, object p_2)
836 {
837 //TODO:
838 }
839 //(sim.ptr, shape.ptr, prim.LocalID, prim.RawPosition, prim.RawOrientation);
840
841 internal static object CreateBodyFromShape2(object pWorld, object pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation)
842 {
843 CollisionWorld world = pWorld as CollisionWorld;
844 IndexedMatrix mat =
845 IndexedMatrix.CreateFromQuaternion(new IndexedQuaternion(pRawOrientation.X, pRawOrientation.Y,
846 pRawOrientation.Z, pRawOrientation.W));
847 mat._origin = new IndexedVector3(pRawPosition.X, pRawPosition.Y, pRawPosition.Z);
848 CollisionShape shape = pShape as CollisionShape;
849 //UpdateSingleAabb2(world, shape);
850 // TODO: Feed Update array into null
851 RigidBody body = new RigidBody(0,new SimMotionState(world,pLocalID,mat,null),shape,IndexedVector3.Zero);
852
853 body.SetUserPointer(pLocalID);
854 return body;
855 }
856
857
858 internal static object CreateBodyWithDefaultMotionState2( object pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation)
859 {
860
861 IndexedMatrix mat =
862 IndexedMatrix.CreateFromQuaternion(new IndexedQuaternion(pRawOrientation.X, pRawOrientation.Y,
863 pRawOrientation.Z, pRawOrientation.W));
864 mat._origin = new IndexedVector3(pRawPosition.X, pRawPosition.Y, pRawPosition.Z);
865
866 CollisionShape shape = pShape as CollisionShape;
867
868 // TODO: Feed Update array into null
869 RigidBody body = new RigidBody(0, new DefaultMotionState( mat, IndexedMatrix.Identity), shape, IndexedVector3.Zero);
870 body.SetWorldTransform(mat);
871 body.SetUserPointer(pLocalID);
872 return body;
873 }
874 //(m_mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
875 internal static void SetCollisionFlags2(object pBody, CollisionFlags collisionFlags)
876 {
877 RigidBody body = pBody as RigidBody;
878 body.SetCollisionFlags((BulletXNA.BulletCollision.CollisionFlags) (uint) collisionFlags);
879 }
880 //(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
881 internal static void SetHitFraction2(object pBody, float pHitFraction)
882 {
883 RigidBody body = pBody as RigidBody;
884 body.SetHitFraction(pHitFraction);
885 }
886 //BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale);
887 internal static object BuildCapsuleShape2(object pWorld, float pRadius, float pHeight, Vector3 pScale)
888 {
889 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
890 IndexedVector3 scale = new IndexedVector3(pScale.X, pScale.Y, pScale.Z);
891 CapsuleShapeZ capsuleShapeZ = new CapsuleShapeZ(pRadius, pHeight);
892 capsuleShapeZ.SetMargin(world.WorldSettings.Params.collisionMargin);
893 capsuleShapeZ.SetLocalScaling(ref scale);
894
895 return capsuleShapeZ;
896 }
897
898 public static object Initialize2(Vector3 worldExtent, ConfigurationParameters[] o, int mMaxCollisionsPerFrame, ref List<BulletXNA.CollisionDesc> collisionArray, int mMaxUpdatesPerFrame, ref List<BulletXNA.EntityProperties> updateArray, object mDebugLogCallbackHandle)
899 {
900 CollisionWorld.WorldData.ParamData p = new CollisionWorld.WorldData.ParamData();
901
902 p.angularDamping = o[0].XangularDamping;
903 p.defaultFriction = o[0].defaultFriction;
904 p.defaultFriction = o[0].defaultFriction;
905 p.defaultDensity = o[0].defaultDensity;
906 p.defaultRestitution = o[0].defaultRestitution;
907 p.collisionMargin = o[0].collisionMargin;
908 p.gravity = o[0].gravity;
909
910 p.linearDamping = o[0].XlinearDamping;
911 p.angularDamping = o[0].XangularDamping;
912 p.deactivationTime = o[0].XdeactivationTime;
913 p.linearSleepingThreshold = o[0].XlinearSleepingThreshold;
914 p.angularSleepingThreshold = o[0].XangularSleepingThreshold;
915 p.ccdMotionThreshold = o[0].XccdMotionThreshold;
916 p.ccdSweptSphereRadius = o[0].XccdSweptSphereRadius;
917 p.contactProcessingThreshold = o[0].XcontactProcessingThreshold;
918
919 p.terrainImplementation = o[0].XterrainImplementation;
920 p.terrainFriction = o[0].XterrainFriction;
921
922 p.terrainHitFraction = o[0].XterrainHitFraction;
923 p.terrainRestitution = o[0].XterrainRestitution;
924 p.terrainCollisionMargin = o[0].XterrainCollisionMargin;
925
926 p.avatarFriction = o[0].XavatarFriction;
927 p.avatarStandingFriction = o[0].XavatarStandingFriction;
928 p.avatarDensity = o[0].XavatarDensity;
929 p.avatarRestitution = o[0].XavatarRestitution;
930 p.avatarCapsuleWidth = o[0].XavatarCapsuleWidth;
931 p.avatarCapsuleDepth = o[0].XavatarCapsuleDepth;
932 p.avatarCapsuleHeight = o[0].XavatarCapsuleHeight;
933 p.avatarContactProcessingThreshold = o[0].XavatarContactProcessingThreshold;
934
935 p.vehicleAngularDamping = o[0].XvehicleAngularDamping;
936
937 p.maxPersistantManifoldPoolSize = o[0].maxPersistantManifoldPoolSize;
938 p.maxCollisionAlgorithmPoolSize = o[0].maxCollisionAlgorithmPoolSize;
939 p.shouldDisableContactPoolDynamicAllocation = o[0].shouldDisableContactPoolDynamicAllocation;
940 p.shouldForceUpdateAllAabbs = o[0].shouldForceUpdateAllAabbs;
941 p.shouldRandomizeSolverOrder = o[0].shouldRandomizeSolverOrder;
942 p.shouldSplitSimulationIslands = o[0].shouldSplitSimulationIslands;
943 p.shouldEnableFrictionCaching = o[0].shouldEnableFrictionCaching;
944 p.numberOfSolverIterations = o[0].numberOfSolverIterations;
945
946 p.linksetImplementation = o[0].XlinksetImplementation;
947 p.linkConstraintUseFrameOffset = o[0].XlinkConstraintUseFrameOffset;
948 p.linkConstraintEnableTransMotor = o[0].XlinkConstraintEnableTransMotor;
949 p.linkConstraintTransMotorMaxVel = o[0].XlinkConstraintTransMotorMaxVel;
950 p.linkConstraintTransMotorMaxForce = o[0].XlinkConstraintTransMotorMaxForce;
951 p.linkConstraintERP = o[0].XlinkConstraintERP;
952 p.linkConstraintCFM = o[0].XlinkConstraintCFM;
953 p.linkConstraintSolverIterations = o[0].XlinkConstraintSolverIterations;
954 p.physicsLoggingFrames = o[0].physicsLoggingFrames;
955 DefaultCollisionConstructionInfo ccci = new DefaultCollisionConstructionInfo();
956
957 DefaultCollisionConfiguration cci = new DefaultCollisionConfiguration();
958 CollisionDispatcher m_dispatcher = new CollisionDispatcher(cci);
959
960
961 if (p.maxPersistantManifoldPoolSize > 0)
962 cci.m_persistentManifoldPoolSize = (int)p.maxPersistantManifoldPoolSize;
963 if (p.shouldDisableContactPoolDynamicAllocation !=0)
964 m_dispatcher.SetDispatcherFlags(DispatcherFlags.CD_DISABLE_CONTACTPOOL_DYNAMIC_ALLOCATION);
965 //if (p.maxCollisionAlgorithmPoolSize >0 )
966
967 DbvtBroadphase m_broadphase = new DbvtBroadphase();
968 //IndexedVector3 aabbMin = new IndexedVector3(0, 0, 0);
969 //IndexedVector3 aabbMax = new IndexedVector3(256, 256, 256);
970
971 //AxisSweep3Internal m_broadphase2 = new AxisSweep3Internal(ref aabbMin, ref aabbMax, Convert.ToInt32(0xfffe), 0xffff, ushort.MaxValue/2, null, true);
972 m_broadphase.GetOverlappingPairCache().SetInternalGhostPairCallback(new GhostPairCallback());
973
974 SequentialImpulseConstraintSolver m_solver = new SequentialImpulseConstraintSolver();
975
976 DiscreteDynamicsWorld world = new DiscreteDynamicsWorld(m_dispatcher, m_broadphase, m_solver, cci);
977 world.UpdatedObjects = updateArray;
978 world.UpdatedCollisions = collisionArray;
979 world.WorldSettings.Params = p;
980 world.SetForceUpdateAllAabbs(p.shouldForceUpdateAllAabbs != 0);
981 world.GetSolverInfo().m_solverMode = SolverMode.SOLVER_USE_WARMSTARTING | SolverMode.SOLVER_SIMD;
982 if (p.shouldRandomizeSolverOrder != 0)
983 world.GetSolverInfo().m_solverMode |= SolverMode.SOLVER_RANDMIZE_ORDER;
984
985 world.GetSimulationIslandManager().SetSplitIslands(p.shouldSplitSimulationIslands != 0);
986 //world.GetDispatchInfo().m_enableSatConvex Not implemented in C# port
987
988 if (p.shouldEnableFrictionCaching != 0)
989 world.GetSolverInfo().m_solverMode |= SolverMode.SOLVER_ENABLE_FRICTION_DIRECTION_CACHING;
990
991 if (p.numberOfSolverIterations > 0)
992 world.GetSolverInfo().m_numIterations = (int) p.numberOfSolverIterations;
993
994
995 world.GetSolverInfo().m_damping = world.WorldSettings.Params.linearDamping;
996 world.GetSolverInfo().m_restitution = world.WorldSettings.Params.defaultRestitution;
997 world.GetSolverInfo().m_globalCfm = 0.0f;
998 world.GetSolverInfo().m_tau = 0.6f;
999 world.GetSolverInfo().m_friction = 0.3f;
1000 world.GetSolverInfo().m_maxErrorReduction = 20f;
1001 world.GetSolverInfo().m_numIterations = 10;
1002 world.GetSolverInfo().m_erp = 0.2f;
1003 world.GetSolverInfo().m_erp2 = 0.1f;
1004 world.GetSolverInfo().m_sor = 1.0f;
1005 world.GetSolverInfo().m_splitImpulse = false;
1006 world.GetSolverInfo().m_splitImpulsePenetrationThreshold = -0.02f;
1007 world.GetSolverInfo().m_linearSlop = 0.0f;
1008 world.GetSolverInfo().m_warmstartingFactor = 0.85f;
1009 world.GetSolverInfo().m_restingContactRestitutionThreshold = 2;
1010 world.SetForceUpdateAllAabbs(true);
1011
1012
1013 world.SetGravity(new IndexedVector3(0,0,p.gravity));
1014
1015 return world;
1016 }
1017 //m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL
1018 internal static bool SetConstraintParam2(object pConstraint, ConstraintParams paramIndex, float paramvalue, ConstraintParamAxis axis)
1019 {
1020 Generic6DofConstraint constrain = pConstraint as Generic6DofConstraint;
1021 if (axis == ConstraintParamAxis.AXIS_LINEAR_ALL || axis == ConstraintParamAxis.AXIS_ALL)
1022 {
1023 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams) (int) paramIndex, paramvalue, 0);
1024 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams) (int) paramIndex, paramvalue, 1);
1025 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams) (int) paramIndex, paramvalue, 2);
1026 }
1027 if (axis == ConstraintParamAxis.AXIS_ANGULAR_ALL || axis == ConstraintParamAxis.AXIS_ALL)
1028 {
1029 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, 3);
1030 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, 4);
1031 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, 5);
1032 }
1033 if (axis == ConstraintParamAxis.AXIS_LINEAR_ALL)
1034 {
1035 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, (int)axis);
1036 }
1037 return true;
1038 }
1039
1040 internal static bool PushUpdate2(object pCollisionObject)
1041 {
1042 bool ret = false;
1043 RigidBody rb = pCollisionObject as RigidBody;
1044 if (rb != null)
1045 {
1046 SimMotionState sms = rb.GetMotionState() as SimMotionState;
1047 if (sms != null)
1048 {
1049 IndexedMatrix wt = IndexedMatrix.Identity;
1050 sms.GetWorldTransform(out wt);
1051 sms.SetWorldTransform(ref wt, true);
1052 ret = true;
1053 }
1054 }
1055 return ret;
1056
1057 }
1058
1059 internal static bool IsCompound2(object pShape)
1060 {
1061 CollisionShape shape = pShape as CollisionShape;
1062 return shape.IsCompound();
1063 }
1064 internal static bool IsPloyhedral2(object pShape)
1065 {
1066 CollisionShape shape = pShape as CollisionShape;
1067 return shape.IsPolyhedral();
1068 }
1069 internal static bool IsConvex2d2(object pShape)
1070 {
1071 CollisionShape shape = pShape as CollisionShape;
1072 return shape.IsConvex2d();
1073 }
1074 internal static bool IsConvex2(object pShape)
1075 {
1076 CollisionShape shape = pShape as CollisionShape;
1077 return shape.IsConvex();
1078 }
1079 internal static bool IsNonMoving2(object pShape)
1080 {
1081 CollisionShape shape = pShape as CollisionShape;
1082 return shape.IsNonMoving();
1083 }
1084 internal static bool IsConcave2(object pShape)
1085 {
1086 CollisionShape shape = pShape as CollisionShape;
1087 return shape.IsConcave();
1088 }
1089 internal static bool IsInfinite2(object pShape)
1090 {
1091 CollisionShape shape = pShape as CollisionShape;
1092 return shape.IsInfinite();
1093 }
1094 internal static bool IsNativeShape2(object pShape)
1095 {
1096 CollisionShape shape = pShape as CollisionShape;
1097 bool ret;
1098 switch (shape.GetShapeType())
1099 {
1100 case BroadphaseNativeTypes.BOX_SHAPE_PROXYTYPE:
1101 case BroadphaseNativeTypes.CONE_SHAPE_PROXYTYPE:
1102 case BroadphaseNativeTypes.SPHERE_SHAPE_PROXYTYPE:
1103 case BroadphaseNativeTypes.CYLINDER_SHAPE_PROXYTYPE:
1104 ret = true;
1105 break;
1106 default:
1107 ret = false;
1108 break;
1109 }
1110 return ret;
1111 }
1112 //sim.ptr, shape.ptr,prim.LocalID, prim.RawPosition, prim.RawOrientation
1113 internal static object CreateGhostFromShape2(object pWorld, object pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation)
1114 {
1115 IndexedMatrix bodyTransform = new IndexedMatrix();
1116 bodyTransform._origin = new IndexedVector3(pRawPosition.X, pRawPosition.Y, pRawPosition.Z);
1117 bodyTransform.SetRotation(new IndexedQuaternion(pRawOrientation.X,pRawOrientation.Y,pRawOrientation.Z,pRawOrientation.W));
1118 GhostObject gObj = new PairCachingGhostObject();
1119 gObj.SetWorldTransform(bodyTransform);
1120 CollisionShape shape = pShape as CollisionShape;
1121 gObj.SetCollisionShape(shape);
1122 gObj.SetUserPointer(pLocalID);
1123 // TODO: Add to Special CollisionObjects!
1124 return gObj;
1125 }
1126
1127 public static void SetCollisionShape2(object pWorld, object pObj, object pShape)
1128 {
1129 var world = pWorld as DiscreteDynamicsWorld;
1130 var obj = pObj as CollisionObject;
1131 var shape = pShape as CollisionShape;
1132 obj.SetCollisionShape(shape);
1133
1134 }
1135 //(PhysicsScene.World.ptr, nativeShapeData)
1136 internal static object BuildNativeShape2(object pWorld, ShapeData pShapeData)
1137 {
1138 var world = pWorld as DiscreteDynamicsWorld;
1139 CollisionShape shape = null;
1140 switch (pShapeData.Type)
1141 {
1142 case BSPhysicsShapeType.SHAPE_BOX:
1143 shape = new BoxShape(new IndexedVector3(0.5f,0.5f,0.5f));
1144 break;
1145 case BSPhysicsShapeType.SHAPE_CONE:
1146 shape = new ConeShapeZ(0.5f, 1.0f);
1147 break;
1148 case BSPhysicsShapeType.SHAPE_CYLINDER:
1149 shape = new CylinderShapeZ(new IndexedVector3(0.5f, 0.5f, 0.5f));
1150 break;
1151 case BSPhysicsShapeType.SHAPE_SPHERE:
1152 shape = new SphereShape(0.5f);
1153 break;
1154
1155 }
1156 if (shape != null)
1157 {
1158 IndexedVector3 scaling = new IndexedVector3(pShapeData.Scale.X, pShapeData.Scale.Y, pShapeData.Scale.Z);
1159 shape.SetMargin(world.WorldSettings.Params.collisionMargin);
1160 shape.SetLocalScaling(ref scaling);
1161
1162 }
1163 return shape;
1164 }
1165 //PhysicsScene.World.ptr, false
1166 internal static object CreateCompoundShape2(object pWorld, bool enableDynamicAabbTree)
1167 {
1168 return new CompoundShape(enableDynamicAabbTree);
1169 }
1170
1171 internal static int GetNumberOfCompoundChildren2(object pCompoundShape)
1172 {
1173 var compoundshape = pCompoundShape as CompoundShape;
1174 return compoundshape.GetNumChildShapes();
1175 }
1176 //LinksetRoot.PhysShape.ptr, newShape.ptr, displacementPos, displacementRot
1177 internal static void AddChildShapeToCompoundShape2(object pCShape, object paddShape, Vector3 displacementPos, Quaternion displacementRot)
1178 {
1179 IndexedMatrix relativeTransform = new IndexedMatrix();
1180 var compoundshape = pCShape as CompoundShape;
1181 var addshape = paddShape as CollisionShape;
1182
1183 relativeTransform._origin = new IndexedVector3(displacementPos.X, displacementPos.Y, displacementPos.Z);
1184 relativeTransform.SetRotation(new IndexedQuaternion(displacementRot.X,displacementRot.Y,displacementRot.Z,displacementRot.W));
1185 compoundshape.AddChildShape(ref relativeTransform, addshape);
1186
1187 }
1188
1189 internal static object RemoveChildShapeFromCompoundShapeIndex2(object pCShape, int pii)
1190 {
1191 var compoundshape = pCShape as CompoundShape;
1192 CollisionShape ret = null;
1193 ret = compoundshape.GetChildShape(pii);
1194 compoundshape.RemoveChildShapeByIndex(pii);
1195 return ret;
1196 }
1197
1198 internal static object CreateGroundPlaneShape2(uint pLocalId, float pheight, float pcollisionMargin)
1199 {
1200 StaticPlaneShape m_planeshape = new StaticPlaneShape(new IndexedVector3(0,0,1),(int)pheight );
1201 m_planeshape.SetMargin(pcollisionMargin);
1202 m_planeshape.SetUserPointer(pLocalId);
1203 return m_planeshape;
1204 }
1205
1206 internal static object CreateHingeConstraint2(object pWorld, object pBody1, object ppBody2, Vector3 ppivotInA, Vector3 ppivotInB, Vector3 paxisInA, Vector3 paxisInB, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies)
1207 {
1208 HingeConstraint constrain = null;
1209 var rb1 = pBody1 as RigidBody;
1210 var rb2 = ppBody2 as RigidBody;
1211 if (rb1 != null && rb2 != null)
1212 {
1213 IndexedVector3 pivotInA = new IndexedVector3(ppivotInA.X, ppivotInA.Y, ppivotInA.Z);
1214 IndexedVector3 pivotInB = new IndexedVector3(ppivotInB.X, ppivotInB.Y, ppivotInB.Z);
1215 IndexedVector3 axisInA = new IndexedVector3(paxisInA.X, paxisInA.Y, paxisInA.Z);
1216 IndexedVector3 axisInB = new IndexedVector3(paxisInB.X, paxisInB.Y, paxisInB.Z);
1217 var world = pWorld as DiscreteDynamicsWorld;
1218 world.AddConstraint(constrain, pdisableCollisionsBetweenLinkedBodies);
1219 }
1220 return constrain;
1221 }
1222
1223 internal static bool ReleaseHeightMapInfo2(object pMapInfo)
1224 {
1225 if (pMapInfo != null)
1226 {
1227 BulletHeightMapInfo mapinfo = pMapInfo as BulletHeightMapInfo;
1228 if (mapinfo.heightMap != null)
1229 mapinfo.heightMap = null;
1230
1231
1232 }
1233 return true;
1234 }
1235
1236 internal static object CreateHullShape2(object pWorld, int pHullCount, float[] pConvHulls)
1237 {
1238 CompoundShape compoundshape = new CompoundShape(false);
1239 var world = pWorld as DiscreteDynamicsWorld;
1240
1241
1242 compoundshape.SetMargin(world.WorldSettings.Params.collisionMargin);
1243 int ii = 1;
1244
1245 for (int i = 0; i < pHullCount; i++)
1246 {
1247 int vertexCount = (int) pConvHulls[ii];
1248
1249 IndexedVector3 centroid = new IndexedVector3(pConvHulls[ii + 1], pConvHulls[ii + 2], pConvHulls[ii + 3]);
1250 IndexedMatrix childTrans = IndexedMatrix.Identity;
1251 childTrans._origin = centroid;
1252
1253 List<IndexedVector3> virts = new List<IndexedVector3>();
1254 int ender = ((ii + 4) + (vertexCount*3));
1255 for (int iii = ii + 4; iii < ender; iii+=3)
1256 {
1257
1258 virts.Add(new IndexedVector3(pConvHulls[iii], pConvHulls[iii + 1], pConvHulls[iii +2]));
1259 }
1260 ConvexHullShape convexShape = new ConvexHullShape(virts, vertexCount);
1261 convexShape.SetMargin(world.WorldSettings.Params.collisionMargin);
1262 compoundshape.AddChildShape(ref childTrans, convexShape);
1263 ii += (vertexCount*3 + 4);
1264 }
1265
1266
1267 return compoundshape;
1268 }
1269
1270 internal static object CreateMeshShape2(object pWorld, int pIndicesCount, int[] indices, int pVerticesCount, float[] verticesAsFloats)
1271 {
1272 //DumpRaw(indices,verticesAsFloats,pIndicesCount,pVerticesCount);
1273
1274 for (int iter = 0; iter < pVerticesCount; iter++)
1275 {
1276 if (verticesAsFloats[iter] > 0 && verticesAsFloats[iter] < 0.0001) verticesAsFloats[iter] = 0;
1277 if (verticesAsFloats[iter] < 0 && verticesAsFloats[iter] > -0.0001) verticesAsFloats[iter] = 0;
1278 }
1279
1280 ObjectArray<int> indicesarr = new ObjectArray<int>(indices);
1281 ObjectArray<float> vertices = new ObjectArray<float>(verticesAsFloats);
1282 DumpRaw(indicesarr,vertices,pIndicesCount,pVerticesCount);
1283 var world = pWorld as DiscreteDynamicsWorld;
1284 IndexedMesh mesh = new IndexedMesh();
1285 mesh.m_indexType = PHY_ScalarType.PHY_INTEGER;
1286 mesh.m_numTriangles = pIndicesCount/3;
1287 mesh.m_numVertices = pVerticesCount;
1288 mesh.m_triangleIndexBase = indicesarr;
1289 mesh.m_vertexBase = vertices;
1290 mesh.m_vertexStride = 3;
1291 mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT;
1292 mesh.m_triangleIndexStride = 3;
1293
1294 TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray();
1295 tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER);
1296 BvhTriangleMeshShape meshShape = new BvhTriangleMeshShape(tribuilder, true,true);
1297 meshShape.SetMargin(world.WorldSettings.Params.collisionMargin);
1298 // world.UpdateSingleAabb(meshShape);
1299 return meshShape;
1300
1301 }
1302 public static void DumpRaw(ObjectArray<int>indices, ObjectArray<float> vertices, int pIndicesCount,int pVerticesCount )
1303 {
1304
1305 String fileName = "objTest3.raw";
1306 String completePath = System.IO.Path.Combine(Util.configDir(), fileName);
1307 StreamWriter sw = new StreamWriter(completePath);
1308 IndexedMesh mesh = new IndexedMesh();
1309
1310 mesh.m_indexType = PHY_ScalarType.PHY_INTEGER;
1311 mesh.m_numTriangles = pIndicesCount / 3;
1312 mesh.m_numVertices = pVerticesCount;
1313 mesh.m_triangleIndexBase = indices;
1314 mesh.m_vertexBase = vertices;
1315 mesh.m_vertexStride = 3;
1316 mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT;
1317 mesh.m_triangleIndexStride = 3;
1318
1319 TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray();
1320 tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER);
1321
1322
1323
1324 for (int i = 0; i < pVerticesCount; i++)
1325 {
1326
1327 string s = vertices[indices[i * 3]].ToString("0.0000");
1328 s += " " + vertices[indices[i * 3 + 1]].ToString("0.0000");
1329 s += " " + vertices[indices[i * 3 + 2]].ToString("0.0000");
1330
1331 sw.Write(s + "\n");
1332 }
1333
1334 sw.Close();
1335 }
1336 public static void DumpRaw(int[] indices, float[] vertices, int pIndicesCount, int pVerticesCount)
1337 {
1338
1339 String fileName = "objTest6.raw";
1340 String completePath = System.IO.Path.Combine(Util.configDir(), fileName);
1341 StreamWriter sw = new StreamWriter(completePath);
1342 IndexedMesh mesh = new IndexedMesh();
1343
1344 mesh.m_indexType = PHY_ScalarType.PHY_INTEGER;
1345 mesh.m_numTriangles = pIndicesCount / 3;
1346 mesh.m_numVertices = pVerticesCount;
1347 mesh.m_triangleIndexBase = indices;
1348 mesh.m_vertexBase = vertices;
1349 mesh.m_vertexStride = 3;
1350 mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT;
1351 mesh.m_triangleIndexStride = 3;
1352
1353 TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray();
1354 tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER);
1355
1356
1357 sw.WriteLine("Indices");
1358 sw.WriteLine(string.Format("int[] indices = new int[{0}];",pIndicesCount));
1359 for (int iter = 0; iter < indices.Length; iter++)
1360 {
1361 sw.WriteLine(string.Format("indices[{0}]={1};",iter,indices[iter]));
1362 }
1363 sw.WriteLine("VerticesFloats");
1364 sw.WriteLine(string.Format("float[] vertices = new float[{0}];", pVerticesCount));
1365 for (int iter = 0; iter < vertices.Length; iter++)
1366 {
1367 sw.WriteLine(string.Format("Vertices[{0}]={1};", iter, vertices[iter].ToString("0.0000")));
1368 }
1369
1370 // for (int i = 0; i < pVerticesCount; i++)
1371 // {
1372 //
1373 // string s = vertices[indices[i * 3]].ToString("0.0000");
1374 // s += " " + vertices[indices[i * 3 + 1]].ToString("0.0000");
1375 // s += " " + vertices[indices[i * 3 + 2]].ToString("0.0000");
1376 //
1377 // sw.Write(s + "\n");
1378 //}
1379
1380 sw.Close();
1381 }
1382 //PhysicsScene.World.ptr, m_mapInfo.ID, m_mapInfo.minCoords, m_mapInfo.maxCoords, m_mapInfo.heightMap, PhysicsScene.Params.terrainCollisionMargin
1383 internal static object CreateHeightMapInfo2(object pWorld, uint pId, Vector3 pminCoords, Vector3 pmaxCoords, float[] pheightMap, float pCollisionMargin)
1384 {
1385 BulletHeightMapInfo mapInfo = new BulletHeightMapInfo(pId, pheightMap, null);
1386 mapInfo.heightMap = null;
1387 mapInfo.minCoords = pminCoords;
1388 mapInfo.maxCoords = pmaxCoords;
1389 mapInfo.sizeX = (int) (pmaxCoords.X - pminCoords.X);
1390 mapInfo.sizeY = (int) (pmaxCoords.Y - pminCoords.Y);
1391 mapInfo.ID = pId;
1392 mapInfo.minZ = pminCoords.Z;
1393 mapInfo.maxZ = pmaxCoords.Z;
1394 mapInfo.collisionMargin = pCollisionMargin;
1395 if (mapInfo.minZ == mapInfo.maxZ)
1396 mapInfo.minZ -= 0.2f;
1397 mapInfo.heightMap = pheightMap;
1398
1399 return mapInfo;
1400
1401 }
1402
1403 internal static object CreateTerrainShape2(object pMapInfo)
1404 {
1405 BulletHeightMapInfo mapinfo = pMapInfo as BulletHeightMapInfo;
1406 const int upAxis = 2;
1407 const float scaleFactor = 1.0f;
1408 HeightfieldTerrainShape terrainShape = new HeightfieldTerrainShape((int)mapinfo.sizeX, (int)mapinfo.sizeY,
1409 mapinfo.heightMap, scaleFactor,
1410 mapinfo.minZ, mapinfo.maxZ, upAxis,
1411 false);
1412 terrainShape.SetMargin(mapinfo.collisionMargin + 0.5f);
1413 terrainShape.SetUseDiamondSubdivision(true);
1414 terrainShape.SetUserPointer(mapinfo.ID);
1415 return terrainShape;
1416 }
1417
1418 internal static bool TranslationalLimitMotor2(object pConstraint, float ponOff, float targetVelocity, float maxMotorForce)
1419 {
1420 TypedConstraint tconstrain = pConstraint as TypedConstraint;
1421 bool onOff = ponOff != 0;
1422 bool ret = false;
1423
1424 switch (tconstrain.GetConstraintType())
1425 {
1426 case TypedConstraintType.D6_CONSTRAINT_TYPE:
1427 Generic6DofConstraint constrain = pConstraint as Generic6DofConstraint;
1428 constrain.GetTranslationalLimitMotor().m_enableMotor[0] = onOff;
1429 constrain.GetTranslationalLimitMotor().m_targetVelocity[0] = targetVelocity;
1430 constrain.GetTranslationalLimitMotor().m_maxMotorForce[0] = maxMotorForce;
1431 ret = true;
1432 break;
1433 }
1434
1435
1436 return ret;
1437
1438 }
1439
1440 internal static int PhysicsStep2(object pWorld, float timeStep, int m_maxSubSteps, float m_fixedTimeStep, out int updatedEntityCount, out List<BulletXNA.EntityProperties> updatedEntities, out int collidersCount, out List<BulletXNA.CollisionDesc>colliders)
1441 {
1442 int epic = PhysicsStepint2(pWorld, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out updatedEntities,
1443 out collidersCount, out colliders);
1444 return epic;
1445 }
1446
1447 private static int PhysicsStepint2(object pWorld,float timeStep, int m_maxSubSteps, float m_fixedTimeStep, out int updatedEntityCount, out List<BulletXNA.EntityProperties> updatedEntities, out int collidersCount, out List<BulletXNA.CollisionDesc> colliders)
1448 {
1449 int numSimSteps = 0;
1450
1451
1452 //if (updatedEntities is null)
1453 // updatedEntities = new List<BulletXNA.EntityProperties>();
1454
1455 //if (colliders is null)
1456 // colliders = new List<BulletXNA.CollisionDesc>();
1457
1458
1459 if (pWorld is DiscreteDynamicsWorld)
1460 {
1461 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
1462
1463 numSimSteps = world.StepSimulation(timeStep, m_maxSubSteps, m_fixedTimeStep);
1464 int updates = 0;
1465
1466 updatedEntityCount = world.UpdatedObjects.Count;
1467 updatedEntities = new List<BulletXNA.EntityProperties>(world.UpdatedObjects);
1468 updatedEntityCount = updatedEntities.Count;
1469 world.UpdatedObjects.Clear();
1470
1471
1472 collidersCount = world.UpdatedCollisions.Count;
1473 colliders = new List<BulletXNA.CollisionDesc>(world.UpdatedCollisions);
1474
1475 world.UpdatedCollisions.Clear();
1476 m_collisionsThisFrame = 0;
1477 int numManifolds = world.GetDispatcher().GetNumManifolds();
1478 for (int j = 0; j < numManifolds; j++)
1479 {
1480 PersistentManifold contactManifold = world.GetDispatcher().GetManifoldByIndexInternal(j);
1481 int numContacts = contactManifold.GetNumContacts();
1482 if (numContacts == 0)
1483 continue;
1484
1485 CollisionObject objA = contactManifold.GetBody0() as CollisionObject;
1486 CollisionObject objB = contactManifold.GetBody1() as CollisionObject;
1487
1488 ManifoldPoint manifoldPoint = contactManifold.GetContactPoint(0);
1489 IndexedVector3 contactPoint = manifoldPoint.GetPositionWorldOnB();
1490 IndexedVector3 contactNormal = -manifoldPoint.m_normalWorldOnB; // make relative to A
1491
1492 RecordCollision(world, objA, objB, contactPoint, contactNormal);
1493 m_collisionsThisFrame ++;
1494 if (m_collisionsThisFrame >= 9999999)
1495 break;
1496
1497
1498 }
1499
1500
1501 }
1502 else
1503 {
1504 //if (updatedEntities is null)
1505 updatedEntities = new List<BulletXNA.EntityProperties>();
1506 updatedEntityCount = 0;
1507 //if (colliders is null)
1508 colliders = new List<BulletXNA.CollisionDesc>();
1509 collidersCount = 0;
1510 }
1511 return numSimSteps;
1512 }
1513
1514 private static void RecordCollision(CollisionWorld world,CollisionObject objA, CollisionObject objB, IndexedVector3 contact, IndexedVector3 norm)
1515 {
1516
1517 IndexedVector3 contactNormal = norm;
1518 if ((objA.GetCollisionFlags() & BulletXNA.BulletCollision.CollisionFlags.BS_WANTS_COLLISIONS) == 0 &&
1519 (objB.GetCollisionFlags() & BulletXNA.BulletCollision.CollisionFlags.BS_WANTS_COLLISIONS) == 0)
1520 {
1521 return;
1522 }
1523 uint idA = (uint)objA.GetUserPointer();
1524 uint idB = (uint)objB.GetUserPointer();
1525 if (idA > idB)
1526 {
1527 uint temp = idA;
1528 idA = idB;
1529 idB = temp;
1530 contactNormal = -contactNormal;
1531 }
1532
1533 ulong collisionID = ((ulong) idA << 32) | idB;
1534
1535 BulletXNA.CollisionDesc cDesc = new BulletXNA.CollisionDesc()
1536 {
1537 aID = idA,
1538 bID = idB,
1539 point = contact,
1540 normal = contactNormal
1541 };
1542 world.UpdatedCollisions.Add(cDesc);
1543 m_collisionsThisFrame++;
1544
1545
1546 }
1547 private static EntityProperties GetDebugProperties(object pWorld, object pBody)
1548 {
1549 EntityProperties ent = new EntityProperties();
1550 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
1551 RigidBody body = pBody as RigidBody;
1552 IndexedMatrix transform = body.GetWorldTransform();
1553 IndexedVector3 LinearVelocity = body.GetInterpolationLinearVelocity();
1554 IndexedVector3 AngularVelocity = body.GetInterpolationAngularVelocity();
1555 IndexedQuaternion rotation = transform.GetRotation();
1556 ent.Acceleration = Vector3.Zero;
1557 ent.ID = (uint)body.GetUserPointer();
1558 ent.Position = new Vector3(transform._origin.X,transform._origin.Y,transform._origin.Z);
1559 ent.Rotation = new Quaternion(rotation.X,rotation.Y,rotation.Z,rotation.W);
1560 ent.Velocity = new Vector3(LinearVelocity.X, LinearVelocity.Y, LinearVelocity.Z);
1561 ent.RotationalVelocity = new Vector3(AngularVelocity.X, AngularVelocity.Y, AngularVelocity.Z);
1562 return ent;
1563
1564
1565 }
1566
1567
1568 internal static Vector3 GetLocalScaling2(object pBody)
1569 {
1570 CollisionShape shape = pBody as CollisionShape;
1571 IndexedVector3 scale = shape.GetLocalScaling();
1572 return new Vector3(scale.X,scale.Y,scale.Z);
1573 }
1574
1575 internal static bool RayCastGround(object pWorld, Vector3 _RayOrigin, float pRayHeight, object NotMe)
1576 {
1577 DynamicsWorld world = pWorld as DynamicsWorld;
1578 if (world != null)
1579 {
1580 if (NotMe is CollisionObject || NotMe is RigidBody)
1581 {
1582 CollisionObject AvoidBody = NotMe as CollisionObject;
1583
1584 IndexedVector3 rOrigin = new IndexedVector3(_RayOrigin.X, _RayOrigin.Y, _RayOrigin.Z);
1585 IndexedVector3 rEnd = new IndexedVector3(_RayOrigin.X, _RayOrigin.Y, _RayOrigin.Z - pRayHeight);
1586 using (
1587 ClosestNotMeRayResultCallback rayCallback = new ClosestNotMeRayResultCallback(rOrigin,
1588 rEnd, AvoidBody)
1589 )
1590 {
1591 world.RayTest(ref rOrigin, ref rEnd, rayCallback);
1592 if (rayCallback.HasHit())
1593 {
1594 IndexedVector3 hitLocation = rayCallback.m_hitPointWorld;
1595
1596 }
1597 return rayCallback.HasHit();
1598 }
1599 }
1600 }
1601 return false;
1602 }
1603}
1604}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BulletSimData.cs b/OpenSim/Region/Physics/BulletSNPlugin/BulletSimData.cs
new file mode 100644
index 0000000..a1ed8d8
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BulletSimData.cs
@@ -0,0 +1,280 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using OMV = OpenMetaverse;
31
32namespace OpenSim.Region.Physics.BulletSNPlugin
33{
34// Classes to allow some type checking for the API
35// These hold pointers to allocated objects in the unmanaged space.
36
37// The physics engine controller class created at initialization
38public struct BulletSim
39{
40 public BulletSim(uint worldId, BSScene bss, object xx)
41 {
42 ptr = xx;
43 worldID = worldId;
44 physicsScene = bss;
45 }
46 public object ptr;
47 public uint worldID;
48 // The scene is only in here so very low level routines have a handle to print debug/error messages
49 public BSScene physicsScene;
50}
51
52// An allocated Bullet btRigidBody
53public struct BulletBody
54{
55 public BulletBody(uint id) : this(id, null)
56 {
57 }
58 public BulletBody(uint id, object xx)
59 {
60 ID = id;
61 ptr = xx;
62 collisionType = CollisionType.Static;
63 }
64 public object ptr;
65 public uint ID;
66 public CollisionType collisionType;
67
68 public void Clear()
69 {
70 ptr = null;
71 }
72 public bool HasPhysicalBody { get { return ptr != null; } }
73
74 // Apply the specificed collision mask into the physical world
75 public void ApplyCollisionMask()
76 {
77 // Should assert the body has been added to the physical world.
78 // (The collision masks are stored in the collision proxy cache which only exists for
79 // a collision body that is in the world.)
80 BulletSimAPI.SetCollisionGroupMask2(ptr,
81 BulletSimData.CollisionTypeMasks[collisionType].group,
82 BulletSimData.CollisionTypeMasks[collisionType].mask);
83 }
84
85 public override string ToString()
86 {
87 StringBuilder buff = new StringBuilder();
88 buff.Append("<id=");
89 buff.Append(ID.ToString());
90 buff.Append(",p=");
91 buff.Append(ptr.ToString());
92 buff.Append(",c=");
93 buff.Append(collisionType);
94 buff.Append(">");
95 return buff.ToString();
96 }
97}
98
99public struct BulletShape
100{
101 public BulletShape(object xx) : this(xx, BSPhysicsShapeType.SHAPE_UNKNOWN)
102 {
103 }
104 public BulletShape(object xx, BSPhysicsShapeType typ)
105 {
106 ptr = xx;
107 type = typ;
108 shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE;
109 isNativeShape = false;
110 }
111 public object ptr;
112 public BSPhysicsShapeType type;
113 public System.UInt64 shapeKey;
114 public bool isNativeShape;
115
116 public void Clear()
117 {
118 ptr = null;
119 }
120 public bool HasPhysicalShape { get { return ptr != null; } }
121
122 public override string ToString()
123 {
124 StringBuilder buff = new StringBuilder();
125 buff.Append("<p=");
126 buff.Append(ptr.ToString());
127 buff.Append(",s=");
128 buff.Append(type.ToString());
129 buff.Append(",k=");
130 buff.Append(shapeKey.ToString("X"));
131 buff.Append(",n=");
132 buff.Append(isNativeShape.ToString());
133 buff.Append(">");
134 return buff.ToString();
135 }
136}
137
138// An allocated Bullet btConstraint
139public struct BulletConstraint
140{
141 public BulletConstraint(object xx)
142 {
143 ptr = xx;
144 }
145 public object ptr;
146
147 public void Clear()
148 {
149 ptr = null;
150 }
151 public bool HasPhysicalConstraint { get { return ptr != null; } }
152}
153
154// An allocated HeightMapThing which holds various heightmap info.
155// Made a class rather than a struct so there would be only one
156// instance of this and C# will pass around pointers rather
157// than making copies.
158public class BulletHeightMapInfo
159{
160 public BulletHeightMapInfo(uint id, float[] hm, object xx) {
161 ID = id;
162 Ptr = xx;
163 heightMap = hm;
164 terrainRegionBase = OMV.Vector3.Zero;
165 minCoords = new OMV.Vector3(100f, 100f, 25f);
166 maxCoords = new OMV.Vector3(101f, 101f, 26f);
167 minZ = maxZ = 0f;
168 sizeX = sizeY = 256f;
169 }
170 public uint ID;
171 public object Ptr;
172 public float[] heightMap;
173 public OMV.Vector3 terrainRegionBase;
174 public OMV.Vector3 minCoords;
175 public OMV.Vector3 maxCoords;
176 public float sizeX, sizeY;
177 public float minZ, maxZ;
178 public BulletShape terrainShape;
179 public BulletBody terrainBody;
180
181 public float collisionMargin { get; set; }
182}
183
184// The general class of collsion object.
185public enum CollisionType
186{
187 Avatar,
188 Groundplane,
189 Terrain,
190 Static,
191 Dynamic,
192 VolumeDetect,
193 // Linkset, // A linkset should be either Static or Dynamic
194 LinksetChild,
195 Unknown
196};
197
198// Hold specification of group and mask collision flags for a CollisionType
199public struct CollisionTypeFilterGroup
200{
201 public CollisionTypeFilterGroup(CollisionType t, uint g, uint m)
202 {
203 type = t;
204 group = g;
205 mask = m;
206 }
207 public CollisionType type;
208 public uint group;
209 public uint mask;
210};
211
212 /* NOTE: old definitions kept for reference. Delete when things are working.
213 // The collsion filters and masked are defined in one place -- don't want them scattered
214 AvatarGroup = BCharacterGroup,
215 AvatarMask = BAllGroup,
216 ObjectGroup = BSolidGroup,
217 ObjectMask = BAllGroup,
218 StaticObjectGroup = BStaticGroup,
219 StaticObjectMask = AvatarGroup | ObjectGroup, // static things don't interact with much
220 LinksetGroup = BLinksetGroup,
221 LinksetMask = BAllGroup,
222 LinksetChildGroup = BLinksetChildGroup,
223 LinksetChildMask = BNoneGroup, // Linkset children disappear from the world
224 VolumeDetectGroup = BSensorTrigger,
225 VolumeDetectMask = ~BSensorTrigger,
226 TerrainGroup = BTerrainGroup,
227 TerrainMask = BAllGroup & ~BStaticGroup, // static objects on the ground don't collide
228 GroundPlaneGroup = BGroundPlaneGroup,
229 GroundPlaneMask = BAllGroup
230 */
231
232public static class BulletSimData
233{
234
235// Map of collisionTypes to flags for collision groups and masks.
236// As mentioned above, don't use the CollisionFilterGroups definitions directly in the code
237// but, instead, use references to this dictionary. Finding and debugging
238// collision flag problems will be made easier.
239public static Dictionary<CollisionType, CollisionTypeFilterGroup> CollisionTypeMasks
240 = new Dictionary<CollisionType, CollisionTypeFilterGroup>()
241{
242 { CollisionType.Avatar,
243 new CollisionTypeFilterGroup(CollisionType.Avatar,
244 (uint)CollisionFilterGroups.BCharacterGroup,
245 (uint)CollisionFilterGroups.BAllGroup)
246 },
247 { CollisionType.Groundplane,
248 new CollisionTypeFilterGroup(CollisionType.Groundplane,
249 (uint)CollisionFilterGroups.BGroundPlaneGroup,
250 (uint)CollisionFilterGroups.BAllGroup)
251 },
252 { CollisionType.Terrain,
253 new CollisionTypeFilterGroup(CollisionType.Terrain,
254 (uint)CollisionFilterGroups.BTerrainGroup,
255 (uint)(CollisionFilterGroups.BAllGroup & ~CollisionFilterGroups.BStaticGroup))
256 },
257 { CollisionType.Static,
258 new CollisionTypeFilterGroup(CollisionType.Static,
259 (uint)CollisionFilterGroups.BStaticGroup,
260 (uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup))
261 },
262 { CollisionType.Dynamic,
263 new CollisionTypeFilterGroup(CollisionType.Dynamic,
264 (uint)CollisionFilterGroups.BSolidGroup,
265 (uint)(CollisionFilterGroups.BAllGroup))
266 },
267 { CollisionType.VolumeDetect,
268 new CollisionTypeFilterGroup(CollisionType.VolumeDetect,
269 (uint)CollisionFilterGroups.BSensorTrigger,
270 (uint)(~CollisionFilterGroups.BSensorTrigger))
271 },
272 { CollisionType.LinksetChild,
273 new CollisionTypeFilterGroup(CollisionType.LinksetChild,
274 (uint)CollisionFilterGroups.BTerrainGroup,
275 (uint)(CollisionFilterGroups.BNoneGroup))
276 },
277};
278
279}
280}