aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSNPlugin
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics/BulletSNPlugin')
-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.cs1377
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSLinkset.cs329
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs397
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSLinksetConstraints.cs316
-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.cs346
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSPlugin.cs81
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs1494
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSScene.cs957
-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.cs461
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSTerrainMesh.cs267
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BulletSimAPI.cs1603
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BulletSimData.cs280
23 files changed, 11751 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..d91c47f
--- /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, false);
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, true);
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 _acceleration = OMV.Vector3.Zero;
264 _rotationalVelocity = OMV.Vector3.Zero;
265
266 // Zero some other properties directly into the physics engine
267 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
268 {
269 if (PhysBody.HasPhysicalBody)
270 BulletSimAPI.ClearAllForces2(PhysBody.ptr);
271 });
272 }
273 public override void ZeroAngularMotion(bool inTaintTime)
274 {
275 _rotationalVelocity = OMV.Vector3.Zero;
276
277 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
278 {
279 if (PhysBody.HasPhysicalBody)
280 {
281 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
282 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
283 // The next also get rid of applied linear force but the linear velocity is untouched.
284 BulletSimAPI.ClearForces2(PhysBody.ptr);
285 }
286 });
287 }
288
289
290 public override void LockAngularMotion(OMV.Vector3 axis) { return; }
291
292 public override OMV.Vector3 RawPosition
293 {
294 get { return _position; }
295 set { _position = value; }
296 }
297 public override OMV.Vector3 Position {
298 get {
299 // Don't refetch the position because this function is called a zillion times
300 // _position = BulletSimAPI.GetObjectPosition2(Scene.World.ptr, LocalID);
301 return _position;
302 }
303 set {
304 _position = value;
305 PositionSanityCheck();
306
307 PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
308 {
309 DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
310 if (PhysBody.HasPhysicalBody)
311 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
312 });
313 }
314 }
315 public override OMV.Vector3 ForcePosition {
316 get {
317 _position = BulletSimAPI.GetPosition2(PhysBody.ptr);
318 return _position;
319 }
320 set {
321 _position = value;
322 PositionSanityCheck();
323 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
324 }
325 }
326
327
328 // Check that the current position is sane and, if not, modify the position to make it so.
329 // Check for being below terrain or on water.
330 // Returns 'true' of the position was made sane by some action.
331 private bool PositionSanityCheck()
332 {
333 bool ret = false;
334
335 // TODO: check for out of bounds
336 if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position))
337 {
338 // The character is out of the known/simulated area.
339 // Upper levels of code will handle the transition to other areas so, for
340 // the time, we just ignore the position.
341 return ret;
342 }
343
344 // If below the ground, move the avatar up
345 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
346 if (Position.Z < terrainHeight)
347 {
348 DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
349 _position.Z = terrainHeight + 2.0f;
350 ret = true;
351 }
352 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
353 {
354 float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
355 if (Position.Z < waterHeight)
356 {
357 _position.Z = waterHeight;
358 ret = true;
359 }
360 }
361
362 return ret;
363 }
364
365 // A version of the sanity check that also makes sure a new position value is
366 // pushed back to the physics engine. This routine would be used by anyone
367 // who is not already pushing the value.
368 private bool PositionSanityCheck(bool inTaintTime)
369 {
370 bool ret = false;
371 if (PositionSanityCheck())
372 {
373 // The new position value must be pushed into the physics engine but we can't
374 // just assign to "Position" because of potential call loops.
375 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate()
376 {
377 DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
378 if (PhysBody.HasPhysicalBody)
379 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
380 });
381 ret = true;
382 }
383 return ret;
384 }
385
386 public override float Mass { get { return _mass; } }
387
388 // used when we only want this prim's mass and not the linkset thing
389 public override float RawMass {
390 get {return _mass; }
391 }
392 public override void UpdatePhysicalMassProperties(float physMass, bool inWorld)
393 {
394 OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass);
395 BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, localInertia);
396 }
397
398 public override OMV.Vector3 Force {
399 get { return _force; }
400 set {
401 _force = value;
402 // m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force);
403 PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
404 {
405 DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
406 if (PhysBody.HasPhysicalBody)
407 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
408 });
409 }
410 }
411
412 public bool TouchingGround()
413 {
414 bool ret = BulletSimAPI.RayCastGround(PhysicsScene.World.ptr,_position,_size.Z * 0.55f, PhysBody.ptr);
415 return ret;
416 }
417 // Avatars don't do vehicles
418 public override int VehicleType { get { return (int)Vehicle.TYPE_NONE; } set { return; } }
419 public override void VehicleFloatParam(int param, float value) { }
420 public override void VehicleVectorParam(int param, OMV.Vector3 value) {}
421 public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { }
422 public override void VehicleFlags(int param, bool remove) { }
423
424 // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more
425 public override void SetVolumeDetect(int param) { return; }
426
427 public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } }
428 public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } }
429
430 // Sets the target in the motor. This starts the changing of the avatar's velocity.
431 public override OMV.Vector3 TargetVelocity
432 {
433 get
434 {
435 return _velocityMotor.TargetValue;
436 }
437 set
438 {
439 DetailLog("{0},BSCharacter.setTargetVelocity,call,vel={1}", LocalID, value);
440
441 if (!_flying)
442 if ((value.Z >= 0.0001f) || (value.Z <= -0.0001f) || _velocity.Z < -0.0001f)
443 if (!TouchingGround())
444 value.Z = _velocity.Z;
445 if (_setAlwaysRun)
446 value *= 1.3f;
447
448 OMV.Vector3 targetVel = value;
449
450 PhysicsScene.TaintedObject("BSCharacter.setTargetVelocity", delegate()
451 {
452
453 _velocityMotor.Reset();
454 _velocityMotor.SetTarget(targetVel);
455 _velocityMotor.SetCurrent(_velocity);
456 _velocityMotor.Enabled = true;
457
458 // Make sure a property update happens next step so the motor gets incorporated.
459 BulletSimAPI.PushUpdate2(PhysBody.ptr);
460 });
461 }
462 }
463 // Directly setting velocity means this is what the user really wants now.
464 public override OMV.Vector3 Velocity {
465 get { return _velocity; }
466 set {
467 _velocity = value;
468 // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity);
469 PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate()
470 {
471 _velocityMotor.Reset();
472 _velocityMotor.SetCurrent(_velocity);
473 _velocityMotor.SetTarget(_velocity);
474 // Even though the motor is initialized, it's not used and the velocity goes straight into the avatar.
475 _velocityMotor.Enabled = false;
476
477 DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity);
478 ForceVelocity = _velocity;
479 });
480 }
481 }
482 public override OMV.Vector3 ForceVelocity {
483 get { return _velocity; }
484 set {
485 PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity");
486
487 _velocity = value;
488 // Depending on whether the avatar is moving or not, change the friction
489 // to keep the avatar from slipping around
490 if (_velocity.Length() == 0)
491 {
492 if (_currentFriction != BSParam.AvatarStandingFriction)
493 {
494 _currentFriction = BSParam.AvatarStandingFriction;
495 if (PhysBody.HasPhysicalBody)
496 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
497 }
498 }
499 else
500 {
501 if (_currentFriction != BSParam.AvatarFriction)
502 {
503 _currentFriction = BSParam.AvatarFriction;
504 if (PhysBody.HasPhysicalBody)
505 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
506 }
507 }
508 // Remember the set velocity so we can suppress the reduction by friction, ...
509 _appliedVelocity = value;
510
511 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
512 BulletSimAPI.Activate2(PhysBody.ptr, true);
513 }
514 }
515 public override OMV.Vector3 Torque {
516 get { return _torque; }
517 set { _torque = value;
518 }
519 }
520 public override float CollisionScore {
521 get { return _collisionScore; }
522 set { _collisionScore = value;
523 }
524 }
525 public override OMV.Vector3 Acceleration {
526 get { return _acceleration; }
527 set { _acceleration = value; }
528 }
529 public override OMV.Quaternion RawOrientation
530 {
531 get { return _orientation; }
532 set { _orientation = value; }
533 }
534 public override OMV.Quaternion Orientation {
535 get { return _orientation; }
536 set {
537 _orientation = value;
538 // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation);
539 PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
540 {
541 if (PhysBody.HasPhysicalBody)
542 {
543 // _position = BulletSimAPI.GetPosition2(BSBody.ptr);
544 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
545 }
546 });
547 }
548 }
549 // Go directly to Bullet to get/set the value.
550 public override OMV.Quaternion ForceOrientation
551 {
552 get
553 {
554 _orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr);
555 return _orientation;
556 }
557 set
558 {
559 _orientation = value;
560 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
561 }
562 }
563 public override int PhysicsActorType {
564 get { return _physicsActorType; }
565 set { _physicsActorType = value;
566 }
567 }
568 public override bool IsPhysical {
569 get { return _isPhysical; }
570 set { _isPhysical = value;
571 }
572 }
573 public override bool IsSolid {
574 get { return true; }
575 }
576 public override bool IsStatic {
577 get { return false; }
578 }
579 public override bool Flying {
580 get { return _flying; }
581 set {
582 _flying = value;
583
584 // simulate flying by changing the effect of gravity
585 Buoyancy = ComputeBuoyancyFromFlying(_flying);
586 }
587 }
588 // Flying is implimented by changing the avatar's buoyancy.
589 // Would this be done better with a vehicle type?
590 private float ComputeBuoyancyFromFlying(bool ifFlying) {
591 return ifFlying ? 1f : 0f;
592 }
593 public override bool
594 SetAlwaysRun {
595 get { return _setAlwaysRun; }
596 set { _setAlwaysRun = value; }
597 }
598 public override bool ThrottleUpdates {
599 get { return _throttleUpdates; }
600 set { _throttleUpdates = value; }
601 }
602 public override bool IsColliding {
603 get { return (CollidingStep == PhysicsScene.SimulationStep); }
604 set { _isColliding = value; }
605 }
606 public override bool CollidingGround {
607 get { return (CollidingGroundStep == PhysicsScene.SimulationStep); }
608 set { CollidingGround = value; }
609 }
610 public override bool CollidingObj {
611 get { return _collidingObj; }
612 set { _collidingObj = value; }
613 }
614 public override bool FloatOnWater {
615 set {
616 _floatOnWater = value;
617 PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate()
618 {
619 if (PhysBody.HasPhysicalBody)
620 {
621 if (_floatOnWater)
622 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
623 else
624 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
625 }
626 });
627 }
628 }
629 public override OMV.Vector3 RotationalVelocity {
630 get { return _rotationalVelocity; }
631 set { _rotationalVelocity = value; }
632 }
633 public override OMV.Vector3 ForceRotationalVelocity {
634 get { return _rotationalVelocity; }
635 set { _rotationalVelocity = value; }
636 }
637 public override bool Kinematic {
638 get { return _kinematic; }
639 set { _kinematic = value; }
640 }
641 // neg=fall quickly, 0=1g, 1=0g, pos=float up
642 public override float Buoyancy {
643 get { return _buoyancy; }
644 set { _buoyancy = value;
645 PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate()
646 {
647 DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
648 ForceBuoyancy = _buoyancy;
649 });
650 }
651 }
652 public override float ForceBuoyancy {
653 get { return _buoyancy; }
654 set {
655 PhysicsScene.AssertInTaintTime("BSCharacter.ForceBuoyancy");
656
657 _buoyancy = value;
658 DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
659 // Buoyancy is faked by changing the gravity applied to the object
660 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
661 if (PhysBody.HasPhysicalBody)
662 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
663 }
664 }
665
666 // Used for MoveTo
667 public override OMV.Vector3 PIDTarget {
668 set { _PIDTarget = value; }
669 }
670 public override bool PIDActive {
671 set { _usePID = value; }
672 }
673 public override float PIDTau {
674 set { _PIDTau = value; }
675 }
676
677 // Used for llSetHoverHeight and maybe vehicle height
678 // Hover Height will override MoveTo target's Z
679 public override bool PIDHoverActive {
680 set { _useHoverPID = value; }
681 }
682 public override float PIDHoverHeight {
683 set { _PIDHoverHeight = value; }
684 }
685 public override PIDHoverType PIDHoverType {
686 set { _PIDHoverType = value; }
687 }
688 public override float PIDHoverTau {
689 set { _PIDHoverTao = value; }
690 }
691
692 // For RotLookAt
693 public override OMV.Quaternion APIDTarget { set { return; } }
694 public override bool APIDActive { set { return; } }
695 public override float APIDStrength { set { return; } }
696 public override float APIDDamping { set { return; } }
697
698 public override void AddForce(OMV.Vector3 force, bool pushforce) {
699 if (force.IsFinite())
700 {
701 _force.X += force.X;
702 _force.Y += force.Y;
703 _force.Z += force.Z;
704 // m_log.DebugFormat("{0}: AddForce. adding={1}, newForce={2}", LogHeader, force, _force);
705 PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate()
706 {
707 DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force);
708 if (PhysBody.HasPhysicalBody)
709 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
710 });
711 }
712 else
713 {
714 m_log.ErrorFormat("{0}: Got a NaN force applied to a Character", LogHeader);
715 }
716 //m_lastUpdateSent = false;
717 }
718
719 public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
720 }
721 public override void SetMomentum(OMV.Vector3 momentum) {
722 }
723
724 private void ComputeAvatarScale(OMV.Vector3 size)
725 {
726 OMV.Vector3 newScale = size;
727 // newScale.X = PhysicsScene.Params.avatarCapsuleWidth;
728 // newScale.Y = PhysicsScene.Params.avatarCapsuleDepth;
729
730 // From the total height, remove the capsule half spheres that are at each end
731 // The 1.15f came from ODE. Not sure what this factors in.
732 // newScale.Z = (size.Z * 1.15f) - (newScale.X + newScale.Y);
733
734 // The total scale height is the central cylindar plus the caps on the two ends.
735 newScale.Z = size.Z + (Math.Min(size.X, size.Y) * 2f);
736
737 // Convert diameters to radii and height to half height -- the way Bullet expects it.
738 Scale = newScale / 2f;
739 }
740
741 // set _avatarVolume and _mass based on capsule size, _density and Scale
742 private void ComputeAvatarVolumeAndMass()
743 {
744 _avatarVolume = (float)(
745 Math.PI
746 * Scale.X
747 * Scale.Y // the area of capsule cylinder
748 * Scale.Z // times height of capsule cylinder
749 + 1.33333333f
750 * Math.PI
751 * Scale.X
752 * Math.Min(Scale.X, Scale.Y)
753 * Scale.Y // plus the volume of the capsule end caps
754 );
755 _mass = _avatarDensity * _avatarVolume;
756 }
757
758 // The physics engine says that properties have updated. Update same and inform
759 // the world that things have changed.
760 public override void UpdateProperties(EntityProperties entprop)
761 {
762 _position = entprop.Position;
763 _orientation = entprop.Rotation;
764 _velocity = entprop.Velocity;
765 _acceleration = entprop.Acceleration;
766 _rotationalVelocity = entprop.RotationalVelocity;
767
768 // Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
769 PositionSanityCheck(true);
770
771 if (_velocityMotor.Enabled)
772 {
773 // TODO: Decide if the step parameters should be changed depending on the avatar's
774 // state (flying, colliding, ...).
775
776 OMV.Vector3 stepVelocity = _velocityMotor.Step(PhysicsScene.LastTimeStep);
777
778 // If falling, we keep the world's downward vector no matter what the other axis specify.
779 if (!Flying && !IsColliding)
780 {
781 stepVelocity.Z = entprop.Velocity.Z;
782 DetailLog("{0},BSCharacter.UpdateProperties,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity);
783 }
784
785 // If the user has said stop and we've stopped applying velocity correction,
786 // the motor can be turned off. Set the velocity to zero so the zero motion is sent to the viewer.
787 if (_velocityMotor.TargetValue.ApproxEquals(OMV.Vector3.Zero, 0.01f) && _velocityMotor.ErrorIsZero)
788 {
789 ZeroMotion(true);
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..f1bed39
--- /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 BulletWorld 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..d1e3f55
--- /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(BulletWorld 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(BulletWorld 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..87d1e44
--- /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 BulletWorld m_world;
45
46 public BSConstraintCollection(BulletWorld 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..fbd1bc0
--- /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(BulletWorld 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..415ad4f
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSDynamics.cs
@@ -0,0 +1,1377 @@
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 Vector3 grav = PhysicsScene.DefaultGravity * (1f - Prim.Buoyancy);
577 BulletSimAPI.SetGravity2(Prim.PhysBody.ptr, grav);
578
579 VDetailLog("{0},BSDynamics.Refresh,mass={1},frict={2},inert={3},aDamp={4}",
580 Prim.LocalID, m_vehicleMass, friction, localInertia, angularDamping);
581 }
582 else
583 {
584 BulletSimAPI.RemoveFromCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
585 }
586 }
587
588 public bool RemoveBodyDependencies(BSPhysObject prim)
589 {
590 // If active, we need to add our properties back when the body is rebuilt.
591 return IsActive;
592 }
593
594 public void RestoreBodyDependencies(BSPhysObject prim)
595 {
596 if (Prim.LocalID != prim.LocalID)
597 {
598 // The call should be on us by our prim. Error if not.
599 PhysicsScene.Logger.ErrorFormat("{0} RestoreBodyDependencies: called by not my prim. passedLocalID={1}, vehiclePrimLocalID={2}",
600 LogHeader, prim.LocalID, Prim.LocalID);
601 return;
602 }
603 Refresh();
604 }
605
606 #region Known vehicle value functions
607 // Vehicle physical parameters that we buffer from constant getting and setting.
608 // The "m_known*" values are unknown until they are fetched and the m_knownHas flag is set.
609 // Changing is remembered and the parameter is stored back into the physics engine only if updated.
610 // This does two things: 1) saves continuious calls into unmanaged code, and
611 // 2) signals when a physics property update must happen back to the simulator
612 // to update values modified for the vehicle.
613 private int m_knownChanged;
614 private int m_knownHas;
615 private float m_knownTerrainHeight;
616 private float m_knownWaterLevel;
617 private Vector3 m_knownPosition;
618 private Vector3 m_knownVelocity;
619 private Vector3 m_knownForce;
620 private Quaternion m_knownOrientation;
621 private Vector3 m_knownRotationalVelocity;
622 private Vector3 m_knownRotationalForce;
623 private Vector3 m_knownForwardVelocity; // vehicle relative forward speed
624
625 private const int m_knownChangedPosition = 1 << 0;
626 private const int m_knownChangedVelocity = 1 << 1;
627 private const int m_knownChangedForce = 1 << 2;
628 private const int m_knownChangedOrientation = 1 << 3;
629 private const int m_knownChangedRotationalVelocity = 1 << 4;
630 private const int m_knownChangedRotationalForce = 1 << 5;
631 private const int m_knownChangedTerrainHeight = 1 << 6;
632 private const int m_knownChangedWaterLevel = 1 << 7;
633 private const int m_knownChangedForwardVelocity = 1 << 8;
634
635 private void ForgetKnownVehicleProperties()
636 {
637 m_knownHas = 0;
638 m_knownChanged = 0;
639 }
640 // Push all the changed values back into the physics engine
641 private void PushKnownChanged()
642 {
643 if (m_knownChanged != 0)
644 {
645 if ((m_knownChanged & m_knownChangedPosition) != 0)
646 Prim.ForcePosition = m_knownPosition;
647
648 if ((m_knownChanged & m_knownChangedOrientation) != 0)
649 Prim.ForceOrientation = m_knownOrientation;
650
651 if ((m_knownChanged & m_knownChangedVelocity) != 0)
652 {
653 Prim.ForceVelocity = m_knownVelocity;
654 BulletSimAPI.SetInterpolationLinearVelocity2(Prim.PhysBody.ptr, VehicleVelocity);
655 }
656
657 if ((m_knownChanged & m_knownChangedForce) != 0)
658 Prim.AddForce((Vector3)m_knownForce, false, true);
659
660 if ((m_knownChanged & m_knownChangedRotationalVelocity) != 0)
661 {
662 Prim.ForceRotationalVelocity = m_knownRotationalVelocity;
663 // Fake out Bullet by making it think the velocity is the same as last time.
664 BulletSimAPI.SetInterpolationAngularVelocity2(Prim.PhysBody.ptr, m_knownRotationalVelocity);
665 }
666
667 if ((m_knownChanged & m_knownChangedRotationalForce) != 0)
668 Prim.AddAngularForce((Vector3)m_knownRotationalForce, false, true);
669
670 // If we set one of the values (ie, the physics engine didn't do it) we must force
671 // an UpdateProperties event to send the changes up to the simulator.
672 BulletSimAPI.PushUpdate2(Prim.PhysBody.ptr);
673 }
674 m_knownChanged = 0;
675 }
676
677 // Since the computation of terrain height can be a little involved, this routine
678 // is used to fetch the height only once for each vehicle simulation step.
679 private float GetTerrainHeight(Vector3 pos)
680 {
681 if ((m_knownHas & m_knownChangedTerrainHeight) == 0)
682 {
683 m_knownTerrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos);
684 m_knownHas |= m_knownChangedTerrainHeight;
685 }
686 return m_knownTerrainHeight;
687 }
688
689 // Since the computation of water level can be a little involved, this routine
690 // is used ot fetch the level only once for each vehicle simulation step.
691 private float GetWaterLevel(Vector3 pos)
692 {
693 if ((m_knownHas & m_knownChangedWaterLevel) == 0)
694 {
695 m_knownWaterLevel = Prim.PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(pos);
696 m_knownHas |= m_knownChangedWaterLevel;
697 }
698 return (float)m_knownWaterLevel;
699 }
700
701 private Vector3 VehiclePosition
702 {
703 get
704 {
705 if ((m_knownHas & m_knownChangedPosition) == 0)
706 {
707 m_knownPosition = Prim.ForcePosition;
708 m_knownHas |= m_knownChangedPosition;
709 }
710 return m_knownPosition;
711 }
712 set
713 {
714 m_knownPosition = value;
715 m_knownChanged |= m_knownChangedPosition;
716 m_knownHas |= m_knownChangedPosition;
717 }
718 }
719
720 private Quaternion VehicleOrientation
721 {
722 get
723 {
724 if ((m_knownHas & m_knownChangedOrientation) == 0)
725 {
726 m_knownOrientation = Prim.ForceOrientation;
727 m_knownHas |= m_knownChangedOrientation;
728 }
729 return m_knownOrientation;
730 }
731 set
732 {
733 m_knownOrientation = value;
734 m_knownChanged |= m_knownChangedOrientation;
735 m_knownHas |= m_knownChangedOrientation;
736 }
737 }
738
739 private Vector3 VehicleVelocity
740 {
741 get
742 {
743 if ((m_knownHas & m_knownChangedVelocity) == 0)
744 {
745 m_knownVelocity = Prim.ForceVelocity;
746 m_knownHas |= m_knownChangedVelocity;
747 }
748 return (Vector3)m_knownVelocity;
749 }
750 set
751 {
752 m_knownVelocity = value;
753 m_knownChanged |= m_knownChangedVelocity;
754 m_knownHas |= m_knownChangedVelocity;
755 }
756 }
757
758 private void VehicleAddForce(Vector3 aForce)
759 {
760 if ((m_knownHas & m_knownChangedForce) == 0)
761 {
762 m_knownForce = Vector3.Zero;
763 }
764 m_knownForce += aForce;
765 m_knownChanged |= m_knownChangedForce;
766 m_knownHas |= m_knownChangedForce;
767 }
768
769 private Vector3 VehicleRotationalVelocity
770 {
771 get
772 {
773 if ((m_knownHas & m_knownChangedRotationalVelocity) == 0)
774 {
775 m_knownRotationalVelocity = Prim.ForceRotationalVelocity;
776 m_knownHas |= m_knownChangedRotationalVelocity;
777 }
778 return (Vector3)m_knownRotationalVelocity;
779 }
780 set
781 {
782 m_knownRotationalVelocity = value;
783 m_knownChanged |= m_knownChangedRotationalVelocity;
784 m_knownHas |= m_knownChangedRotationalVelocity;
785 }
786 }
787 private void VehicleAddAngularForce(Vector3 aForce)
788 {
789 if ((m_knownHas & m_knownChangedRotationalForce) == 0)
790 {
791 m_knownRotationalForce = Vector3.Zero;
792 }
793 m_knownRotationalForce += aForce;
794 m_knownChanged |= m_knownChangedRotationalForce;
795 m_knownHas |= m_knownChangedRotationalForce;
796 }
797 // Vehicle relative forward velocity
798 private Vector3 VehicleForwardVelocity
799 {
800 get
801 {
802 if ((m_knownHas & m_knownChangedForwardVelocity) == 0)
803 {
804 m_knownForwardVelocity = VehicleVelocity * Quaternion.Inverse(Quaternion.Normalize(VehicleOrientation));
805 m_knownHas |= m_knownChangedForwardVelocity;
806 }
807 return m_knownForwardVelocity;
808 }
809 }
810 private float VehicleForwardSpeed
811 {
812 get
813 {
814 return VehicleForwardVelocity.X;
815 }
816 }
817
818 #endregion // Known vehicle value functions
819
820 // One step of the vehicle properties for the next 'pTimestep' seconds.
821 internal void Step(float pTimestep)
822 {
823 if (!IsActive) return;
824
825 ForgetKnownVehicleProperties();
826
827 MoveLinear(pTimestep);
828 MoveAngular(pTimestep);
829
830 LimitRotation(pTimestep);
831
832 // remember the position so next step we can limit absolute movement effects
833 m_lastPositionVector = VehiclePosition;
834
835 // If we forced the changing of some vehicle parameters, update the values and
836 // for the physics engine to note the changes so an UpdateProperties event will happen.
837 PushKnownChanged();
838
839 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
840 Prim.LocalID, VehiclePosition, Prim.Force, VehicleVelocity, VehicleRotationalVelocity);
841 }
842
843 // Apply the effect of the linear motor and other linear motions (like hover and float).
844 private void MoveLinear(float pTimestep)
845 {
846 Vector3 linearMotorContribution = m_linearMotor.Step(pTimestep);
847
848 // The movement computed in the linear motor is relative to the vehicle
849 // coordinates. Rotate the movement to world coordinates.
850 linearMotorContribution *= VehicleOrientation;
851
852 // ==================================================================
853 // Buoyancy: force to overcome gravity.
854 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
855 // So, if zero, don't change anything (let gravity happen). If one, negate the effect of gravity.
856 Vector3 buoyancyContribution = Prim.PhysicsScene.DefaultGravity * m_VehicleBuoyancy;
857
858 Vector3 terrainHeightContribution = ComputeLinearTerrainHeightCorrection(pTimestep);
859
860 Vector3 hoverContribution = ComputeLinearHover(pTimestep);
861
862 ComputeLinearBlockingEndPoint(pTimestep);
863
864 Vector3 limitMotorUpContribution = ComputeLinearMotorUp(pTimestep);
865
866 // ==================================================================
867 Vector3 newVelocity = linearMotorContribution
868 + terrainHeightContribution
869 + hoverContribution
870 + limitMotorUpContribution;
871
872 Vector3 newForce = buoyancyContribution;
873
874 // If not changing some axis, reduce out velocity
875 if ((m_flags & (VehicleFlag.NO_X)) != 0)
876 newVelocity.X = 0;
877 if ((m_flags & (VehicleFlag.NO_Y)) != 0)
878 newVelocity.Y = 0;
879 if ((m_flags & (VehicleFlag.NO_Z)) != 0)
880 newVelocity.Z = 0;
881
882 // ==================================================================
883 // Clamp high or low velocities
884 float newVelocityLengthSq = newVelocity.LengthSquared();
885 if (newVelocityLengthSq > 1000f)
886 {
887 newVelocity /= newVelocity.Length();
888 newVelocity *= 1000f;
889 }
890 else if (newVelocityLengthSq < 0.001f)
891 newVelocity = Vector3.Zero;
892
893 // ==================================================================
894 // Stuff new linear velocity into the vehicle.
895 // Since the velocity is just being set, it is not scaled by pTimeStep. Bullet will do that for us.
896 VehicleVelocity = newVelocity;
897
898 // Other linear forces are applied as forces.
899 Vector3 totalDownForce = newForce * m_vehicleMass;
900 if (!totalDownForce.ApproxEquals(Vector3.Zero, 0.01f))
901 {
902 VehicleAddForce(totalDownForce);
903 }
904
905 VDetailLog("{0}, MoveLinear,done,newVel={1},totDown={2},IsColliding={3}",
906 Prim.LocalID, newVelocity, totalDownForce, Prim.IsColliding);
907 VDetailLog("{0}, MoveLinear,done,linContrib={1},terrContrib={2},hoverContrib={3},limitContrib={4},buoyContrib={5}",
908 Prim.LocalID,
909 linearMotorContribution, terrainHeightContribution, hoverContribution,
910 limitMotorUpContribution, buoyancyContribution
911 );
912
913 } // end MoveLinear()
914
915 public Vector3 ComputeLinearTerrainHeightCorrection(float pTimestep)
916 {
917 Vector3 ret = Vector3.Zero;
918 // If below the terrain, move us above the ground a little.
919 // TODO: Consider taking the rotated size of the object or possibly casting a ray.
920 if (VehiclePosition.Z < GetTerrainHeight(VehiclePosition))
921 {
922 // TODO: correct position by applying force rather than forcing position.
923 Vector3 newPosition = VehiclePosition;
924 newPosition.Z = GetTerrainHeight(VehiclePosition) + 1f;
925 VehiclePosition = newPosition;
926 VDetailLog("{0}, MoveLinear,terrainHeight,terrainHeight={1},pos={2}",
927 Prim.LocalID, GetTerrainHeight(VehiclePosition), VehiclePosition);
928 }
929 return ret;
930 }
931
932 public Vector3 ComputeLinearHover(float pTimestep)
933 {
934 Vector3 ret = Vector3.Zero;
935
936 // m_VhoverEfficiency: 0=bouncy, 1=totally damped
937 // m_VhoverTimescale: time to achieve height
938 if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
939 {
940 // We should hover, get the target height
941 if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0)
942 {
943 m_VhoverTargetHeight = GetWaterLevel(VehiclePosition) + m_VhoverHeight;
944 }
945 if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
946 {
947 m_VhoverTargetHeight = GetTerrainHeight(VehiclePosition) + m_VhoverHeight;
948 }
949 if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
950 {
951 m_VhoverTargetHeight = m_VhoverHeight;
952 }
953
954 if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0)
955 {
956 // If body is already heigher, use its height as target height
957 if (VehiclePosition.Z > m_VhoverTargetHeight)
958 m_VhoverTargetHeight = VehiclePosition.Z;
959 }
960
961 if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0)
962 {
963 if (Math.Abs(VehiclePosition.Z - m_VhoverTargetHeight) > 0.2f)
964 {
965 Vector3 pos = VehiclePosition;
966 pos.Z = m_VhoverTargetHeight;
967 VehiclePosition = pos;
968 }
969 }
970 else
971 {
972 // Error is positive if below the target and negative if above.
973 float verticalError = m_VhoverTargetHeight - VehiclePosition.Z;
974 float verticalCorrectionVelocity = verticalError / m_VhoverTimescale;
975
976 // TODO: implement m_VhoverEfficiency correctly
977 if (Math.Abs(verticalError) > m_VhoverEfficiency)
978 {
979 ret = new Vector3(0f, 0f, verticalCorrectionVelocity);
980 }
981 }
982
983 VDetailLog("{0}, MoveLinear,hover,pos={1},ret={2},hoverTS={3},height={4},target={5}",
984 Prim.LocalID, VehiclePosition, ret, m_VhoverTimescale, m_VhoverHeight, m_VhoverTargetHeight);
985 }
986
987 return ret;
988 }
989
990 public bool ComputeLinearBlockingEndPoint(float pTimestep)
991 {
992 bool changed = false;
993
994 Vector3 pos = VehiclePosition;
995 Vector3 posChange = pos - m_lastPositionVector;
996 if (m_BlockingEndPoint != Vector3.Zero)
997 {
998 if (pos.X >= (m_BlockingEndPoint.X - (float)1))
999 {
1000 pos.X -= posChange.X + 1;
1001 changed = true;
1002 }
1003 if (pos.Y >= (m_BlockingEndPoint.Y - (float)1))
1004 {
1005 pos.Y -= posChange.Y + 1;
1006 changed = true;
1007 }
1008 if (pos.Z >= (m_BlockingEndPoint.Z - (float)1))
1009 {
1010 pos.Z -= posChange.Z + 1;
1011 changed = true;
1012 }
1013 if (pos.X <= 0)
1014 {
1015 pos.X += posChange.X + 1;
1016 changed = true;
1017 }
1018 if (pos.Y <= 0)
1019 {
1020 pos.Y += posChange.Y + 1;
1021 changed = true;
1022 }
1023 if (changed)
1024 {
1025 VehiclePosition = pos;
1026 VDetailLog("{0}, MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}",
1027 Prim.LocalID, m_BlockingEndPoint, posChange, pos);
1028 }
1029 }
1030 return changed;
1031 }
1032
1033 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
1034 // Prevent ground vehicles from motoring into the sky. This flag has a subtle effect when
1035 // used with conjunction with banking: the strength of the banking will decay when the
1036 // vehicle no longer experiences collisions. The decay timescale is the same as
1037 // VEHICLE_BANKING_TIMESCALE. This is to help prevent ground vehicles from steering
1038 // when they are in mid jump.
1039 // TODO: this code is wrong. Also, what should it do for boats (height from water)?
1040 // This is just using the ground and a general collision check. Should really be using
1041 // a downward raycast to find what is below.
1042 public Vector3 ComputeLinearMotorUp(float pTimestep)
1043 {
1044 Vector3 ret = Vector3.Zero;
1045 float distanceAboveGround = 0f;
1046
1047 if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
1048 {
1049 float targetHeight = Type == Vehicle.TYPE_BOAT ? GetWaterLevel(VehiclePosition) : GetTerrainHeight(VehiclePosition);
1050 distanceAboveGround = VehiclePosition.Z - targetHeight;
1051 // Not colliding if the vehicle is off the ground
1052 if (!Prim.IsColliding)
1053 {
1054 // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale);
1055 ret = new Vector3(0, 0, -distanceAboveGround);
1056 }
1057 // TODO: this calculation is wrong. From the description at
1058 // (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce
1059 // has a decay factor. This says this force should
1060 // be computed with a motor.
1061 // TODO: add interaction with banking.
1062 }
1063 VDetailLog("{0}, MoveLinear,limitMotorUp,distAbove={1},colliding={2},ret={3}",
1064 Prim.LocalID, distanceAboveGround, Prim.IsColliding, ret);
1065 return ret;
1066 }
1067
1068 // =======================================================================
1069 // =======================================================================
1070 // Apply the effect of the angular motor.
1071 // The 'contribution' is how much angular correction velocity each function wants.
1072 // All the contributions are added together and the resulting velocity is
1073 // set directly on the vehicle.
1074 private void MoveAngular(float pTimestep)
1075 {
1076 // The user wants this many radians per second angular change?
1077 Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep);
1078
1079 // ==================================================================
1080 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
1081 // This flag prevents linear deflection parallel to world z-axis. This is useful
1082 // for preventing ground vehicles with large linear deflection, like bumper cars,
1083 // from climbing their linear deflection into the sky.
1084 // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement
1085 if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
1086 {
1087 angularMotorContribution.X = 0f;
1088 angularMotorContribution.Y = 0f;
1089 VDetailLog("{0}, MoveAngular,noDeflectionUp,angularMotorContrib={1}", Prim.LocalID, angularMotorContribution);
1090 }
1091
1092 Vector3 verticalAttractionContribution = ComputeAngularVerticalAttraction();
1093
1094 Vector3 deflectionContribution = ComputeAngularDeflection();
1095
1096 Vector3 bankingContribution = ComputeAngularBanking();
1097
1098 // ==================================================================
1099 m_lastVertAttractor = verticalAttractionContribution;
1100
1101 m_lastAngularVelocity = angularMotorContribution
1102 + verticalAttractionContribution
1103 + deflectionContribution
1104 + bankingContribution;
1105
1106 // ==================================================================
1107 // Apply the correction velocity.
1108 // TODO: Should this be applied as an angular force (torque)?
1109 if (!m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f))
1110 {
1111 VehicleRotationalVelocity = m_lastAngularVelocity;
1112
1113 VDetailLog("{0}, MoveAngular,done,nonZero,angMotorContrib={1},vertAttrContrib={2},bankContrib={3},deflectContrib={4},totalContrib={5}",
1114 Prim.LocalID,
1115 angularMotorContribution, verticalAttractionContribution,
1116 bankingContribution, deflectionContribution,
1117 m_lastAngularVelocity
1118 );
1119 }
1120 else
1121 {
1122 // The vehicle is not adding anything angular wise.
1123 VehicleRotationalVelocity = Vector3.Zero;
1124 VDetailLog("{0}, MoveAngular,done,zero", Prim.LocalID);
1125 }
1126
1127 // ==================================================================
1128 //Offset section
1129 if (m_linearMotorOffset != Vector3.Zero)
1130 {
1131 //Offset of linear velocity doesn't change the linear velocity,
1132 // but causes a torque to be applied, for example...
1133 //
1134 // IIIII >>> IIIII
1135 // IIIII >>> IIIII
1136 // IIIII >>> IIIII
1137 // ^
1138 // | Applying a force at the arrow will cause the object to move forward, but also rotate
1139 //
1140 //
1141 // The torque created is the linear velocity crossed with the offset
1142
1143 // TODO: this computation should be in the linear section
1144 // because that is where we know the impulse being applied.
1145 Vector3 torqueFromOffset = Vector3.Zero;
1146 // torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse);
1147 if (float.IsNaN(torqueFromOffset.X))
1148 torqueFromOffset.X = 0;
1149 if (float.IsNaN(torqueFromOffset.Y))
1150 torqueFromOffset.Y = 0;
1151 if (float.IsNaN(torqueFromOffset.Z))
1152 torqueFromOffset.Z = 0;
1153
1154 VehicleAddAngularForce(torqueFromOffset * m_vehicleMass);
1155 VDetailLog("{0}, BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset);
1156 }
1157
1158 }
1159 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
1160 // Some vehicles, like boats, should always keep their up-side up. This can be done by
1161 // enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to
1162 // the world z-axis (a.k.a. "up"). To take advantage of this feature you would set the
1163 // VEHICLE_VERTICAL_ATTRACTION_TIMESCALE to control the period of the spring frequency,
1164 // and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An
1165 // efficiency of 0.0 will cause the spring to wobble around its equilibrium, while an
1166 // efficiency of 1.0 will cause the spring to reach its equilibrium with exponential decay.
1167 public Vector3 ComputeAngularVerticalAttraction()
1168 {
1169 Vector3 ret = Vector3.Zero;
1170
1171 // If vertical attaction timescale is reasonable
1172 if (m_verticalAttractionTimescale < m_verticalAttractionCutoff)
1173 {
1174 // Take a vector pointing up and convert it from world to vehicle relative coords.
1175 Vector3 verticalError = Vector3.UnitZ * VehicleOrientation;
1176
1177 // If vertical attraction correction is needed, the vector that was pointing up (UnitZ)
1178 // is now:
1179 // leaning to one side: rotated around the X axis with the Y value going
1180 // from zero (nearly straight up) to one (completely to the side)) or
1181 // leaning front-to-back: rotated around the Y axis with the value of X being between
1182 // zero and one.
1183 // The value of Z is how far the rotation is off with 1 meaning none and 0 being 90 degrees.
1184
1185 // Y error means needed rotation around X axis and visa versa.
1186 // Since the error goes from zero to one, the asin is the corresponding angle.
1187 ret.X = (float)Math.Asin(verticalError.Y);
1188 // (Tilt forward (positive X) needs to tilt back (rotate negative) around Y axis.)
1189 ret.Y = -(float)Math.Asin(verticalError.X);
1190
1191 // If verticalError.Z is negative, the vehicle is upside down. Add additional push.
1192 if (verticalError.Z < 0f)
1193 {
1194 ret.X += PIOverFour;
1195 ret.Y += PIOverFour;
1196 }
1197
1198 // 'ret' is now the necessary velocity to correct tilt in one second.
1199 // Correction happens over a number of seconds.
1200 Vector3 unscaledContrib = ret;
1201 ret /= m_verticalAttractionTimescale;
1202
1203 VDetailLog("{0}, MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},eff={3},ts={4},vertAttr={5}",
1204 Prim.LocalID, verticalError, unscaledContrib, m_verticalAttractionEfficiency, m_verticalAttractionTimescale, ret);
1205 }
1206 return ret;
1207 }
1208
1209 // Return the angular correction to correct the direction the vehicle is pointing to be
1210 // the direction is should want to be pointing.
1211 // The vehicle is moving in some direction and correct its orientation to it is pointing
1212 // in that direction.
1213 // TODO: implement reference frame.
1214 public Vector3 ComputeAngularDeflection()
1215 {
1216 Vector3 ret = Vector3.Zero;
1217 return ret; // DEBUG DEBUG DEBUG
1218 // Disable angular deflection for the moment.
1219 // Since angularMotorUp and angularDeflection are computed independently, they will calculate
1220 // approximately the same X or Y correction. When added together (when contributions are combined)
1221 // this creates an over-correction and then wabbling as the target is overshot.
1222 // TODO: rethink how the different correction computations inter-relate.
1223
1224 if (m_angularDeflectionEfficiency != 0)
1225 {
1226 // The direction the vehicle is moving
1227 Vector3 movingDirection = VehicleVelocity;
1228 movingDirection.Normalize();
1229
1230 // The direction the vehicle is pointing
1231 Vector3 pointingDirection = Vector3.UnitX * VehicleOrientation;
1232 pointingDirection.Normalize();
1233
1234 // The difference between what is and what should be.
1235 Vector3 deflectionError = movingDirection - pointingDirection;
1236
1237 // Don't try to correct very large errors (not our job)
1238 if (Math.Abs(deflectionError.X) > PIOverFour) deflectionError.X = 0f;
1239 if (Math.Abs(deflectionError.Y) > PIOverFour) deflectionError.Y = 0f;
1240 if (Math.Abs(deflectionError.Z) > PIOverFour) deflectionError.Z = 0f;
1241
1242 // ret = m_angularDeflectionCorrectionMotor(1f, deflectionError);
1243
1244 // Scale the correction by recovery timescale and efficiency
1245 ret = (-deflectionError) * m_angularDeflectionEfficiency;
1246 ret /= m_angularDeflectionTimescale;
1247
1248 VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}",
1249 Prim.LocalID, movingDirection, pointingDirection, deflectionError, ret);
1250 VDetailLog("{0}, MoveAngular,Deflection,fwdSpd={1},defEff={2},defTS={3}",
1251 Prim.LocalID, VehicleForwardSpeed, m_angularDeflectionEfficiency, m_angularDeflectionTimescale);
1252 }
1253 return ret;
1254 }
1255
1256 // Return an angular change to rotate the vehicle around the Z axis when the vehicle
1257 // is tipped around the X axis.
1258 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
1259 // The vertical attractor feature must be enabled in order for the banking behavior to
1260 // function. The way banking works is this: a rotation around the vehicle's roll-axis will
1261 // produce a angular velocity around the yaw-axis, causing the vehicle to turn. The magnitude
1262 // of the yaw effect will be proportional to the
1263 // VEHICLE_BANKING_EFFICIENCY, the angle of the roll rotation, and sometimes the vehicle's
1264 // velocity along its preferred axis of motion.
1265 // The VEHICLE_BANKING_EFFICIENCY can vary between -1 and +1. When it is positive then any
1266 // positive rotation (by the right-hand rule) about the roll-axis will effect a
1267 // (negative) torque around the yaw-axis, making it turn to the right--that is the
1268 // vehicle will lean into the turn, which is how real airplanes and motorcycle's work.
1269 // Negating the banking coefficient will make it so that the vehicle leans to the
1270 // outside of the turn (not very "physical" but might allow interesting vehicles so why not?).
1271 // The VEHICLE_BANKING_MIX is a fake (i.e. non-physical) parameter that is useful for making
1272 // banking vehicles do what you want rather than what the laws of physics allow.
1273 // For example, consider a real motorcycle...it must be moving forward in order for
1274 // it to turn while banking, however video-game motorcycles are often configured
1275 // to turn in place when at a dead stop--because they are often easier to control
1276 // that way using the limited interface of the keyboard or game controller. The
1277 // VEHICLE_BANKING_MIX enables combinations of both realistic and non-realistic
1278 // banking by functioning as a slider between a banking that is correspondingly
1279 // totally static (0.0) and totally dynamic (1.0). By "static" we mean that the
1280 // banking effect depends only on the vehicle's rotation about its roll-axis compared
1281 // to "dynamic" where the banking is also proportional to its velocity along its
1282 // roll-axis. Finding the best value of the "mixture" will probably require trial and error.
1283 // The time it takes for the banking behavior to defeat a preexisting angular velocity about the
1284 // world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to
1285 // bank quickly then give it a banking timescale of about a second or less, otherwise you can
1286 // make a sluggish vehicle by giving it a timescale of several seconds.
1287 public Vector3 ComputeAngularBanking()
1288 {
1289 Vector3 ret = Vector3.Zero;
1290
1291 if (m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff)
1292 {
1293 // This works by rotating a unit vector to the orientation of the vehicle. The
1294 // roll (tilt) will be Y component of a tilting Z vector (zero for no tilt
1295 // up to one for full over).
1296 Vector3 rollComponents = Vector3.UnitZ * VehicleOrientation;
1297
1298 // Figure out the yaw value for this much roll.
1299 float turnComponent = rollComponents.Y * rollComponents.Y * m_bankingEfficiency;
1300 // Keep the sign
1301 if (rollComponents.Y < 0f)
1302 turnComponent = -turnComponent;
1303
1304 // TODO: there must be a better computation of the banking force.
1305 float bankingTurnForce = turnComponent;
1306
1307 // actual error = static turn error + dynamic turn error
1308 float mixedBankingError = bankingTurnForce * (1f - m_bankingMix) + bankingTurnForce * m_bankingMix * VehicleForwardSpeed;
1309 // TODO: the banking effect should not go to infinity but what to limit it to?
1310 mixedBankingError = ClampInRange(-20f, mixedBankingError, 20f);
1311
1312 // Build the force vector to change rotation from what it is to what it should be
1313 ret.Z = -mixedBankingError;
1314
1315 // Don't do it all at once.
1316 ret /= m_bankingTimescale;
1317
1318 VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},turnComp={3},bankErr={4},mixedBankErr={5},ret={6}",
1319 Prim.LocalID, rollComponents, VehicleForwardSpeed, turnComponent, bankingTurnForce, mixedBankingError, ret);
1320 }
1321 return ret;
1322 }
1323
1324 // This is from previous instantiations of XXXDynamics.cs.
1325 // Applies roll reference frame.
1326 // TODO: is this the right way to separate the code to do this operation?
1327 // Should this be in MoveAngular()?
1328 internal void LimitRotation(float timestep)
1329 {
1330 Quaternion rotq = VehicleOrientation;
1331 Quaternion m_rot = rotq;
1332 if (m_RollreferenceFrame != Quaternion.Identity)
1333 {
1334 if (rotq.X >= m_RollreferenceFrame.X)
1335 {
1336 m_rot.X = rotq.X - (m_RollreferenceFrame.X / 2);
1337 }
1338 if (rotq.Y >= m_RollreferenceFrame.Y)
1339 {
1340 m_rot.Y = rotq.Y - (m_RollreferenceFrame.Y / 2);
1341 }
1342 if (rotq.X <= -m_RollreferenceFrame.X)
1343 {
1344 m_rot.X = rotq.X + (m_RollreferenceFrame.X / 2);
1345 }
1346 if (rotq.Y <= -m_RollreferenceFrame.Y)
1347 {
1348 m_rot.Y = rotq.Y + (m_RollreferenceFrame.Y / 2);
1349 }
1350 }
1351 if ((m_flags & VehicleFlag.LOCK_ROTATION) != 0)
1352 {
1353 m_rot.X = 0;
1354 m_rot.Y = 0;
1355 }
1356 if (rotq != m_rot)
1357 {
1358 VehicleOrientation = m_rot;
1359 VDetailLog("{0}, LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot);
1360 }
1361
1362 }
1363
1364 private float ClampInRange(float low, float val, float high)
1365 {
1366 return Math.Max(low, Math.Min(val, high));
1367 // return Utils.Clamp(val, low, high);
1368 }
1369
1370 // Invoke the detailed logger and output something if it's enabled.
1371 private void VDetailLog(string msg, params Object[] args)
1372 {
1373 if (Prim.PhysicsScene.VehicleLoggingEnabled)
1374 Prim.PhysicsScene.DetailLog(msg, args);
1375 }
1376 }
1377}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSLinkset.cs
new file mode 100644
index 0000000..845a113
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSLinkset.cs
@@ -0,0 +1,329 @@
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 public float LinksetMass { get; protected set; }
101
102 public virtual bool LinksetIsColliding { get { return false; } }
103
104 public OMV.Vector3 CenterOfMass
105 {
106 get { return ComputeLinksetCenterOfMass(); }
107 }
108
109 public OMV.Vector3 GeometricCenter
110 {
111 get { return ComputeLinksetGeometricCenter(); }
112 }
113
114 protected BSLinkset(BSScene scene, BSPhysObject parent)
115 {
116 // A simple linkset of one (no children)
117 LinksetID = m_nextLinksetID++;
118 // We create LOTS of linksets.
119 if (m_nextLinksetID <= 0)
120 m_nextLinksetID = 1;
121 PhysicsScene = scene;
122 LinksetRoot = parent;
123 m_children = new HashSet<BSPhysObject>();
124 LinksetMass = parent.RawMass;
125 Rebuilding = false;
126 }
127
128 // Link to a linkset where the child knows the parent.
129 // Parent changing should not happen so do some sanity checking.
130 // We return the parent's linkset so the child can track its membership.
131 // Called at runtime.
132 public BSLinkset AddMeToLinkset(BSPhysObject child)
133 {
134 lock (m_linksetActivityLock)
135 {
136 // Don't add the root to its own linkset
137 if (!IsRoot(child))
138 AddChildToLinkset(child);
139 LinksetMass = ComputeLinksetMass();
140 }
141 return this;
142 }
143
144 // Remove a child from a linkset.
145 // Returns a new linkset for the child which is a linkset of one (just the
146 // orphened child).
147 // Called at runtime.
148 public BSLinkset RemoveMeFromLinkset(BSPhysObject child)
149 {
150 lock (m_linksetActivityLock)
151 {
152 if (IsRoot(child))
153 {
154 // Cannot remove the root from a linkset.
155 return this;
156 }
157 RemoveChildFromLinkset(child);
158 LinksetMass = ComputeLinksetMass();
159 }
160
161 // The child is down to a linkset of just itself
162 return BSLinkset.Factory(PhysicsScene, child);
163 }
164
165 // Return 'true' if the passed object is the root object of this linkset
166 public bool IsRoot(BSPhysObject requestor)
167 {
168 return (requestor.LocalID == LinksetRoot.LocalID);
169 }
170
171 public int NumberOfChildren { get { return m_children.Count; } }
172
173 // Return 'true' if this linkset has any children (more than the root member)
174 public bool HasAnyChildren { get { return (m_children.Count > 0); } }
175
176 // Return 'true' if this child is in this linkset
177 public bool HasChild(BSPhysObject child)
178 {
179 bool ret = false;
180 lock (m_linksetActivityLock)
181 {
182 ret = m_children.Contains(child);
183 /* Safer version but the above should work
184 foreach (BSPhysObject bp in m_children)
185 {
186 if (child.LocalID == bp.LocalID)
187 {
188 ret = true;
189 break;
190 }
191 }
192 */
193 }
194 return ret;
195 }
196
197 // Perform an action on each member of the linkset including root prim.
198 // Depends on the action on whether this should be done at taint time.
199 public delegate bool ForEachMemberAction(BSPhysObject obj);
200 public virtual bool ForEachMember(ForEachMemberAction action)
201 {
202 bool ret = false;
203 lock (m_linksetActivityLock)
204 {
205 action(LinksetRoot);
206 foreach (BSPhysObject po in m_children)
207 {
208 if (action(po))
209 break;
210 }
211 }
212 return ret;
213 }
214
215 // I am the root of a linkset and a new child is being added
216 // Called while LinkActivity is locked.
217 protected abstract void AddChildToLinkset(BSPhysObject child);
218
219 // I am the root of a linkset and one of my children is being removed.
220 // Safe to call even if the child is not really in my linkset.
221 protected abstract void RemoveChildFromLinkset(BSPhysObject child);
222
223 // When physical properties are changed the linkset needs to recalculate
224 // its internal properties.
225 // May be called at runtime or taint-time.
226 public virtual void Refresh(BSPhysObject requestor)
227 {
228 LinksetMass = ComputeLinksetMass();
229 }
230
231 // Flag denoting the linkset is in the process of being rebuilt.
232 // Used to know not the schedule a rebuild in the middle of a rebuild.
233 protected bool Rebuilding { get; set; }
234
235 // The object is going dynamic (physical). Do any setup necessary
236 // for a dynamic linkset.
237 // Only the state of the passed object can be modified. The rest of the linkset
238 // has not yet been fully constructed.
239 // Return 'true' if any properties updated on the passed object.
240 // Called at taint-time!
241 public abstract bool MakeDynamic(BSPhysObject child);
242
243 // The object is going static (non-physical). Do any setup necessary
244 // for a static linkset.
245 // Return 'true' if any properties updated on the passed object.
246 // Called at taint-time!
247 public abstract bool MakeStatic(BSPhysObject child);
248
249 // Called when a parameter update comes from the physics engine for any object
250 // of the linkset is received.
251 // Passed flag is update came from physics engine (true) or the user (false).
252 // Called at taint-time!!
253 public abstract void UpdateProperties(BSPhysObject physObject, bool physicalUpdate);
254
255 // Routine used when rebuilding the body of the root of the linkset
256 // Destroy all the constraints have have been made to root.
257 // This is called when the root body is changing.
258 // Returns 'true' of something was actually removed and would need restoring
259 // Called at taint-time!!
260 public abstract bool RemoveBodyDependencies(BSPrim child);
261
262 // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
263 // this routine will restore the removed constraints.
264 // Called at taint-time!!
265 public abstract void RestoreBodyDependencies(BSPrim child);
266
267 // ================================================================
268 protected virtual float ComputeLinksetMass()
269 {
270 float mass = LinksetRoot.RawMass;
271 if (HasAnyChildren)
272 {
273 lock (m_linksetActivityLock)
274 {
275 foreach (BSPhysObject bp in m_children)
276 {
277 mass += bp.RawMass;
278 }
279 }
280 }
281 return mass;
282 }
283
284 protected virtual OMV.Vector3 ComputeLinksetCenterOfMass()
285 {
286 OMV.Vector3 com;
287 lock (m_linksetActivityLock)
288 {
289 com = LinksetRoot.Position * LinksetRoot.RawMass;
290 float totalMass = LinksetRoot.RawMass;
291
292 foreach (BSPhysObject bp in m_children)
293 {
294 com += bp.Position * bp.RawMass;
295 totalMass += bp.RawMass;
296 }
297 if (totalMass != 0f)
298 com /= totalMass;
299 }
300
301 return com;
302 }
303
304 protected virtual OMV.Vector3 ComputeLinksetGeometricCenter()
305 {
306 OMV.Vector3 com;
307 lock (m_linksetActivityLock)
308 {
309 com = LinksetRoot.Position;
310
311 foreach (BSPhysObject bp in m_children)
312 {
313 com += bp.Position * bp.RawMass;
314 }
315 com /= (m_children.Count + 1);
316 }
317
318 return com;
319 }
320
321 // Invoke the detailed logger and output something if it's enabled.
322 protected void DetailLog(string msg, params Object[] args)
323 {
324 if (PhysicsScene.PhysicsLogging.Enabled)
325 PhysicsScene.DetailLog(msg, args);
326 }
327
328}
329}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs
new file mode 100644
index 0000000..9a977e6
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs
@@ -0,0 +1,397 @@
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 base.Refresh(requestor);
93
94 // Something changed so do the rebuilding thing
95 // ScheduleRebuild();
96 }
97
98 // Schedule a refresh to happen after all the other taint processing.
99 private void ScheduleRebuild(BSPhysObject requestor)
100 {
101 DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1}",
102 requestor.LocalID, Rebuilding);
103 // When rebuilding, it is possible to set properties that would normally require a rebuild.
104 // If already rebuilding, don't request another rebuild.
105 if (!Rebuilding)
106 {
107 PhysicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate()
108 {
109 if (HasAnyChildren)
110 RecomputeLinksetCompound();
111 });
112 }
113 }
114
115 // The object is going dynamic (physical). Do any setup necessary
116 // for a dynamic linkset.
117 // Only the state of the passed object can be modified. The rest of the linkset
118 // has not yet been fully constructed.
119 // Return 'true' if any properties updated on the passed object.
120 // Called at taint-time!
121 public override bool MakeDynamic(BSPhysObject child)
122 {
123 bool ret = false;
124 DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child));
125 if (IsRoot(child))
126 {
127 // The root is going dynamic. Make sure mass is properly set.
128 ScheduleRebuild(LinksetRoot);
129 }
130 else
131 {
132 // The origional prims are removed from the world as the shape of the root compound
133 // shape takes over.
134 BulletSimAPI.AddToCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
135 BulletSimAPI.ForceActivationState2(child.PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
136 // We don't want collisions from the old linkset children.
137 BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
138
139 child.PhysBody.collisionType = CollisionType.LinksetChild;
140
141 ret = true;
142 }
143 return ret;
144 }
145
146 // The object is going static (non-physical). Do any setup necessary for a static linkset.
147 // Return 'true' if any properties updated on the passed object.
148 // This doesn't normally happen -- OpenSim removes the objects from the physical
149 // world if it is a static linkset.
150 // Called at taint-time!
151 public override bool MakeStatic(BSPhysObject child)
152 {
153 bool ret = false;
154 DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child));
155 if (IsRoot(child))
156 {
157 ScheduleRebuild(LinksetRoot);
158 }
159 else
160 {
161 // The non-physical children can come back to life.
162 BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
163
164 child.PhysBody.collisionType = CollisionType.LinksetChild;
165
166 // Don't force activation so setting of DISABLE_SIMULATION can stay if used.
167 BulletSimAPI.Activate2(child.PhysBody.ptr, false);
168 ret = true;
169 }
170 return ret;
171 }
172
173 public override void UpdateProperties(BSPhysObject updated, bool physicalUpdate)
174 {
175 // The user moving a child around requires the rebuilding of the linkset compound shape
176 // One problem is this happens when a border is crossed -- the simulator implementation
177 // is to store the position into the group which causes the move of the object
178 // but it also means all the child positions get updated.
179 // What would cause an unnecessary rebuild so we make sure the linkset is in a
180 // region before bothering to do a rebuild.
181 if (!IsRoot(updated)
182 && !physicalUpdate
183 && PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
184 {
185 updated.LinksetInfo = null;
186 ScheduleRebuild(updated);
187 }
188 }
189
190 // Routine called when rebuilding the body of some member of the linkset.
191 // Since we don't keep in world relationships, do nothing unless it's a child changing.
192 // Returns 'true' of something was actually removed and would need restoring
193 // Called at taint-time!!
194 public override bool RemoveBodyDependencies(BSPrim child)
195 {
196 bool ret = false;
197
198 DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}",
199 child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(), IsRoot(child));
200
201 if (!IsRoot(child))
202 {
203 // Because it is a convenient time, recompute child world position and rotation based on
204 // its position in the linkset.
205 RecomputeChildWorldPosition(child, true);
206 }
207
208 // Cannot schedule a refresh/rebuild here because this routine is called when
209 // the linkset is being rebuilt.
210 // InternalRefresh(LinksetRoot);
211
212 return ret;
213 }
214
215 // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
216 // this routine will restore the removed constraints.
217 // Called at taint-time!!
218 public override void RestoreBodyDependencies(BSPrim child)
219 {
220 }
221
222 // When the linkset is built, the child shape is added to the compound shape relative to the
223 // root shape. The linkset then moves around but this does not move the actual child
224 // prim. The child prim's location must be recomputed based on the location of the root shape.
225 private void RecomputeChildWorldPosition(BSPhysObject child, bool inTaintTime)
226 {
227 BSLinksetCompoundInfo lci = child.LinksetInfo as BSLinksetCompoundInfo;
228 if (lci != null)
229 {
230 if (inTaintTime)
231 {
232 OMV.Vector3 oldPos = child.RawPosition;
233 child.ForcePosition = LinksetRoot.RawPosition + lci.OffsetPos;
234 child.ForceOrientation = LinksetRoot.RawOrientation * lci.OffsetRot;
235 DetailLog("{0},BSLinksetCompound.RecomputeChildWorldPosition,oldPos={1},lci={2},newPos={3}",
236 child.LocalID, oldPos, lci, child.RawPosition);
237 }
238 else
239 {
240 // TaintedObject is not used here so the raw position is set now and not at taint-time.
241 child.Position = LinksetRoot.RawPosition + lci.OffsetPos;
242 child.Orientation = LinksetRoot.RawOrientation * lci.OffsetRot;
243 }
244 }
245 else
246 {
247 // This happens when children have been added to the linkset but the linkset
248 // has not been constructed yet. So like, at taint time, adding children to a linkset
249 // and then changing properties of the children (makePhysical, for instance)
250 // but the post-print action of actually rebuilding the linkset has not yet happened.
251 // PhysicsScene.Logger.WarnFormat("{0} Restoring linkset child position failed because of no relative position computed. ID={1}",
252 // LogHeader, child.LocalID);
253 DetailLog("{0},BSLinksetCompound.recomputeChildWorldPosition,noRelativePositonInfo", child.LocalID);
254 }
255 }
256
257 // ================================================================
258
259 // Add a new child to the linkset.
260 // Called while LinkActivity is locked.
261 protected override void AddChildToLinkset(BSPhysObject child)
262 {
263 if (!HasChild(child))
264 {
265 m_children.Add(child);
266
267 DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
268
269 // Rebuild the compound shape with the new child shape included
270 ScheduleRebuild(child);
271 }
272 return;
273 }
274
275 // Remove the specified child from the linkset.
276 // Safe to call even if the child is not really in the linkset.
277 protected override void RemoveChildFromLinkset(BSPhysObject child)
278 {
279 if (m_children.Remove(child))
280 {
281 DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
282 child.LocalID,
283 LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(),
284 child.LocalID, child.PhysBody.ptr.ToString());
285
286 // Cause the child's body to be rebuilt and thus restored to normal operation
287 RecomputeChildWorldPosition(child, false);
288 child.ForceBodyShapeRebuild(false);
289
290 if (!HasAnyChildren)
291 {
292 // The linkset is now empty. The root needs rebuilding.
293 LinksetRoot.ForceBodyShapeRebuild(false);
294 }
295 else
296 {
297 // Rebuild the compound shape with the child removed
298 ScheduleRebuild(child);
299 }
300 }
301 return;
302 }
303
304 // Called before the simulation step to make sure the compound based linkset
305 // is all initialized.
306 // Constraint linksets are rebuilt every time.
307 // Note that this works for rebuilding just the root after a linkset is taken apart.
308 // Called at taint time!!
309 private void RecomputeLinksetCompound()
310 {
311 try
312 {
313 // Suppress rebuilding while rebuilding
314 Rebuilding = true;
315
316 // Cause the root shape to be rebuilt as a compound object with just the root in it
317 LinksetRoot.ForceBodyShapeRebuild(true);
318
319 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}",
320 LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren);
321
322 // Add a shape for each of the other children in the linkset
323 ForEachMember(delegate(BSPhysObject cPrim)
324 {
325 if (!IsRoot(cPrim))
326 {
327 // Compute the displacement of the child from the root of the linkset.
328 // This info is saved in the child prim so the relationship does not
329 // change over time and the new child position can be computed
330 // when the linkset is being disassembled (the linkset may have moved).
331 BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo;
332 if (lci == null)
333 {
334 // Each child position and rotation is given relative to the root.
335 OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
336 OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation;
337 OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation;
338
339 // Save relative position for recomputing child's world position after moving linkset.
340 lci = new BSLinksetCompoundInfo(displacementPos, displacementRot);
341 cPrim.LinksetInfo = lci;
342 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci);
343 }
344
345 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}",
346 LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, lci.OffsetPos, lci.OffsetRot);
347
348 if (cPrim.PhysShape.isNativeShape)
349 {
350 // A native shape is turning into a hull collision shape because native
351 // shapes are not shared so we have to hullify it so it will be tracked
352 // and freed at the correct time. This also solves the scaling problem
353 // (native shapes scaled but hull/meshes are assumed to not be).
354 // TODO: decide of the native shape can just be used in the compound shape.
355 // Use call to CreateGeomNonSpecial().
356 BulletShape saveShape = cPrim.PhysShape;
357 cPrim.PhysShape.Clear(); // Don't let the create free the child's shape
358 // PhysicsScene.Shapes.CreateGeomNonSpecial(true, cPrim, null);
359 PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null);
360 BulletShape newShape = cPrim.PhysShape;
361 cPrim.PhysShape = saveShape;
362 BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, lci.OffsetPos, lci.OffsetRot);
363 }
364 else
365 {
366 // For the shared shapes (meshes and hulls), just use the shape in the child.
367 // The reference count added here will be decremented when the compound shape
368 // is destroyed in BSShapeCollection (the child shapes are looped over and dereferenced).
369 if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape))
370 {
371 PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}",
372 LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape);
373 }
374 BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, lci.OffsetPos, lci.OffsetRot);
375 }
376 }
377 return false; // 'false' says to move onto the next child in the list
378 });
379
380 // With all of the linkset packed into the root prim, it has the mass of everyone.
381 LinksetMass = LinksetMass;
382 LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true);
383 }
384 finally
385 {
386 Rebuilding = false;
387 }
388
389 BulletSimAPI.RecalculateCompoundShapeLocalAabb2(LinksetRoot.PhysShape.ptr);
390
391 // DEBUG: see of inter-linkset collisions are causing problems for constraint linksets.
392 // BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
393 // (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
394
395 }
396}
397} \ 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..46ff99f
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetConstraints.cs
@@ -0,0 +1,316 @@
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 base.Refresh(requestor);
50
51 // Queue to happen after all the other taint processing
52 PhysicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate()
53 {
54 if (HasAnyChildren && IsRoot(requestor))
55 RecomputeLinksetConstraints();
56 });
57 }
58
59 // The object is going dynamic (physical). Do any setup necessary
60 // for a dynamic linkset.
61 // Only the state of the passed object can be modified. The rest of the linkset
62 // has not yet been fully constructed.
63 // Return 'true' if any properties updated on the passed object.
64 // Called at taint-time!
65 public override bool MakeDynamic(BSPhysObject child)
66 {
67 // What is done for each object in BSPrim is what we want.
68 return false;
69 }
70
71 // The object is going static (non-physical). Do any setup necessary for a static linkset.
72 // Return 'true' if any properties updated on the passed object.
73 // This doesn't normally happen -- OpenSim removes the objects from the physical
74 // world if it is a static linkset.
75 // Called at taint-time!
76 public override bool MakeStatic(BSPhysObject child)
77 {
78 // What is done for each object in BSPrim is what we want.
79 return false;
80 }
81
82 // Called at taint-time!!
83 public override void UpdateProperties(BSPhysObject updated, bool inTaintTime)
84 {
85 // Nothing to do for constraints on property updates
86 }
87
88 // Routine called when rebuilding the body of some member of the linkset.
89 // Destroy all the constraints have have been made to root and set
90 // up to rebuild the constraints before the next simulation step.
91 // Returns 'true' of something was actually removed and would need restoring
92 // Called at taint-time!!
93 public override bool RemoveBodyDependencies(BSPrim child)
94 {
95 bool ret = false;
96
97 DetailLog("{0},BSLinksetConstraint.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}",
98 child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString());
99
100 lock (m_linksetActivityLock)
101 {
102 // Just undo all the constraints for this linkset. Rebuild at the end of the step.
103 ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot);
104 // Cause the constraints, et al to be rebuilt before the next simulation step.
105 Refresh(LinksetRoot);
106 }
107 return ret;
108 }
109
110 // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
111 // this routine will restore the removed constraints.
112 // Called at taint-time!!
113 public override void RestoreBodyDependencies(BSPrim child)
114 {
115 // The Refresh operation queued by RemoveBodyDependencies() will build any missing constraints.
116 }
117
118 // ================================================================
119
120 // Add a new child to the linkset.
121 // Called while LinkActivity is locked.
122 protected override void AddChildToLinkset(BSPhysObject child)
123 {
124 if (!HasChild(child))
125 {
126 m_children.Add(child);
127
128 DetailLog("{0},BSLinksetConstraints.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
129
130 // Cause constraints and assorted properties to be recomputed before the next simulation step.
131 Refresh(LinksetRoot);
132 }
133 return;
134 }
135
136 // Remove the specified child from the linkset.
137 // Safe to call even if the child is not really in my linkset.
138 protected override void RemoveChildFromLinkset(BSPhysObject child)
139 {
140 if (m_children.Remove(child))
141 {
142 BSPhysObject rootx = LinksetRoot; // capture the root and body as of now
143 BSPhysObject childx = child;
144
145 DetailLog("{0},BSLinksetConstraints.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
146 childx.LocalID,
147 rootx.LocalID, rootx.PhysBody.ptr.ToString(),
148 childx.LocalID, childx.PhysBody.ptr.ToString());
149
150 PhysicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate()
151 {
152 PhysicallyUnlinkAChildFromRoot(rootx, childx);
153 });
154 // See that the linkset parameters are recomputed at the end of the taint time.
155 Refresh(LinksetRoot);
156 }
157 else
158 {
159 // Non-fatal occurance.
160 // PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader);
161 }
162 return;
163 }
164
165 // Create a constraint between me (root of linkset) and the passed prim (the child).
166 // Called at taint time!
167 private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
168 {
169 // Don't build the constraint when asked. Put it off until just before the simulation step.
170 Refresh(rootPrim);
171 }
172
173 private BSConstraint BuildConstraint(BSPhysObject rootPrim, BSPhysObject childPrim)
174 {
175 // Zero motion for children so they don't interpolate
176 childPrim.ZeroMotion(true);
177
178 // Relative position normalized to the root prim
179 // Essentually a vector pointing from center of rootPrim to center of childPrim
180 OMV.Vector3 childRelativePosition = childPrim.Position - rootPrim.Position;
181
182 // real world coordinate of midpoint between the two objects
183 OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2);
184
185 DetailLog("{0},BSLinksetConstraint.BuildConstraint,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}",
186 rootPrim.LocalID,
187 rootPrim.LocalID, rootPrim.PhysBody.ptr.ToString(),
188 childPrim.LocalID, childPrim.PhysBody.ptr.ToString(),
189 rootPrim.Position, childPrim.Position, midPoint);
190
191 // create a constraint that allows no freedom of movement between the two objects
192 // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
193
194 BSConstraint6Dof constrain = new BSConstraint6Dof(
195 PhysicsScene.World, rootPrim.PhysBody, childPrim.PhysBody, midPoint, true, true );
196 // PhysicsScene.World, childPrim.BSBody, rootPrim.BSBody, midPoint, true, true );
197
198 /* NOTE: below is an attempt to build constraint with full frame computation, etc.
199 * Using the midpoint is easier since it lets the Bullet code manipulate the transforms
200 * of the objects.
201 * Code left for future programmers.
202 // ==================================================================================
203 // relative position normalized to the root prim
204 OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation);
205 OMV.Vector3 childRelativePosition = (childPrim.Position - rootPrim.Position) * invThisOrientation;
206
207 // relative rotation of the child to the parent
208 OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation;
209 OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation);
210
211 DetailLog("{0},BSLinksetConstraint.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID);
212 BS6DofConstraint constrain = new BS6DofConstraint(
213 PhysicsScene.World, rootPrim.Body, childPrim.Body,
214 OMV.Vector3.Zero,
215 OMV.Quaternion.Inverse(rootPrim.Orientation),
216 OMV.Vector3.Zero,
217 OMV.Quaternion.Inverse(childPrim.Orientation),
218 true,
219 true
220 );
221 // ==================================================================================
222 */
223
224 PhysicsScene.Constraints.AddConstraint(constrain);
225
226 // zero linear and angular limits makes the objects unable to move in relation to each other
227 constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
228 constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
229
230 // tweek the constraint to increase stability
231 constrain.UseFrameOffset(BSParam.BoolNumeric(BSParam.LinkConstraintUseFrameOffset));
232 constrain.TranslationalLimitMotor(BSParam.BoolNumeric(BSParam.LinkConstraintEnableTransMotor),
233 BSParam.LinkConstraintTransMotorMaxVel,
234 BSParam.LinkConstraintTransMotorMaxForce);
235 constrain.SetCFMAndERP(BSParam.LinkConstraintCFM, BSParam.LinkConstraintERP);
236 if (BSParam.LinkConstraintSolverIterations != 0f)
237 {
238 constrain.SetSolverIterations(BSParam.LinkConstraintSolverIterations);
239 }
240 return constrain;
241 }
242
243 // Remove linkage between the linkset root and a particular child
244 // The root and child bodies are passed in because we need to remove the constraint between
245 // the bodies that were present at unlink time.
246 // Called at taint time!
247 private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
248 {
249 bool ret = false;
250 DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}",
251 rootPrim.LocalID,
252 rootPrim.LocalID, rootPrim.PhysBody.ptr.ToString(),
253 childPrim.LocalID, childPrim.PhysBody.ptr.ToString());
254
255 // Find the constraint for this link and get rid of it from the overall collection and from my list
256 if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody))
257 {
258 // Make the child refresh its location
259 BulletSimAPI.PushUpdate2(childPrim.PhysBody.ptr);
260 ret = true;
261 }
262
263 return ret;
264 }
265
266 // Remove linkage between myself and any possible children I might have.
267 // Returns 'true' of any constraints were destroyed.
268 // Called at taint time!
269 private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim)
270 {
271 DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID);
272
273 return PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody);
274 }
275
276 // Call each of the constraints that make up this linkset and recompute the
277 // various transforms and variables. Create constraints of not created yet.
278 // Called before the simulation step to make sure the constraint based linkset
279 // is all initialized.
280 // Called at taint time!!
281 private void RecomputeLinksetConstraints()
282 {
283 float linksetMass = LinksetMass;
284 LinksetRoot.UpdatePhysicalMassProperties(linksetMass, true);
285
286 // DEBUG: see of inter-linkset collisions are causing problems
287 // BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
288 // (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
289 DetailLog("{0},BSLinksetConstraint.RecomputeLinksetConstraints,set,rBody={1},linksetMass={2}",
290 LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(), linksetMass);
291
292 foreach (BSPhysObject child in m_children)
293 {
294 // A child in the linkset physically shows the mass of the whole linkset.
295 // This allows Bullet to apply enough force on the child to move the whole linkset.
296 // (Also do the mass stuff before recomputing the constraint so mass is not zero.)
297 child.UpdatePhysicalMassProperties(linksetMass, true);
298
299 BSConstraint constrain;
300 if (!PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain))
301 {
302 // If constraint doesn't exist yet, create it.
303 constrain = BuildConstraint(LinksetRoot, child);
304 }
305 constrain.RecomputeConstraintVariables(linksetMass);
306
307 // DEBUG: see of inter-linkset collisions are causing problems
308 // BulletSimAPI.SetCollisionFilterMask2(child.BSBody.ptr,
309 // (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
310
311 // BulletSimAPI.DumpConstraint2(PhysicsScene.World.ptr, constrain.Constraint.ptr); // DEBUG DEBUG
312 }
313
314 }
315}
316}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSMaterials.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSMaterials.cs
new file mode 100644
index 0000000..d7941b6
--- /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",3.5f, 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.3f, 0.4f);
128 Attributes[(int)MaterialAttributes.Material.Glass + (int)MaterialAttributes.Material.NumberOfTypes] =
129 new MaterialAttributes("glassPhysical",dDensity, 0.2f, 0.7f);
130 Attributes[(int)MaterialAttributes.Material.Wood + (int)MaterialAttributes.Material.NumberOfTypes] =
131 new MaterialAttributes("woodPhysical",dDensity, 0.6f, 0.5f);
132 Attributes[(int)MaterialAttributes.Material.Flesh + (int)MaterialAttributes.Material.NumberOfTypes] =
133 new MaterialAttributes("fleshPhysical",dDensity, 0.9f, 0.3f);
134 Attributes[(int)MaterialAttributes.Material.Plastic + (int)MaterialAttributes.Material.NumberOfTypes] =
135 new MaterialAttributes("plasticPhysical",dDensity, 0.4f, 0.7f);
136 Attributes[(int)MaterialAttributes.Material.Rubber + (int)MaterialAttributes.Material.NumberOfTypes] =
137 new MaterialAttributes("rubberPhysical",dDensity, 0.9f, 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",3.5f, 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..5e93a03
--- /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.Heightmap,
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 3.5f,
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..689da7f
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSPhysObject.cs
@@ -0,0 +1,346 @@
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 // 'inWorld' true if the object has already been added to the dynamic world.
100 public abstract void UpdatePhysicalMassProperties(float mass, bool inWorld);
101
102 // The last value calculated for the prim's inertia
103 public OMV.Vector3 Inertia { get; set; }
104
105 // Reference to the physical body (btCollisionObject) of this object
106 public BulletBody PhysBody;
107 // Reference to the physical shape (btCollisionShape) of this object
108 public BulletShape PhysShape;
109
110 // 'true' if the mesh's underlying asset failed to build.
111 // This will keep us from looping after the first time the build failed.
112 public bool LastAssetBuildFailed { get; set; }
113
114 // The objects base shape information. Null if not a prim type shape.
115 public PrimitiveBaseShape BaseShape { get; protected set; }
116 // Some types of objects have preferred physical representations.
117 // Returns SHAPE_UNKNOWN if there is no preference.
118 public virtual BSPhysicsShapeType PreferredPhysicalShape
119 {
120 get { return BSPhysicsShapeType.SHAPE_UNKNOWN; }
121 }
122
123 // When the physical properties are updated, an EntityProperty holds the update values.
124 // Keep the current and last EntityProperties to enable computation of differences
125 // between the current update and the previous values.
126 public EntityProperties CurrentEntityProperties { get; set; }
127 public EntityProperties LastEntityProperties { get; set; }
128
129 public virtual OMV.Vector3 Scale { get; set; }
130 public abstract bool IsSolid { get; }
131 public abstract bool IsStatic { get; }
132
133 // Materialness
134 public MaterialAttributes.Material Material { get; private set; }
135 public override void SetMaterial(int material)
136 {
137 Material = (MaterialAttributes.Material)material;
138 }
139
140 // Stop all physical motion.
141 public abstract void ZeroMotion(bool inTaintTime);
142 public abstract void ZeroAngularMotion(bool inTaintTime);
143
144 // Step the vehicle simulation for this object. A NOOP if the vehicle was not configured.
145 public virtual void StepVehicle(float timeStep) { }
146
147 // Update the physical location and motion of the object. Called with data from Bullet.
148 public abstract void UpdateProperties(EntityProperties entprop);
149
150 public abstract OMV.Vector3 RawPosition { get; set; }
151 public abstract OMV.Vector3 ForcePosition { get; set; }
152
153 public abstract OMV.Quaternion RawOrientation { get; set; }
154 public abstract OMV.Quaternion ForceOrientation { get; set; }
155
156 // The system is telling us the velocity it wants to move at.
157 // protected OMV.Vector3 m_targetVelocity; // use the definition in PhysicsActor
158 public override OMV.Vector3 TargetVelocity
159 {
160 get { return m_targetVelocity; }
161 set
162 {
163 m_targetVelocity = value;
164 Velocity = value;
165 }
166 }
167 public abstract OMV.Vector3 ForceVelocity { get; set; }
168
169 public abstract OMV.Vector3 ForceRotationalVelocity { get; set; }
170
171 public abstract float ForceBuoyancy { get; set; }
172
173 public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
174
175 #region Collisions
176
177 // Requested number of milliseconds between collision events. Zero means disabled.
178 protected int SubscribedEventsMs { get; set; }
179 // Given subscription, the time that a collision may be passed up
180 protected int NextCollisionOkTime { get; set; }
181 // The simulation step that last had a collision
182 protected long CollidingStep { get; set; }
183 // The simulation step that last had a collision with the ground
184 protected long CollidingGroundStep { get; set; }
185 // The collision flags we think are set in Bullet
186 protected CollisionFlags CurrentCollisionFlags { get; set; }
187
188 // The collisions that have been collected this tick
189 protected CollisionEventUpdate CollisionCollection;
190
191 // The simulation step is telling this object about a collision.
192 // Return 'true' if a collision was processed and should be sent up.
193 // Called at taint time from within the Step() function
194 public virtual bool Collide(uint collidingWith, BSPhysObject collidee,
195 OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
196 {
197 bool ret = false;
198
199 // The following lines make IsColliding() and IsCollidingGround() work
200 CollidingStep = PhysicsScene.SimulationStep;
201 if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID)
202 {
203 CollidingGroundStep = PhysicsScene.SimulationStep;
204 }
205
206 // prims in the same linkset cannot collide with each other
207 if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID))
208 {
209 return ret;
210 }
211
212 // if someone has subscribed for collision events....
213 if (SubscribedEvents()) {
214 CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
215 DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}",
216 LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth);
217
218 ret = true;
219 }
220 return ret;
221 }
222
223 // Send the collected collisions into the simulator.
224 // Called at taint time from within the Step() function thus no locking problems
225 // with CollisionCollection and ObjectsWithNoMoreCollisions.
226 // Return 'true' if there were some actual collisions passed up
227 public virtual bool SendCollisions()
228 {
229 bool ret = true;
230 // If the 'no collision' call, force it to happen right now so quick collision_end
231 bool force = (CollisionCollection.Count == 0);
232
233 // throttle the collisions to the number of milliseconds specified in the subscription
234 if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime))
235 {
236 NextCollisionOkTime = PhysicsScene.SimulationNowTime + SubscribedEventsMs;
237
238 // We are called if we previously had collisions. If there are no collisions
239 // this time, send up one last empty event so OpenSim can sense collision end.
240 if (CollisionCollection.Count == 0)
241 {
242 // If I have no collisions this time, remove me from the list of objects with collisions.
243 ret = false;
244 }
245
246 // DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
247 base.SendCollisionUpdate(CollisionCollection);
248
249 // The CollisionCollection instance is passed around in the simulator.
250 // Make sure we don't have a handle to that one and that a new one is used for next time.
251 // This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here,
252 // a race condition is created for the other users of this instance.
253 CollisionCollection = new CollisionEventUpdate();
254 }
255 return ret;
256 }
257
258 // Subscribe for collision events.
259 // Parameter is the millisecond rate the caller wishes collision events to occur.
260 public override void SubscribeEvents(int ms) {
261 // DetailLog("{0},{1}.SubscribeEvents,subscribing,ms={2}", LocalID, TypeName, ms);
262 SubscribedEventsMs = ms;
263 if (ms > 0)
264 {
265 // make sure first collision happens
266 NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs);
267
268 PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate()
269 {
270 if (PhysBody.HasPhysicalBody)
271 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
272 });
273 }
274 else
275 {
276 // Subscribing for zero or less is the same as unsubscribing
277 UnSubscribeEvents();
278 }
279 }
280 public override void UnSubscribeEvents() {
281 // DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName);
282 SubscribedEventsMs = 0;
283 PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate()
284 {
285 // Make sure there is a body there because sometimes destruction happens in an un-ideal order.
286 if (PhysBody.HasPhysicalBody)
287 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
288 });
289 }
290 // Return 'true' if the simulator wants collision events
291 public override bool SubscribedEvents() {
292 return (SubscribedEventsMs > 0);
293 }
294
295 #endregion // Collisions
296
297 #region Per Simulation Step actions
298 // There are some actions that must be performed for a physical object before each simulation step.
299 // These actions are optional so, rather than scanning all the physical objects and asking them
300 // if they have anything to do, a physical object registers for an event call before the step is performed.
301 // This bookkeeping makes it easy to add, remove and clean up after all these registrations.
302 private Dictionary<string, BSScene.PreStepAction> RegisteredActions = new Dictionary<string, BSScene.PreStepAction>();
303 protected void RegisterPreStepAction(string op, uint id, BSScene.PreStepAction actn)
304 {
305 string identifier = op + "-" + id.ToString();
306 RegisteredActions[identifier] = actn;
307 PhysicsScene.BeforeStep += actn;
308 DetailLog("{0},BSPhysObject.RegisterPreStepAction,id={1}", LocalID, identifier);
309 }
310
311 // Unregister a pre step action. Safe to call if the action has not been registered.
312 protected void UnRegisterPreStepAction(string op, uint id)
313 {
314 string identifier = op + "-" + id.ToString();
315 bool removed = false;
316 if (RegisteredActions.ContainsKey(identifier))
317 {
318 PhysicsScene.BeforeStep -= RegisteredActions[identifier];
319 RegisteredActions.Remove(identifier);
320 removed = true;
321 }
322 DetailLog("{0},BSPhysObject.UnRegisterPreStepAction,id={1},removed={2}", LocalID, identifier, removed);
323 }
324
325 protected void UnRegisterAllPreStepActions()
326 {
327 foreach (KeyValuePair<string, BSScene.PreStepAction> kvp in RegisteredActions)
328 {
329 PhysicsScene.BeforeStep -= kvp.Value;
330 }
331 RegisteredActions.Clear();
332 DetailLog("{0},BSPhysObject.UnRegisterAllPreStepActions,", LocalID);
333 }
334
335
336 #endregion // Per Simulation Step actions
337
338 // High performance detailed logging routine used by the physical objects.
339 protected void DetailLog(string msg, params Object[] args)
340 {
341 if (PhysicsScene.PhysicsLogging.Enabled)
342 PhysicsScene.DetailLog(msg, args);
343 }
344
345}
346}
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..aadb5b2
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs
@@ -0,0 +1,1494 @@
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 Linkset.Refresh(this);
115
116 DetailLog("{0},BSPrim.constructor,call", LocalID);
117 // do the actual object creation at taint time
118 PhysicsScene.TaintedObject("BSPrim.create", delegate()
119 {
120 CreateGeomAndObject(true);
121
122 CurrentCollisionFlags = BulletSimAPI.GetCollisionFlags2(PhysBody.ptr);
123 });
124 }
125
126 // called when this prim is being destroyed and we should free all the resources
127 public override void Destroy()
128 {
129 // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID);
130 base.Destroy();
131
132 // Undo any links between me and any other object
133 BSPhysObject parentBefore = Linkset.LinksetRoot;
134 int childrenBefore = Linkset.NumberOfChildren;
135
136 Linkset = Linkset.RemoveMeFromLinkset(this);
137
138 DetailLog("{0},BSPrim.Destroy,call,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}",
139 LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
140
141 // Undo any vehicle properties
142 this.VehicleType = (int)Vehicle.TYPE_NONE;
143
144 PhysicsScene.TaintedObject("BSPrim.destroy", delegate()
145 {
146 DetailLog("{0},BSPrim.Destroy,taint,", LocalID);
147 // If there are physical body and shape, release my use of same.
148 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
149 PhysBody.Clear();
150 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
151 PhysShape.Clear();
152 });
153 }
154
155 // No one uses this property.
156 public override bool Stopped {
157 get { return false; }
158 }
159 public override OMV.Vector3 Size {
160 get { return _size; }
161 set {
162 // We presume the scale and size are the same. If scale must be changed for
163 // the physical shape, that is done when the geometry is built.
164 _size = value;
165 Scale = _size;
166 ForceBodyShapeRebuild(false);
167 }
168 }
169
170 public override PrimitiveBaseShape Shape {
171 set {
172 BaseShape = value;
173 ForceBodyShapeRebuild(false);
174 }
175 }
176 // Whatever the linkset wants is what I want.
177 public override BSPhysicsShapeType PreferredPhysicalShape
178 { get { return Linkset.PreferredPhysicalShape(this); } }
179
180 public override bool ForceBodyShapeRebuild(bool inTaintTime)
181 {
182 LastAssetBuildFailed = false;
183 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ForceBodyShapeRebuild", delegate()
184 {
185 _mass = CalculateMass(); // changing the shape changes the mass
186 CreateGeomAndObject(true);
187 });
188 return true;
189 }
190 public override bool Grabbed {
191 set { _grabbed = value;
192 }
193 }
194 public override bool Selected {
195 set
196 {
197 if (value != _isSelected)
198 {
199 _isSelected = value;
200 PhysicsScene.TaintedObject("BSPrim.setSelected", delegate()
201 {
202 DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected);
203 SetObjectDynamic(false);
204 });
205 }
206 }
207 }
208 public override void CrossingFailure() { return; }
209
210 // link me to the specified parent
211 public override void link(PhysicsActor obj) {
212 BSPrim parent = obj as BSPrim;
213 if (parent != null)
214 {
215 BSPhysObject parentBefore = Linkset.LinksetRoot;
216 int childrenBefore = Linkset.NumberOfChildren;
217
218 Linkset = parent.Linkset.AddMeToLinkset(this);
219
220 DetailLog("{0},BSPrim.link,call,parentBefore={1}, childrenBefore=={2}, parentAfter={3}, childrenAfter={4}",
221 LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
222 }
223 return;
224 }
225
226 // delink me from my linkset
227 public override void delink() {
228 // TODO: decide if this parent checking needs to happen at taint time
229 // Race condition here: if link() and delink() in same simulation tick, the delink will not happen
230
231 BSPhysObject parentBefore = Linkset.LinksetRoot;
232 int childrenBefore = Linkset.NumberOfChildren;
233
234 Linkset = Linkset.RemoveMeFromLinkset(this);
235
236 DetailLog("{0},BSPrim.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ",
237 LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
238 return;
239 }
240
241 // Set motion values to zero.
242 // Do it to the properties so the values get set in the physics engine.
243 // Push the setting of the values to the viewer.
244 // Called at taint time!
245 public override void ZeroMotion(bool inTaintTime)
246 {
247 _velocity = OMV.Vector3.Zero;
248 _acceleration = OMV.Vector3.Zero;
249 _rotationalVelocity = OMV.Vector3.Zero;
250
251 // Zero some other properties in the physics engine
252 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
253 {
254 if (PhysBody.HasPhysicalBody)
255 BulletSimAPI.ClearAllForces2(PhysBody.ptr);
256 });
257 }
258 public override void ZeroAngularMotion(bool inTaintTime)
259 {
260 _rotationalVelocity = OMV.Vector3.Zero;
261 // Zero some other properties in the physics engine
262 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
263 {
264 // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity);
265 if (PhysBody.HasPhysicalBody)
266 {
267 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
268 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
269 }
270 });
271 }
272
273 public override void LockAngularMotion(OMV.Vector3 axis)
274 {
275 DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis);
276 return;
277 }
278
279 public override OMV.Vector3 RawPosition
280 {
281 get { return _position; }
282 set { _position = value; }
283 }
284 public override OMV.Vector3 Position {
285 get {
286 /* NOTE: this refetch is not necessary. The simulator knows about linkset children
287 * and does not fetch this position info for children. Thus this is commented out.
288 // child prims move around based on their parent. Need to get the latest location
289 if (!Linkset.IsRoot(this))
290 _position = Linkset.PositionGet(this);
291 */
292
293 // don't do the GetObjectPosition for root elements because this function is called a zillion times.
294 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
295 return _position;
296 }
297 set {
298 // If the position must be forced into the physics engine, use ForcePosition.
299 // All positions are given in world positions.
300 if (_position == value)
301 {
302 DetailLog("{0},BSPrim.setPosition,taint,positionNotChanging,pos={1},orient={2}", LocalID, _position, _orientation);
303 return;
304 }
305 _position = value;
306 PositionSanityCheck(false);
307
308 // A linkset might need to know if a component information changed.
309 Linkset.UpdateProperties(this, false);
310
311 PhysicsScene.TaintedObject("BSPrim.setPosition", delegate()
312 {
313 DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
314 ForcePosition = _position;
315 });
316 }
317 }
318 public override OMV.Vector3 ForcePosition {
319 get {
320 _position = BulletSimAPI.GetPosition2(PhysBody.ptr);
321 return _position;
322 }
323 set {
324 _position = value;
325 if (PhysBody.HasPhysicalBody)
326 {
327 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
328 ActivateIfPhysical(false);
329 }
330 }
331 }
332
333 // Check that the current position is sane and, if not, modify the position to make it so.
334 // Check for being below terrain and being out of bounds.
335 // Returns 'true' of the position was made sane by some action.
336 private bool PositionSanityCheck(bool inTaintTime)
337 {
338 bool ret = false;
339
340 if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position))
341 {
342 // The physical object is out of the known/simulated area.
343 // Upper levels of code will handle the transition to other areas so, for
344 // the time, we just ignore the position.
345 return ret;
346 }
347
348 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
349 OMV.Vector3 upForce = OMV.Vector3.Zero;
350 if (RawPosition.Z < terrainHeight)
351 {
352 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
353 float targetHeight = terrainHeight + (Size.Z / 2f);
354 // Upforce proportional to the distance away from the terrain. Correct the error in 1 sec.
355 upForce.Z = (terrainHeight - RawPosition.Z) * 1f;
356 ret = true;
357 }
358
359 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
360 {
361 float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
362 // TODO: a floating motor so object will bob in the water
363 if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f)
364 {
365 // Upforce proportional to the distance away from the water. Correct the error in 1 sec.
366 upForce.Z = (waterHeight - RawPosition.Z) * 1f;
367 ret = true;
368 }
369 }
370
371 // The above code computes a force to apply to correct any out-of-bounds problems. Apply same.
372 // TODO: This should be intergrated with a geneal physics action mechanism.
373 // TODO: This should be moderated with PID'ness.
374 if (ret)
375 {
376 // Apply upforce and overcome gravity.
377 OMV.Vector3 correctionForce = upForce - PhysicsScene.DefaultGravity;
378 DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, _position, upForce, correctionForce);
379 AddForce(correctionForce, false, inTaintTime);
380 }
381 return ret;
382 }
383
384 // Return the effective mass of the object.
385 // The definition of this call is to return the mass of the prim.
386 // If the simulator cares about the mass of the linkset, it will sum it itself.
387 public override float Mass
388 {
389 get
390 {
391 return _mass;
392 }
393 }
394
395 // used when we only want this prim's mass and not the linkset thing
396 public override float RawMass {
397 get { return _mass; }
398 }
399 // Set the physical mass to the passed mass.
400 // Note that this does not change _mass!
401 public override void UpdatePhysicalMassProperties(float physMass, bool inWorld)
402 {
403 if (PhysBody.HasPhysicalBody)
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 if (inWorld)
414 {
415 // Changing interesting properties doesn't change proxy and collision cache
416 // information. The Bullet solution is to re-add the object to the world
417 // after parameters are changed.
418 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
419 }
420
421 Inertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass);
422 BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, Inertia);
423 BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr);
424
425 // center of mass is at the zero of the object
426 // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(PhysBody.ptr, ForcePosition, ForceOrientation);
427 DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2},inWorld={3}", LocalID, physMass, Inertia, inWorld);
428
429 if (inWorld)
430 {
431 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr,_position,_orientation);
432 }
433
434 // Must set gravity after it has been added to the world because, for unknown reasons,
435 // adding the object resets the object's gravity to world gravity
436 OMV.Vector3 grav = PhysicsScene.DefaultGravity * (1f - Buoyancy);
437 BulletSimAPI.SetGravity2(PhysBody.ptr, grav);
438
439 }
440 }
441 }
442
443 // Is this used?
444 public override OMV.Vector3 CenterOfMass
445 {
446 get { return Linkset.CenterOfMass; }
447 }
448
449 // Is this used?
450 public override OMV.Vector3 GeometricCenter
451 {
452 get { return Linkset.GeometricCenter; }
453 }
454
455 public override OMV.Vector3 Force {
456 get { return _force; }
457 set {
458 _force = value;
459 if (_force != OMV.Vector3.Zero)
460 {
461 // If the force is non-zero, it must be reapplied each tick because
462 // Bullet clears the forces applied last frame.
463 RegisterPreStepAction("BSPrim.setForce", LocalID,
464 delegate(float timeStep)
465 {
466 DetailLog("{0},BSPrim.setForce,preStep,force={1}", LocalID, _force);
467 if (PhysBody.HasPhysicalBody)
468 {
469 BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, _force);
470 ActivateIfPhysical(false);
471 }
472 }
473 );
474 }
475 else
476 {
477 UnRegisterPreStepAction("BSPrim.setForce", LocalID);
478 }
479 }
480 }
481
482 public override int VehicleType {
483 get {
484 return (int)_vehicle.Type; // if we are a vehicle, return that type
485 }
486 set {
487 Vehicle type = (Vehicle)value;
488
489 PhysicsScene.TaintedObject("setVehicleType", delegate()
490 {
491 // Done at taint time so we're sure the physics engine is not using the variables
492 // Vehicle code changes the parameters for this vehicle type.
493 _vehicle.ProcessTypeChange(type);
494 ActivateIfPhysical(false);
495
496 // If an active vehicle, register the vehicle code to be called before each step
497 if (_vehicle.Type == Vehicle.TYPE_NONE)
498 UnRegisterPreStepAction("BSPrim.Vehicle", LocalID);
499 else
500 RegisterPreStepAction("BSPrim.Vehicle", LocalID, _vehicle.Step);
501 });
502 }
503 }
504 public override void VehicleFloatParam(int param, float value)
505 {
506 PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate()
507 {
508 _vehicle.ProcessFloatVehicleParam((Vehicle)param, value);
509 ActivateIfPhysical(false);
510 });
511 }
512 public override void VehicleVectorParam(int param, OMV.Vector3 value)
513 {
514 PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate()
515 {
516 _vehicle.ProcessVectorVehicleParam((Vehicle)param, value);
517 ActivateIfPhysical(false);
518 });
519 }
520 public override void VehicleRotationParam(int param, OMV.Quaternion rotation)
521 {
522 PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate()
523 {
524 _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation);
525 ActivateIfPhysical(false);
526 });
527 }
528 public override void VehicleFlags(int param, bool remove)
529 {
530 PhysicsScene.TaintedObject("BSPrim.VehicleFlags", delegate()
531 {
532 _vehicle.ProcessVehicleFlags(param, remove);
533 });
534 }
535
536 // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more
537 public override void SetVolumeDetect(int param) {
538 bool newValue = (param != 0);
539 if (_isVolumeDetect != newValue)
540 {
541 _isVolumeDetect = newValue;
542 PhysicsScene.TaintedObject("BSPrim.SetVolumeDetect", delegate()
543 {
544 // DetailLog("{0},setVolumeDetect,taint,volDetect={1}", LocalID, _isVolumeDetect);
545 SetObjectDynamic(true);
546 });
547 }
548 return;
549 }
550 public override OMV.Vector3 Velocity {
551 get { return _velocity; }
552 set {
553 _velocity = value;
554 PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate()
555 {
556 // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity);
557 ForceVelocity = _velocity;
558 });
559 }
560 }
561 public override OMV.Vector3 ForceVelocity {
562 get { return _velocity; }
563 set {
564 PhysicsScene.AssertInTaintTime("BSPrim.ForceVelocity");
565
566 _velocity = value;
567 if (PhysBody.HasPhysicalBody)
568 {
569 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
570 ActivateIfPhysical(false);
571 }
572 }
573 }
574 public override OMV.Vector3 Torque {
575 get { return _torque; }
576 set {
577 _torque = value;
578 if (_torque != OMV.Vector3.Zero)
579 {
580 // If the torque is non-zero, it must be reapplied each tick because
581 // Bullet clears the forces applied last frame.
582 RegisterPreStepAction("BSPrim.setTorque", LocalID,
583 delegate(float timeStep)
584 {
585 if (PhysBody.HasPhysicalBody)
586 AddAngularForce(_torque, false, true);
587 }
588 );
589 }
590 else
591 {
592 UnRegisterPreStepAction("BSPrim.setTorque", LocalID);
593 }
594 // DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque);
595 }
596 }
597 public override float CollisionScore {
598 get { return _collisionScore; }
599 set { _collisionScore = value;
600 }
601 }
602 public override OMV.Vector3 Acceleration {
603 get { return _acceleration; }
604 set { _acceleration = value; }
605 }
606 public override OMV.Quaternion RawOrientation
607 {
608 get { return _orientation; }
609 set { _orientation = value; }
610 }
611 public override OMV.Quaternion Orientation {
612 get {
613 /* NOTE: this refetch is not necessary. The simulator knows about linkset children
614 * and does not fetch this position info for children. Thus this is commented out.
615 // Children move around because tied to parent. Get a fresh value.
616 if (!Linkset.IsRoot(this))
617 {
618 _orientation = Linkset.OrientationGet(this);
619 }
620 */
621 return _orientation;
622 }
623 set {
624 if (_orientation == value)
625 return;
626 _orientation = value;
627
628 // A linkset might need to know if a component information changed.
629 Linkset.UpdateProperties(this, false);
630
631 PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate()
632 {
633 if (PhysBody.HasPhysicalBody)
634 {
635 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
636 // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation);
637 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
638 }
639 });
640 }
641 }
642 // Go directly to Bullet to get/set the value.
643 public override OMV.Quaternion ForceOrientation
644 {
645 get
646 {
647 _orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr);
648 return _orientation;
649 }
650 set
651 {
652 _orientation = value;
653 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
654 }
655 }
656 public override int PhysicsActorType {
657 get { return _physicsActorType; }
658 set { _physicsActorType = value; }
659 }
660 public override bool IsPhysical {
661 get { return _isPhysical; }
662 set {
663 if (_isPhysical != value)
664 {
665 _isPhysical = value;
666 PhysicsScene.TaintedObject("BSPrim.setIsPhysical", delegate()
667 {
668 // DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical);
669 SetObjectDynamic(true);
670 // whether phys-to-static or static-to-phys, the object is not moving.
671 ZeroMotion(true);
672 });
673 }
674 }
675 }
676
677 // An object is static (does not move) if selected or not physical
678 public override bool IsStatic
679 {
680 get { return _isSelected || !IsPhysical; }
681 }
682
683 // An object is solid if it's not phantom and if it's not doing VolumeDetect
684 public override bool IsSolid
685 {
686 get { return !IsPhantom && !_isVolumeDetect; }
687 }
688
689 // Make gravity work if the object is physical and not selected
690 // Called at taint-time!!
691 private void SetObjectDynamic(bool forceRebuild)
692 {
693 // Recreate the physical object if necessary
694 CreateGeomAndObject(forceRebuild);
695 }
696
697 // Convert the simulator's physical properties into settings on BulletSim objects.
698 // There are four flags we're interested in:
699 // IsStatic: Object does not move, otherwise the object has mass and moves
700 // isSolid: other objects bounce off of this object
701 // isVolumeDetect: other objects pass through but can generate collisions
702 // collisionEvents: whether this object returns collision events
703 private void UpdatePhysicalParameters()
704 {
705 // DetailLog("{0},BSPrim.UpdatePhysicalParameters,entry,body={1},shape={2}", LocalID, BSBody, BSShape);
706
707 // Mangling all the physical properties requires the object not be in the physical world.
708 // This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found).
709 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
710
711 // Set up the object physicalness (does gravity and collisions move this object)
712 MakeDynamic(IsStatic);
713
714 // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters)
715 _vehicle.Refresh();
716
717 // Arrange for collision events if the simulator wants them
718 EnableCollisions(SubscribedEvents());
719
720 // Make solid or not (do things bounce off or pass through this object).
721 MakeSolid(IsSolid);
722
723 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr, _position, _orientation);
724
725 // Rebuild its shape
726 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
727
728 // Collision filter can be set only when the object is in the world
729 PhysBody.ApplyCollisionMask();
730
731 // Recompute any linkset parameters.
732 // When going from non-physical to physical, this re-enables the constraints that
733 // had been automatically disabled when the mass was set to zero.
734 // For compound based linksets, this enables and disables interactions of the children.
735 Linkset.Refresh(this);
736
737 DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}",
738 LocalID, IsStatic, IsSolid, Mass, SubscribedEvents(), CurrentCollisionFlags, PhysBody, PhysShape);
739 }
740
741 // "Making dynamic" means changing to and from static.
742 // When static, gravity does not effect the object and it is fixed in space.
743 // When dynamic, the object can fall and be pushed by others.
744 // This is independent of its 'solidness' which controls what passes through
745 // this object and what interacts with it.
746 private void MakeDynamic(bool makeStatic)
747 {
748 if (makeStatic)
749 {
750 // Become a Bullet 'static' object type
751 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
752 // Stop all movement
753 ZeroMotion(true);
754
755 // Set various physical properties so other object interact properly
756 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false);
757 BulletSimAPI.SetFriction2(PhysBody.ptr, matAttrib.friction);
758 BulletSimAPI.SetRestitution2(PhysBody.ptr, matAttrib.restitution);
759
760 // Mass is zero which disables a bunch of physics stuff in Bullet
761 UpdatePhysicalMassProperties(0f, false);
762 // Set collision detection parameters
763 if (BSParam.CcdMotionThreshold > 0f)
764 {
765 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold);
766 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius);
767 }
768
769 // The activation state is 'disabled' so Bullet will not try to act on it.
770 // BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
771 // Start it out sleeping and physical actions could wake it up.
772 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ISLAND_SLEEPING);
773
774 // This collides like a static object
775 PhysBody.collisionType = CollisionType.Static;
776
777 // There can be special things needed for implementing linksets
778 Linkset.MakeStatic(this);
779 }
780 else
781 {
782 // Not a Bullet static object
783 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
784
785 // Set various physical properties so other object interact properly
786 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, true);
787 BulletSimAPI.SetFriction2(PhysBody.ptr, matAttrib.friction);
788 BulletSimAPI.SetRestitution2(PhysBody.ptr, matAttrib.restitution);
789
790 // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382
791 // Since this can be called multiple times, only zero forces when becoming physical
792 // BulletSimAPI.ClearAllForces2(BSBody.ptr);
793
794 // For good measure, make sure the transform is set through to the motion state
795 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
796
797 // Center of mass is at the center of the object
798 // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation);
799
800 // A dynamic object has mass
801 UpdatePhysicalMassProperties(RawMass, false);
802
803 // Set collision detection parameters
804 if (BSParam.CcdMotionThreshold > 0f)
805 {
806 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold);
807 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius);
808 }
809
810 // Various values for simulation limits
811 BulletSimAPI.SetDamping2(PhysBody.ptr, BSParam.LinearDamping, BSParam.AngularDamping);
812 BulletSimAPI.SetDeactivationTime2(PhysBody.ptr, BSParam.DeactivationTime);
813 BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold);
814 BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, BSParam.ContactProcessingThreshold);
815
816 // This collides like an object.
817 PhysBody.collisionType = CollisionType.Dynamic;
818
819 // Force activation of the object so Bullet will act on it.
820 // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects.
821 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ACTIVE_TAG);
822
823 // There might be special things needed for implementing linksets.
824 Linkset.MakeDynamic(this);
825 }
826 }
827
828 // "Making solid" means that other object will not pass through this object.
829 // To make transparent, we create a Bullet ghost object.
830 // Note: This expects to be called from the UpdatePhysicalParameters() routine as
831 // the functions after this one set up the state of a possibly newly created collision body.
832 private void MakeSolid(bool makeSolid)
833 {
834 CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(PhysBody.ptr);
835 if (makeSolid)
836 {
837 // Verify the previous code created the correct shape for this type of thing.
838 if ((bodyType & CollisionObjectTypes.CO_RIGID_BODY) == 0)
839 {
840 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType);
841 }
842 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
843 }
844 else
845 {
846 if ((bodyType & CollisionObjectTypes.CO_GHOST_OBJECT) == 0)
847 {
848 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType);
849 }
850 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
851
852 // Change collision info from a static object to a ghosty collision object
853 PhysBody.collisionType = CollisionType.VolumeDetect;
854 }
855 }
856
857 // Enable physical actions. Bullet will keep sleeping non-moving physical objects so
858 // they need waking up when parameters are changed.
859 // Called in taint-time!!
860 private void ActivateIfPhysical(bool forceIt)
861 {
862 if (IsPhysical && PhysBody.HasPhysicalBody)
863 BulletSimAPI.Activate2(PhysBody.ptr, forceIt);
864 }
865
866 // Turn on or off the flag controlling whether collision events are returned to the simulator.
867 private void EnableCollisions(bool wantsCollisionEvents)
868 {
869 if (wantsCollisionEvents)
870 {
871 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
872 }
873 else
874 {
875 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
876 }
877 }
878
879 // prims don't fly
880 public override bool Flying {
881 get { return _flying; }
882 set {
883 _flying = value;
884 }
885 }
886 public override bool SetAlwaysRun {
887 get { return _setAlwaysRun; }
888 set { _setAlwaysRun = value; }
889 }
890 public override bool ThrottleUpdates {
891 get { return _throttleUpdates; }
892 set { _throttleUpdates = value; }
893 }
894 public override bool IsColliding {
895 get { return (CollidingStep == PhysicsScene.SimulationStep); }
896 set { _isColliding = value; }
897 }
898 public override bool CollidingGround {
899 get { return (CollidingGroundStep == PhysicsScene.SimulationStep); }
900 set { _collidingGround = value; }
901 }
902 public override bool CollidingObj {
903 get { return _collidingObj; }
904 set { _collidingObj = value; }
905 }
906 public bool IsPhantom {
907 get {
908 // SceneObjectPart removes phantom objects from the physics scene
909 // so, although we could implement touching and such, we never
910 // are invoked as a phantom object
911 return false;
912 }
913 }
914 public override bool FloatOnWater {
915 set {
916 _floatOnWater = value;
917 PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate()
918 {
919 if (_floatOnWater)
920 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
921 else
922 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
923 });
924 }
925 }
926 public override OMV.Vector3 RotationalVelocity {
927 get {
928 return _rotationalVelocity;
929 }
930 set {
931 _rotationalVelocity = value;
932 // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity);
933 PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
934 {
935 DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
936 ForceRotationalVelocity = _rotationalVelocity;
937 });
938 }
939 }
940 public override OMV.Vector3 ForceRotationalVelocity {
941 get {
942 return _rotationalVelocity;
943 }
944 set {
945 _rotationalVelocity = value;
946 if (PhysBody.HasPhysicalBody)
947 {
948 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
949 ActivateIfPhysical(false);
950 }
951 }
952 }
953 public override bool Kinematic {
954 get { return _kinematic; }
955 set { _kinematic = value;
956 // m_log.DebugFormat("{0}: Kinematic={1}", LogHeader, _kinematic);
957 }
958 }
959 public override float Buoyancy {
960 get { return _buoyancy; }
961 set {
962 _buoyancy = value;
963 PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate()
964 {
965 ForceBuoyancy = _buoyancy;
966 });
967 }
968 }
969 public override float ForceBuoyancy {
970 get { return _buoyancy; }
971 set {
972 _buoyancy = value;
973 // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
974 // Force the recalculation of the various inertia,etc variables in the object
975 UpdatePhysicalMassProperties(_mass, true);
976 ActivateIfPhysical(false);
977 }
978 }
979
980 // Used for MoveTo
981 public override OMV.Vector3 PIDTarget {
982 set { _PIDTarget = value; }
983 }
984 public override bool PIDActive {
985 set { _usePID = value; }
986 }
987 public override float PIDTau {
988 set { _PIDTau = value; }
989 }
990
991 // Used for llSetHoverHeight and maybe vehicle height
992 // Hover Height will override MoveTo target's Z
993 public override bool PIDHoverActive {
994 set { _useHoverPID = value; }
995 }
996 public override float PIDHoverHeight {
997 set { _PIDHoverHeight = value; }
998 }
999 public override PIDHoverType PIDHoverType {
1000 set { _PIDHoverType = value; }
1001 }
1002 public override float PIDHoverTau {
1003 set { _PIDHoverTao = value; }
1004 }
1005
1006 // For RotLookAt
1007 public override OMV.Quaternion APIDTarget { set { return; } }
1008 public override bool APIDActive { set { return; } }
1009 public override float APIDStrength { set { return; } }
1010 public override float APIDDamping { set { return; } }
1011
1012 public override void AddForce(OMV.Vector3 force, bool pushforce) {
1013 // Since this force is being applied in only one step, make this a force per second.
1014 OMV.Vector3 addForce = force / PhysicsScene.LastTimeStep;
1015 AddForce(addForce, pushforce, false);
1016 }
1017 // Applying a force just adds this to the total force on the object.
1018 // This added force will only last the next simulation tick.
1019 public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
1020 // for an object, doesn't matter if force is a pushforce or not
1021 if (force.IsFinite())
1022 {
1023 float magnitude = force.Length();
1024 if (magnitude > 20000f)
1025 {
1026 // Force has a limit
1027 force = force / magnitude * 20000f;
1028 }
1029
1030 OMV.Vector3 addForce = force;
1031 DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce);
1032
1033 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate()
1034 {
1035 // Bullet adds this central force to the total force for this tick
1036 DetailLog("{0},BSPrim.addForce,taint,force={1}", LocalID, addForce);
1037 if (PhysBody.HasPhysicalBody)
1038 {
1039 BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, addForce);
1040 ActivateIfPhysical(false);
1041 }
1042 });
1043 }
1044 else
1045 {
1046 m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID);
1047 return;
1048 }
1049 }
1050
1051 public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
1052 AddAngularForce(force, pushforce, false);
1053 }
1054 public void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime)
1055 {
1056 if (force.IsFinite())
1057 {
1058 OMV.Vector3 angForce = force;
1059 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate()
1060 {
1061 if (PhysBody.HasPhysicalBody)
1062 {
1063 BulletSimAPI.ApplyTorque2(PhysBody.ptr, angForce);
1064 ActivateIfPhysical(false);
1065 }
1066 });
1067 }
1068 else
1069 {
1070 m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID);
1071 return;
1072 }
1073 }
1074
1075 // A torque impulse.
1076 // ApplyTorqueImpulse adds torque directly to the angularVelocity.
1077 // AddAngularForce accumulates the force and applied it to the angular velocity all at once.
1078 // Computed as: angularVelocity += impulse * inertia;
1079 public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime)
1080 {
1081 OMV.Vector3 applyImpulse = impulse;
1082 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate()
1083 {
1084 if (PhysBody.HasPhysicalBody)
1085 {
1086 BulletSimAPI.ApplyTorqueImpulse2(PhysBody.ptr, applyImpulse);
1087 ActivateIfPhysical(false);
1088 }
1089 });
1090 }
1091
1092 public override void SetMomentum(OMV.Vector3 momentum) {
1093 // DetailLog("{0},BSPrim.SetMomentum,call,mom={1}", LocalID, momentum);
1094 }
1095 #region Mass Calculation
1096
1097 private float CalculateMass()
1098 {
1099 float volume = _size.X * _size.Y * _size.Z; // default
1100 float tmp;
1101
1102 float returnMass = 0;
1103 float hollowAmount = (float)BaseShape.ProfileHollow * 2.0e-5f;
1104 float hollowVolume = hollowAmount * hollowAmount;
1105
1106 switch (BaseShape.ProfileShape)
1107 {
1108 case ProfileShape.Square:
1109 // default box
1110
1111 if (BaseShape.PathCurve == (byte)Extrusion.Straight)
1112 {
1113 if (hollowAmount > 0.0)
1114 {
1115 switch (BaseShape.HollowShape)
1116 {
1117 case HollowShape.Square:
1118 case HollowShape.Same:
1119 break;
1120
1121 case HollowShape.Circle:
1122
1123 hollowVolume *= 0.78539816339f;
1124 break;
1125
1126 case HollowShape.Triangle:
1127
1128 hollowVolume *= (0.5f * .5f);
1129 break;
1130
1131 default:
1132 hollowVolume = 0;
1133 break;
1134 }
1135 volume *= (1.0f - hollowVolume);
1136 }
1137 }
1138
1139 else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
1140 {
1141 //a tube
1142
1143 volume *= 0.78539816339e-2f * (float)(200 - BaseShape.PathScaleX);
1144 tmp= 1.0f -2.0e-2f * (float)(200 - BaseShape.PathScaleY);
1145 volume -= volume*tmp*tmp;
1146
1147 if (hollowAmount > 0.0)
1148 {
1149 hollowVolume *= hollowAmount;
1150
1151 switch (BaseShape.HollowShape)
1152 {
1153 case HollowShape.Square:
1154 case HollowShape.Same:
1155 break;
1156
1157 case HollowShape.Circle:
1158 hollowVolume *= 0.78539816339f;;
1159 break;
1160
1161 case HollowShape.Triangle:
1162 hollowVolume *= 0.5f * 0.5f;
1163 break;
1164 default:
1165 hollowVolume = 0;
1166 break;
1167 }
1168 volume *= (1.0f - hollowVolume);
1169 }
1170 }
1171
1172 break;
1173
1174 case ProfileShape.Circle:
1175
1176 if (BaseShape.PathCurve == (byte)Extrusion.Straight)
1177 {
1178 volume *= 0.78539816339f; // elipse base
1179
1180 if (hollowAmount > 0.0)
1181 {
1182 switch (BaseShape.HollowShape)
1183 {
1184 case HollowShape.Same:
1185 case HollowShape.Circle:
1186 break;
1187
1188 case HollowShape.Square:
1189 hollowVolume *= 0.5f * 2.5984480504799f;
1190 break;
1191
1192 case HollowShape.Triangle:
1193 hollowVolume *= .5f * 1.27323954473516f;
1194 break;
1195
1196 default:
1197 hollowVolume = 0;
1198 break;
1199 }
1200 volume *= (1.0f - hollowVolume);
1201 }
1202 }
1203
1204 else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
1205 {
1206 volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - BaseShape.PathScaleX);
1207 tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
1208 volume *= (1.0f - tmp * tmp);
1209
1210 if (hollowAmount > 0.0)
1211 {
1212
1213 // calculate the hollow volume by it's shape compared to the prim shape
1214 hollowVolume *= hollowAmount;
1215
1216 switch (BaseShape.HollowShape)
1217 {
1218 case HollowShape.Same:
1219 case HollowShape.Circle:
1220 break;
1221
1222 case HollowShape.Square:
1223 hollowVolume *= 0.5f * 2.5984480504799f;
1224 break;
1225
1226 case HollowShape.Triangle:
1227 hollowVolume *= .5f * 1.27323954473516f;
1228 break;
1229
1230 default:
1231 hollowVolume = 0;
1232 break;
1233 }
1234 volume *= (1.0f - hollowVolume);
1235 }
1236 }
1237 break;
1238
1239 case ProfileShape.HalfCircle:
1240 if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
1241 {
1242 volume *= 0.52359877559829887307710723054658f;
1243 }
1244 break;
1245
1246 case ProfileShape.EquilateralTriangle:
1247
1248 if (BaseShape.PathCurve == (byte)Extrusion.Straight)
1249 {
1250 volume *= 0.32475953f;
1251
1252 if (hollowAmount > 0.0)
1253 {
1254
1255 // calculate the hollow volume by it's shape compared to the prim shape
1256 switch (BaseShape.HollowShape)
1257 {
1258 case HollowShape.Same:
1259 case HollowShape.Triangle:
1260 hollowVolume *= .25f;
1261 break;
1262
1263 case HollowShape.Square:
1264 hollowVolume *= 0.499849f * 3.07920140172638f;
1265 break;
1266
1267 case HollowShape.Circle:
1268 // Hollow shape is a perfect cyllinder in respect to the cube's scale
1269 // Cyllinder hollow volume calculation
1270
1271 hollowVolume *= 0.1963495f * 3.07920140172638f;
1272 break;
1273
1274 default:
1275 hollowVolume = 0;
1276 break;
1277 }
1278 volume *= (1.0f - hollowVolume);
1279 }
1280 }
1281 else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
1282 {
1283 volume *= 0.32475953f;
1284 volume *= 0.01f * (float)(200 - BaseShape.PathScaleX);
1285 tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
1286 volume *= (1.0f - tmp * tmp);
1287
1288 if (hollowAmount > 0.0)
1289 {
1290
1291 hollowVolume *= hollowAmount;
1292
1293 switch (BaseShape.HollowShape)
1294 {
1295 case HollowShape.Same:
1296 case HollowShape.Triangle:
1297 hollowVolume *= .25f;
1298 break;
1299
1300 case HollowShape.Square:
1301 hollowVolume *= 0.499849f * 3.07920140172638f;
1302 break;
1303
1304 case HollowShape.Circle:
1305
1306 hollowVolume *= 0.1963495f * 3.07920140172638f;
1307 break;
1308
1309 default:
1310 hollowVolume = 0;
1311 break;
1312 }
1313 volume *= (1.0f - hollowVolume);
1314 }
1315 }
1316 break;
1317
1318 default:
1319 break;
1320 }
1321
1322
1323
1324 float taperX1;
1325 float taperY1;
1326 float taperX;
1327 float taperY;
1328 float pathBegin;
1329 float pathEnd;
1330 float profileBegin;
1331 float profileEnd;
1332
1333 if (BaseShape.PathCurve == (byte)Extrusion.Straight || BaseShape.PathCurve == (byte)Extrusion.Flexible)
1334 {
1335 taperX1 = BaseShape.PathScaleX * 0.01f;
1336 if (taperX1 > 1.0f)
1337 taperX1 = 2.0f - taperX1;
1338 taperX = 1.0f - taperX1;
1339
1340 taperY1 = BaseShape.PathScaleY * 0.01f;
1341 if (taperY1 > 1.0f)
1342 taperY1 = 2.0f - taperY1;
1343 taperY = 1.0f - taperY1;
1344 }
1345 else
1346 {
1347 taperX = BaseShape.PathTaperX * 0.01f;
1348 if (taperX < 0.0f)
1349 taperX = -taperX;
1350 taperX1 = 1.0f - taperX;
1351
1352 taperY = BaseShape.PathTaperY * 0.01f;
1353 if (taperY < 0.0f)
1354 taperY = -taperY;
1355 taperY1 = 1.0f - taperY;
1356
1357 }
1358
1359
1360 volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY);
1361
1362 pathBegin = (float)BaseShape.PathBegin * 2.0e-5f;
1363 pathEnd = 1.0f - (float)BaseShape.PathEnd * 2.0e-5f;
1364 volume *= (pathEnd - pathBegin);
1365
1366 // this is crude aproximation
1367 profileBegin = (float)BaseShape.ProfileBegin * 2.0e-5f;
1368 profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f;
1369 volume *= (profileEnd - profileBegin);
1370
1371 returnMass = _density * volume;
1372
1373 /* Comment out code that computes the mass of the linkset. That is done in the Linkset class.
1374 if (IsRootOfLinkset)
1375 {
1376 foreach (BSPrim prim in _childrenPrims)
1377 {
1378 returnMass += prim.CalculateMass();
1379 }
1380 }
1381 */
1382
1383 returnMass = Util.Clamp(returnMass, BSParam.MinimumObjectMass, BSParam.MaximumObjectMass);
1384
1385 return returnMass;
1386 }// end CalculateMass
1387 #endregion Mass Calculation
1388
1389 // Rebuild the geometry and object.
1390 // This is called when the shape changes so we need to recreate the mesh/hull.
1391 // Called at taint-time!!!
1392 public void CreateGeomAndObject(bool forceRebuild)
1393 {
1394 // If this prim is part of a linkset, we must remove and restore the physical
1395 // links if the body is rebuilt.
1396 bool needToRestoreLinkset = false;
1397 bool needToRestoreVehicle = false;
1398
1399 // Create the correct physical representation for this type of object.
1400 // Updates PhysBody and PhysShape with the new information.
1401 // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary.
1402 PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, null, delegate(BulletBody dBody)
1403 {
1404 // Called if the current prim body is about to be destroyed.
1405 // Remove all the physical dependencies on the old body.
1406 // (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...)
1407 needToRestoreLinkset = Linkset.RemoveBodyDependencies(this);
1408 needToRestoreVehicle = _vehicle.RemoveBodyDependencies(this);
1409 });
1410
1411 if (needToRestoreLinkset)
1412 {
1413 // If physical body dependencies were removed, restore them
1414 Linkset.RestoreBodyDependencies(this);
1415 }
1416 if (needToRestoreVehicle)
1417 {
1418 // If physical body dependencies were removed, restore them
1419 _vehicle.RestoreBodyDependencies(this);
1420 }
1421
1422 // Make sure the properties are set on the new object
1423 UpdatePhysicalParameters();
1424 return;
1425 }
1426
1427 // The physics engine says that properties have updated. Update same and inform
1428 // the world that things have changed.
1429 // TODO: do we really need to check for changed? Maybe just copy values and call RequestPhysicsterseUpdate()
1430 enum UpdatedProperties {
1431 Position = 1 << 0,
1432 Rotation = 1 << 1,
1433 Velocity = 1 << 2,
1434 Acceleration = 1 << 3,
1435 RotationalVel = 1 << 4
1436 }
1437
1438 const float ROTATION_TOLERANCE = 0.01f;
1439 const float VELOCITY_TOLERANCE = 0.001f;
1440 const float POSITION_TOLERANCE = 0.05f;
1441 const float ACCELERATION_TOLERANCE = 0.01f;
1442 const float ROTATIONAL_VELOCITY_TOLERANCE = 0.01f;
1443
1444 public override void UpdateProperties(EntityProperties entprop)
1445 {
1446 // Updates only for individual prims and for the root object of a linkset.
1447 if (Linkset.IsRoot(this))
1448 {
1449 // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet
1450 // TODO: handle physics introduced by Bullet with computed vehicle physics.
1451 if (_vehicle.IsActive)
1452 {
1453 entprop.RotationalVelocity = OMV.Vector3.Zero;
1454 }
1455
1456 // Assign directly to the local variables so the normal set action does not happen
1457 _position = entprop.Position;
1458 _orientation = entprop.Rotation;
1459 _velocity = entprop.Velocity;
1460 _acceleration = entprop.Acceleration;
1461 _rotationalVelocity = entprop.RotationalVelocity;
1462
1463 // The sanity check can change the velocity and/or position.
1464 if (IsPhysical && PositionSanityCheck(true))
1465 {
1466 entprop.Position = _position;
1467 entprop.Velocity = _velocity;
1468 }
1469
1470 OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; // DEBUG DEBUG DEBUG
1471 DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}",
1472 LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity);
1473
1474 // remember the current and last set values
1475 LastEntityProperties = CurrentEntityProperties;
1476 CurrentEntityProperties = entprop;
1477
1478 base.RequestPhysicsterseUpdate();
1479 }
1480 /*
1481 else
1482 {
1483 // For debugging, report the movement of children
1484 DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
1485 LocalID, entprop.Position, entprop.Rotation, entprop.Velocity,
1486 entprop.Acceleration, entprop.RotationalVelocity);
1487 }
1488 */
1489
1490 // The linkset implimentation might want to know about this.
1491 Linkset.UpdateProperties(this, true);
1492 }
1493}
1494}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSScene.cs
new file mode 100644
index 0000000..1a7c34b
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSScene.cs
@@ -0,0 +1,957 @@
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 BulletWorld 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 BulletWorld(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 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
501 // Only enable this in a limited test world with few objects.
502 // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
503
504 // step the physical world one interval
505 m_simulationStep++;
506 int numSubSteps = 0;
507
508 try
509 {
510 if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
511
512 numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
513 out updatedEntityCount, out m_updateArray, out collidersCount, out m_collisionArray);
514
515 if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
516 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
517 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
518 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
519 }
520 catch (Exception e)
521 {
522 m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}",
523 LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e);
524 DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}",
525 DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
526 updatedEntityCount = 0;
527 collidersCount = 0;
528 }
529
530 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in.
531
532 // Get a value for 'now' so all the collision and update routines don't have to get their own.
533 SimulationNowTime = Util.EnvironmentTickCount();
534
535 // If there were collisions, process them by sending the event to the prim.
536 // Collisions must be processed before updates.
537 if (collidersCount > 0)
538 {
539 for (int ii = 0; ii < collidersCount; ii++)
540 {
541 uint cA = m_collisionArray[ii].aID;
542 uint cB = m_collisionArray[ii].bID;
543 Vector3 point = new Vector3(m_collisionArray[ii].point.X, m_collisionArray[ii].point.Y,
544 m_collisionArray[ii].point.Z);
545 Vector3 normal = new Vector3(m_collisionArray[ii].normal.X, m_collisionArray[ii].normal.Y,
546 m_collisionArray[ii].normal.Z);
547 SendCollision(cA, cB, point, normal, 0.01f);
548 SendCollision(cB, cA, point, -normal, 0.01f);
549 }
550 }
551
552 // The above SendCollision's batch up the collisions on the objects.
553 // Now push the collisions into the simulator.
554 if (ObjectsWithCollisions.Count > 0)
555 {
556 foreach (BSPhysObject bsp in ObjectsWithCollisions)
557 if (!bsp.SendCollisions())
558 {
559 // If the object is done colliding, see that it's removed from the colliding list
560 ObjectsWithNoMoreCollisions.Add(bsp);
561 }
562 }
563
564 // This is a kludge to get avatar movement updates.
565 // The simulator expects collisions for avatars even if there are have been no collisions.
566 // The event updates avatar animations and stuff.
567 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
568 foreach (BSPhysObject bsp in m_avatars)
569 if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
570 bsp.SendCollisions();
571
572 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
573 // Not done above because it is inside an iteration of ObjectWithCollisions.
574 // This complex collision processing is required to create an empty collision
575 // event call after all collisions have happened on an object. This enables
576 // the simulator to generate the 'collision end' event.
577 if (ObjectsWithNoMoreCollisions.Count > 0)
578 {
579 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
580 ObjectsWithCollisions.Remove(po);
581 ObjectsWithNoMoreCollisions.Clear();
582 }
583 // Done with collisions.
584
585 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
586 if (updatedEntityCount > 0)
587 {
588 for (int ii = 0; ii < updatedEntityCount; ii++)
589 {
590
591 BulletXNA.EntityProperties entprop = m_updateArray[ii];
592 BSPhysObject pobj;
593 if (PhysObjects.TryGetValue(entprop.ID, out pobj))
594 {
595 EntityProperties prop = new EntityProperties()
596 {
597 Acceleration = new Vector3(entprop.Acceleration.X, entprop.Acceleration.Y, entprop.Acceleration.Z),
598 ID = entprop.ID,
599 Position = new Vector3(entprop.Position.X,entprop.Position.Y,entprop.Position.Z),
600 Rotation = new Quaternion(entprop.Rotation.X,entprop.Rotation.Y,entprop.Rotation.Z,entprop.Rotation.W),
601 RotationalVelocity = new Vector3(entprop.AngularVelocity.X,entprop.AngularVelocity.Y,entprop.AngularVelocity.Z),
602 Velocity = new Vector3(entprop.Velocity.X,entprop.Velocity.Y,entprop.Velocity.Z)
603 };
604 //m_log.Debug(pobj.Name + ":" + prop.ToString() + "\n");
605 pobj.UpdateProperties(prop);
606 }
607 }
608 }
609
610 TriggerPostStepEvent(timeStep);
611
612 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
613 // Only enable this in a limited test world with few objects.
614 // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
615
616 // The physics engine returns the number of milliseconds it simulated this call.
617 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
618 // Multiply by 55 to give a nominal frame rate of 55.
619 return (float)numSubSteps * m_fixedTimeStep * 1000f * 55f;
620 }
621
622 // Something has collided
623 private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration)
624 {
625 if (localID <= TerrainManager.HighestTerrainID)
626 {
627 return; // don't send collisions to the terrain
628 }
629
630 BSPhysObject collider;
631 if (!PhysObjects.TryGetValue(localID, out collider))
632 {
633 // If the object that is colliding cannot be found, just ignore the collision.
634 DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith);
635 return;
636 }
637
638 // The terrain is not in the physical object list so 'collidee' can be null when Collide() is called.
639 BSPhysObject collidee = null;
640 PhysObjects.TryGetValue(collidingWith, out collidee);
641
642 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
643
644 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
645 {
646 // If a collision was posted, remember to send it to the simulator
647 ObjectsWithCollisions.Add(collider);
648 }
649
650 return;
651 }
652
653 #endregion // Simulation
654
655 public override void GetResults() { }
656
657 #region Terrain
658
659 public override void SetTerrain(float[] heightMap) {
660 TerrainManager.SetTerrain(heightMap);
661 }
662
663 public override void SetWaterLevel(float baseheight)
664 {
665 SimpleWaterLevel = baseheight;
666 }
667
668 public override void DeleteTerrain()
669 {
670 // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
671 }
672
673 // Although no one seems to check this, I do support combining.
674 public override bool SupportsCombining()
675 {
676 return TerrainManager.SupportsCombining();
677 }
678 // This call says I am a child to region zero in a mega-region. 'pScene' is that
679 // of region zero, 'offset' is my offset from regions zero's origin, and
680 // 'extents' is the largest XY that is handled in my region.
681 public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
682 {
683 TerrainManager.Combine(pScene, offset, extents);
684 }
685
686 // Unhook all the combining that I know about.
687 public override void UnCombine(PhysicsScene pScene)
688 {
689 TerrainManager.UnCombine(pScene);
690 }
691
692 #endregion // Terrain
693
694 public override Dictionary<uint, float> GetTopColliders()
695 {
696 return new Dictionary<uint, float>();
697 }
698
699 public override bool IsThreaded { get { return false; } }
700
701 #region Taints
702 // The simulation execution order is:
703 // Simulate()
704 // DoOneTimeTaints
705 // TriggerPreStepEvent
706 // DoOneTimeTaints
707 // Step()
708 // ProcessAndForwardCollisions
709 // ProcessAndForwardPropertyUpdates
710 // TriggerPostStepEvent
711
712 // Calls to the PhysicsActors can't directly call into the physics engine
713 // because it might be busy. We delay changes to a known time.
714 // We rely on C#'s closure to save and restore the context for the delegate.
715 public void TaintedObject(String ident, TaintCallback callback)
716 {
717 if (!m_initialized) return;
718
719 lock (_taintLock)
720 {
721 _taintOperations.Add(new TaintCallbackEntry(ident, callback));
722 }
723
724 return;
725 }
726
727 // Sometimes a potentially tainted operation can be used in and out of taint time.
728 // This routine executes the command immediately if in taint-time otherwise it is queued.
729 public void TaintedObject(bool inTaintTime, string ident, TaintCallback callback)
730 {
731 if (inTaintTime)
732 callback();
733 else
734 TaintedObject(ident, callback);
735 }
736
737 private void TriggerPreStepEvent(float timeStep)
738 {
739 PreStepAction actions = BeforeStep;
740 if (actions != null)
741 actions(timeStep);
742
743 }
744
745 private void TriggerPostStepEvent(float timeStep)
746 {
747 PreStepAction actions = AfterStep;
748 if (actions != null)
749 actions(timeStep);
750
751 }
752
753 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues
754 // a callback into itself to do the actual property change. That callback is called
755 // here just before the physics engine is called to step the simulation.
756 public void ProcessTaints()
757 {
758 ProcessRegularTaints();
759 ProcessPostTaintTaints();
760 }
761
762 private void ProcessRegularTaints()
763 {
764 if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process
765 {
766 // swizzle a new list into the list location so we can process what's there
767 List<TaintCallbackEntry> oldList;
768 lock (_taintLock)
769 {
770 oldList = _taintOperations;
771 _taintOperations = new List<TaintCallbackEntry>();
772 }
773
774 foreach (TaintCallbackEntry tcbe in oldList)
775 {
776 try
777 {
778 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG
779 tcbe.callback();
780 }
781 catch (Exception e)
782 {
783 m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e);
784 }
785 }
786 oldList.Clear();
787 }
788 }
789
790 // Schedule an update to happen after all the regular taints are processed.
791 // Note that new requests for the same operation ("ident") for the same object ("ID")
792 // will replace any previous operation by the same object.
793 public void PostTaintObject(String ident, uint ID, TaintCallback callback)
794 {
795 string uniqueIdent = ident + "-" + ID.ToString();
796 lock (_taintLock)
797 {
798 _postTaintOperations[uniqueIdent] = new TaintCallbackEntry(uniqueIdent, callback);
799 }
800
801 return;
802 }
803
804 // Taints that happen after the normal taint processing but before the simulation step.
805 private void ProcessPostTaintTaints()
806 {
807 if (_postTaintOperations.Count > 0)
808 {
809 Dictionary<string, TaintCallbackEntry> oldList;
810 lock (_taintLock)
811 {
812 oldList = _postTaintOperations;
813 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
814 }
815
816 foreach (KeyValuePair<string,TaintCallbackEntry> kvp in oldList)
817 {
818 try
819 {
820 DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG
821 kvp.Value.callback();
822 }
823 catch (Exception e)
824 {
825 m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e);
826 }
827 }
828 oldList.Clear();
829 }
830 }
831
832 // Only used for debugging. Does not change state of anything so locking is not necessary.
833 public bool AssertInTaintTime(string whereFrom)
834 {
835 if (!InTaintTime)
836 {
837 DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
838 m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
839 Util.PrintCallStack(DetailLog);
840 }
841 return InTaintTime;
842 }
843
844 #endregion // Taints
845
846 #region INI and command line parameter processing
847
848 #region IPhysicsParameters
849 // Get the list of parameters this physics engine supports
850 public PhysParameterEntry[] GetParameterList()
851 {
852 BSParam.BuildParameterTable();
853 return BSParam.SettableParameters;
854 }
855
856 // Set parameter on a specific or all instances.
857 // Return 'false' if not able to set the parameter.
858 // Setting the value in the m_params block will change the value the physics engine
859 // will use the next time since it's pinned and shared memory.
860 // Some of the values require calling into the physics engine to get the new
861 // value activated ('terrainFriction' for instance).
862 public bool SetPhysicsParameter(string parm, float val, uint localID)
863 {
864 bool ret = false;
865 BSParam.ParameterDefn theParam;
866 if (BSParam.TryGetParameter(parm, out theParam))
867 {
868 theParam.setter(this, parm, localID, val);
869 ret = true;
870 }
871 return ret;
872 }
873
874 // update all the localIDs specified
875 // If the local ID is APPLY_TO_NONE, just change the default value
876 // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
877 // If the localID is a specific object, apply the parameter change to only that object
878 internal delegate void AssignVal(float x);
879 internal void UpdateParameterObject(AssignVal setDefault, string parm, uint localID, float val)
880 {
881 List<uint> objectIDs = new List<uint>();
882 switch (localID)
883 {
884 case PhysParameterEntry.APPLY_TO_NONE:
885 setDefault(val); // setting only the default value
886 // This will cause a call into the physical world if some operation is specified (SetOnObject).
887 objectIDs.Add(TERRAIN_ID);
888 TaintedUpdateParameter(parm, objectIDs, val);
889 break;
890 case PhysParameterEntry.APPLY_TO_ALL:
891 setDefault(val); // setting ALL also sets the default value
892 lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
893 TaintedUpdateParameter(parm, objectIDs, val);
894 break;
895 default:
896 // setting only one localID
897 objectIDs.Add(localID);
898 TaintedUpdateParameter(parm, objectIDs, val);
899 break;
900 }
901 }
902
903 // schedule the actual updating of the paramter to when the phys engine is not busy
904 private void TaintedUpdateParameter(string parm, List<uint> lIDs, float val)
905 {
906 float xval = val;
907 List<uint> xlIDs = lIDs;
908 string xparm = parm;
909 TaintedObject("BSScene.UpdateParameterSet", delegate() {
910 BSParam.ParameterDefn thisParam;
911 if (BSParam.TryGetParameter(xparm, out thisParam))
912 {
913 if (thisParam.onObject != null)
914 {
915 foreach (uint lID in xlIDs)
916 {
917 BSPhysObject theObject = null;
918 PhysObjects.TryGetValue(lID, out theObject);
919 thisParam.onObject(this, theObject, xval);
920 }
921 }
922 }
923 });
924 }
925
926 // Get parameter.
927 // Return 'false' if not able to get the parameter.
928 public bool GetPhysicsParameter(string parm, out float value)
929 {
930 float val = 0f;
931 bool ret = false;
932 BSParam.ParameterDefn theParam;
933 if (BSParam.TryGetParameter(parm, out theParam))
934 {
935 val = theParam.getter(this);
936 ret = true;
937 }
938 value = val;
939 return ret;
940 }
941
942 #endregion IPhysicsParameters
943
944 #endregion Runtime settable parameters
945
946 // Invoke the detailed logger and output something if it's enabled.
947 public void DetailLog(string msg, params Object[] args)
948 {
949 PhysicsLogging.Write(msg, args);
950 // Add the Flush() if debugging crashes. Gets all the messages written out.
951 if (m_physicsLoggingDoFlush) PhysicsLogging.Flush();
952 }
953 // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
954 public const string DetailLogZero = "0000000000";
955
956}
957}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSShapeCollection.cs
new file mode 100644
index 0000000..764aece
--- /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, BulletWorld 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, BulletWorld 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, false, 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, false, 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, BulletWorld 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..ba17059
--- /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..66d62f0
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainManager.cs
@@ -0,0 +1,461 @@
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, Vector3.Zero, Quaternion.Identity);
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
380 lastHeight = ret;
381 return ret;
382 }
383
384 public float GetWaterLevelAtXYZ(Vector3 pos)
385 {
386 float ret = WATER_HEIGHT_GETHEIGHT_RET;
387
388 Vector3 terrainBaseXYZ;
389 BSTerrainPhys physTerrain;
390 if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
391 {
392 ret = physTerrain.GetWaterLevelAtXYZ(pos);
393 }
394 else
395 {
396 PhysicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}",
397 LogHeader, PhysicsScene.RegionName, pos, terrainBaseXYZ, ret);
398 }
399 return ret;
400 }
401
402 // Given an address, return 'true' of there is a description of that terrain and output
403 // the descriptor class and the 'base' fo the addresses therein.
404 private bool GetTerrainPhysicalAtXYZ(Vector3 pos, out BSTerrainPhys outPhysTerrain, out Vector3 outTerrainBase)
405 {
406 int offsetX = ((int)(pos.X / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
407 int offsetY = ((int)(pos.Y / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
408 Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
409
410 BSTerrainPhys physTerrain = null;
411 lock (m_terrains)
412 {
413 m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain);
414 }
415 outTerrainBase = terrainBaseXYZ;
416 outPhysTerrain = physTerrain;
417 return (physTerrain != null);
418 }
419
420 // Although no one seems to check this, I do support combining.
421 public bool SupportsCombining()
422 {
423 return true;
424 }
425
426 // This routine is called two ways:
427 // One with 'offset' and 'pScene' zero and null but 'extents' giving the maximum
428 // extent of the combined regions. This is to inform the parent of the size
429 // of the combined regions.
430 // and one with 'offset' as the offset of the child region to the base region,
431 // 'pScene' pointing to the parent and 'extents' of zero. This informs the
432 // child of its relative base and new parent.
433 public void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
434 {
435 m_worldOffset = offset;
436 m_worldMax = extents;
437 MegaRegionParentPhysicsScene = pScene;
438 if (pScene != null)
439 {
440 // We are a child.
441 // We want m_worldMax to be the highest coordinate of our piece of terrain.
442 m_worldMax = offset + DefaultRegionSize;
443 }
444 DetailLog("{0},BSTerrainManager.Combine,offset={1},extents={2},wOffset={3},wMax={4}",
445 BSScene.DetailLogZero, offset, extents, m_worldOffset, m_worldMax);
446 }
447
448 // Unhook all the combining that I know about.
449 public void UnCombine(PhysicsScene pScene)
450 {
451 // Just like ODE, we don't do anything yet.
452 DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero);
453 }
454
455
456 private void DetailLog(string msg, params Object[] args)
457 {
458 PhysicsScene.PhysicsLogging.Write(msg, args);
459 }
460}
461}
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..93643c9
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BulletSimAPI.cs
@@ -0,0 +1,1603 @@
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[StructLayout(LayoutKind.Sequential)]
63public struct ConvexHull
64{
65 Vector3 Offset;
66 int VertexCount;
67 Vector3[] Vertices;
68}
69public enum BSPhysicsShapeType
70{
71 SHAPE_UNKNOWN = 0,
72 SHAPE_CAPSULE = 1,
73 SHAPE_BOX = 2,
74 SHAPE_CONE = 3,
75 SHAPE_CYLINDER = 4,
76 SHAPE_SPHERE = 5,
77 SHAPE_MESH = 6,
78 SHAPE_HULL = 7,
79 // following defined by BulletSim
80 SHAPE_GROUNDPLANE = 20,
81 SHAPE_TERRAIN = 21,
82 SHAPE_COMPOUND = 22,
83 SHAPE_HEIGHTMAP = 23,
84};
85
86// The native shapes have predefined shape hash keys
87public enum FixedShapeKey : ulong
88{
89 KEY_NONE = 0,
90 KEY_BOX = 1,
91 KEY_SPHERE = 2,
92 KEY_CONE = 3,
93 KEY_CYLINDER = 4,
94 KEY_CAPSULE = 5,
95}
96
97[StructLayout(LayoutKind.Sequential)]
98public struct ShapeData
99{
100 public uint ID;
101 public BSPhysicsShapeType Type;
102 public Vector3 Position;
103 public Quaternion Rotation;
104 public Vector3 Velocity;
105 public Vector3 Scale;
106 public float Mass;
107 public float Buoyancy;
108 public System.UInt64 HullKey;
109 public System.UInt64 MeshKey;
110 public float Friction;
111 public float Restitution;
112 public float Collidable; // true of things bump into this
113 public float Static; // true if a static object. Otherwise gravity, etc.
114 public float Solid; // true if object cannot be passed through
115 public Vector3 Size;
116
117 // note that bools are passed as floats since bool size changes by language and architecture
118 public const float numericTrue = 1f;
119 public const float numericFalse = 0f;
120}
121[StructLayout(LayoutKind.Sequential)]
122public struct SweepHit
123{
124 public uint ID;
125 public float Fraction;
126 public Vector3 Normal;
127 public Vector3 Point;
128}
129[StructLayout(LayoutKind.Sequential)]
130public struct RaycastHit
131{
132 public uint ID;
133 public float Fraction;
134 public Vector3 Normal;
135}
136[StructLayout(LayoutKind.Sequential)]
137public struct CollisionDesc
138{
139 public uint aID;
140 public uint bID;
141 public Vector3 point;
142 public Vector3 normal;
143}
144[StructLayout(LayoutKind.Sequential)]
145public struct EntityProperties
146{
147 public uint ID;
148 public Vector3 Position;
149 public Quaternion Rotation;
150 public Vector3 Velocity;
151 public Vector3 Acceleration;
152 public Vector3 RotationalVelocity;
153 public override string ToString()
154 {
155 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}>",
156 ID.ToString(),
157 Position.X,Position.Y,Position.Z,
158 Rotation.X,Rotation.Y,Rotation.Z,Rotation.W,
159 Velocity.X,Velocity.Y,Velocity.Z,
160 RotationalVelocity.X,RotationalVelocity.Y,RotationalVelocity.Z
161 );
162 }
163}
164
165// Format of this structure must match the definition in the C++ code
166// NOTE: adding the X causes compile breaks if used. These are unused symbols
167// that can be removed from both here and the unmanaged definition of this structure.
168[StructLayout(LayoutKind.Sequential)]
169public struct ConfigurationParameters
170{
171 public float defaultFriction;
172 public float defaultDensity;
173 public float defaultRestitution;
174 public float collisionMargin;
175 public float gravity;
176
177 public float XlinearDamping;
178 public float XangularDamping;
179 public float XdeactivationTime;
180 public float XlinearSleepingThreshold;
181 public float XangularSleepingThreshold;
182 public float XccdMotionThreshold;
183 public float XccdSweptSphereRadius;
184 public float XcontactProcessingThreshold;
185
186 public float XterrainImplementation;
187 public float XterrainFriction;
188 public float XterrainHitFraction;
189 public float XterrainRestitution;
190 public float XterrainCollisionMargin;
191
192 public float XavatarFriction;
193 public float XavatarStandingFriction;
194 public float XavatarDensity;
195 public float XavatarRestitution;
196 public float XavatarCapsuleWidth;
197 public float XavatarCapsuleDepth;
198 public float XavatarCapsuleHeight;
199 public float XavatarContactProcessingThreshold;
200
201 public float XvehicleAngularDamping;
202
203 public float maxPersistantManifoldPoolSize;
204 public float maxCollisionAlgorithmPoolSize;
205 public float shouldDisableContactPoolDynamicAllocation;
206 public float shouldForceUpdateAllAabbs;
207 public float shouldRandomizeSolverOrder;
208 public float shouldSplitSimulationIslands;
209 public float shouldEnableFrictionCaching;
210 public float numberOfSolverIterations;
211
212 public float XlinksetImplementation;
213 public float XlinkConstraintUseFrameOffset;
214 public float XlinkConstraintEnableTransMotor;
215 public float XlinkConstraintTransMotorMaxVel;
216 public float XlinkConstraintTransMotorMaxForce;
217 public float XlinkConstraintERP;
218 public float XlinkConstraintCFM;
219 public float XlinkConstraintSolverIterations;
220
221 public float physicsLoggingFrames;
222
223 public const float numericTrue = 1f;
224 public const float numericFalse = 0f;
225}
226
227
228// The states a bullet collision object can have
229
230public enum ActivationState : uint
231{
232 UNDEFINED = 0,
233 ACTIVE_TAG = 1,
234 ISLAND_SLEEPING = 2,
235 WANTS_DEACTIVATION = 3,
236 DISABLE_DEACTIVATION = 4,
237 DISABLE_SIMULATION = 5,
238}
239
240public enum CollisionObjectTypes : int
241{
242 CO_COLLISION_OBJECT = 1 << 0,
243 CO_RIGID_BODY = 1 << 1,
244 CO_GHOST_OBJECT = 1 << 2,
245 CO_SOFT_BODY = 1 << 3,
246 CO_HF_FLUID = 1 << 4,
247 CO_USER_TYPE = 1 << 5,
248}
249
250// Values used by Bullet and BulletSim to control object properties.
251// Bullet's "CollisionFlags" has more to do with operations on the
252// object (if collisions happen, if gravity effects it, ...).
253 [Flags]
254public enum CollisionFlags : uint
255{
256 CF_STATIC_OBJECT = 1 << 0,
257 CF_KINEMATIC_OBJECT = 1 << 1,
258 CF_NO_CONTACT_RESPONSE = 1 << 2,
259 CF_CUSTOM_MATERIAL_CALLBACK = 1 << 3,
260 CF_CHARACTER_OBJECT = 1 << 4,
261 CF_DISABLE_VISUALIZE_OBJECT = 1 << 5,
262 CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6,
263 // Following used by BulletSim to control collisions and updates
264 BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10,
265 BS_FLOATS_ON_WATER = 1 << 11,
266 BS_VEHICLE_COLLISIONS = 1 << 12,
267 BS_NONE = 0,
268 BS_ALL = 0xFFFFFFFF,
269
270 // These are the collision flags switched depending on physical state.
271 // The other flags are used for other things and should not be fooled with.
272 BS_ACTIVE = CF_STATIC_OBJECT
273 | CF_KINEMATIC_OBJECT
274 | CF_NO_CONTACT_RESPONSE
275};
276
277// Values for collisions groups and masks
278public enum CollisionFilterGroups : uint
279{
280 // Don't use the bit definitions!! Define the use in a
281 // filter/mask definition below. This way collision interactions
282 // are more easily debugged.
283 BNoneGroup = 0,
284 BDefaultGroup = 1 << 0,
285 BStaticGroup = 1 << 1,
286 BKinematicGroup = 1 << 2,
287 BDebrisGroup = 1 << 3,
288 BSensorTrigger = 1 << 4,
289 BCharacterGroup = 1 << 5,
290 BAllGroup = 0xFFFFFFFF,
291 // Filter groups defined by BulletSim
292 BGroundPlaneGroup = 1 << 10,
293 BTerrainGroup = 1 << 11,
294 BRaycastGroup = 1 << 12,
295 BSolidGroup = 1 << 13,
296 // BLinksetGroup = xx // a linkset proper is either static or dynamic
297 BLinksetChildGroup = 1 << 14,
298 // The collsion filters and masked are defined in one place -- don't want them scattered
299 AvatarGroup = BCharacterGroup,
300 AvatarMask = BAllGroup,
301 ObjectGroup = BSolidGroup,
302 ObjectMask = BAllGroup,
303 StaticObjectGroup = BStaticGroup,
304 StaticObjectMask = AvatarGroup | ObjectGroup, // static things don't interact with much
305 LinksetGroup = BLinksetChildGroup,
306 LinksetMask = BAllGroup & ~BLinksetChildGroup, // linkset objects don't collide with each other
307 VolumeDetectGroup = BSensorTrigger,
308 VolumeDetectMask = ~BSensorTrigger,
309 TerrainGroup = BTerrainGroup,
310 TerrainMask = BAllGroup & ~BStaticGroup, // static objects on the ground don't collide
311 GroundPlaneGroup = BGroundPlaneGroup,
312 GroundPlaneMask = BAllGroup
313
314};
315
316// CFM controls the 'hardness' of the constraint. 0=fixed, 0..1=violatable. Default=0
317// ERP controls amount of correction per tick. Usable range=0.1..0.8. Default=0.2.
318public enum ConstraintParams : int
319{
320 BT_CONSTRAINT_ERP = 1, // this one is not used in Bullet as of 20120730
321 BT_CONSTRAINT_STOP_ERP,
322 BT_CONSTRAINT_CFM,
323 BT_CONSTRAINT_STOP_CFM,
324};
325public enum ConstraintParamAxis : int
326{
327 AXIS_LINEAR_X = 0,
328 AXIS_LINEAR_Y,
329 AXIS_LINEAR_Z,
330 AXIS_ANGULAR_X,
331 AXIS_ANGULAR_Y,
332 AXIS_ANGULAR_Z,
333 AXIS_LINEAR_ALL = 20, // these last three added by BulletSim so we don't have to do zillions of calls
334 AXIS_ANGULAR_ALL,
335 AXIS_ALL
336};
337
338// ===============================================================================
339static class BulletSimAPI {
340 private static int m_collisionsThisFrame;
341 public delegate void DebugLogCallback(string msg);
342 /// <summary>
343 ///
344 /// </summary>
345 /// <param name="p"></param>
346 /// <param name="p_2"></param>
347 internal static bool RemoveObjectFromWorld2(object pWorld, object pBody)
348 {
349 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
350 RigidBody body = pBody as RigidBody;
351 world.RemoveRigidBody(body);
352 return true;
353 }
354
355 internal static void SetRestitution2(object pBody, float pRestitution)
356 {
357 RigidBody body = pBody as RigidBody;
358 body.SetRestitution(pRestitution);
359 }
360
361 internal static void SetMargin2(object pShape, float pMargin)
362 {
363 CollisionShape shape = pShape as CollisionShape;
364 shape.SetMargin(pMargin);
365 }
366
367 internal static void SetLocalScaling2(object pShape, Vector3 pScale)
368 {
369 CollisionShape shape = pShape as CollisionShape;
370 IndexedVector3 vec = new IndexedVector3(pScale.X, pScale.Y, pScale.Z);
371 shape.SetLocalScaling(ref vec);
372
373 }
374
375 internal static void SetContactProcessingThreshold2(object pBody, float contactprocessingthreshold)
376 {
377 RigidBody body = pBody as RigidBody;
378 body.SetContactProcessingThreshold(contactprocessingthreshold);
379 }
380
381 internal static void SetCcdMotionThreshold2(object pBody, float pccdMotionThreashold)
382 {
383 RigidBody body = pBody as RigidBody;
384 body.SetCcdMotionThreshold(pccdMotionThreashold);
385 }
386
387 internal static void SetCcdSweptSphereRadius2(object pBody, float pCcdSweptSphereRadius)
388 {
389 RigidBody body = pBody as RigidBody;
390 body.SetCcdSweptSphereRadius(pCcdSweptSphereRadius);
391 }
392
393 internal static void SetAngularFactorV2(object pBody, Vector3 pAngularFactor)
394 {
395 RigidBody body = pBody as RigidBody;
396 body.SetAngularFactor(new IndexedVector3(pAngularFactor.X, pAngularFactor.Y, pAngularFactor.Z));
397 }
398
399 internal static CollisionFlags AddToCollisionFlags2(object pBody, CollisionFlags pcollisionFlags)
400 {
401 CollisionObject body = pBody as CollisionObject;
402 CollisionFlags existingcollisionFlags = (CollisionFlags)(uint)body.GetCollisionFlags();
403 existingcollisionFlags |= pcollisionFlags;
404 body.SetCollisionFlags((BulletXNA.BulletCollision.CollisionFlags)(uint)existingcollisionFlags);
405 return (CollisionFlags) (uint) existingcollisionFlags;
406 }
407
408 internal static void AddObjectToWorld2(object pWorld, object pBody)
409 {
410 RigidBody body = pBody as RigidBody;
411 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
412 //if (!(body.GetCollisionShape().GetShapeType() == BroadphaseNativeTypes.STATIC_PLANE_PROXYTYPE && body.GetCollisionShape().GetShapeType() == BroadphaseNativeTypes.TERRAIN_SHAPE_PROXYTYPE))
413
414 world.AddRigidBody(body);
415
416 //if (body.GetBroadphaseHandle() != null)
417 // world.UpdateSingleAabb(body);
418 }
419
420 internal static void AddObjectToWorld2(object pWorld, object pBody, Vector3 _position, Quaternion _orientation)
421 {
422 RigidBody body = pBody as RigidBody;
423 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
424 //if (!(body.GetCollisionShape().GetShapeType() == BroadphaseNativeTypes.STATIC_PLANE_PROXYTYPE && body.GetCollisionShape().GetShapeType() == BroadphaseNativeTypes.TERRAIN_SHAPE_PROXYTYPE))
425
426 world.AddRigidBody(body);
427 IndexedVector3 vposition = new IndexedVector3(_position.X, _position.Y, _position.Z);
428 IndexedQuaternion vquaternion = new IndexedQuaternion(_orientation.X, _orientation.Y, _orientation.Z,
429 _orientation.W);
430 IndexedMatrix mat = IndexedMatrix.CreateFromQuaternion(vquaternion);
431 mat._origin = vposition;
432 body.SetWorldTransform(mat);
433 //if (body.GetBroadphaseHandle() != null)
434 // world.UpdateSingleAabb(body);
435 }
436
437 internal static void ForceActivationState2(object pBody, ActivationState pActivationState)
438 {
439 CollisionObject body = pBody as CollisionObject;
440 body.ForceActivationState((BulletXNA.BulletCollision.ActivationState)(uint)pActivationState);
441 }
442
443 internal static void UpdateSingleAabb2(object pWorld, object pBody)
444 {
445 CollisionObject body = pBody as CollisionObject;
446 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
447 world.UpdateSingleAabb(body);
448 }
449
450 internal static bool SetCollisionGroupMask2(object pBody, uint pGroup, uint pMask)
451 {
452 RigidBody body = pBody as RigidBody;
453 body.GetBroadphaseHandle().m_collisionFilterGroup = (BulletXNA.BulletCollision.CollisionFilterGroups) pGroup;
454 body.GetBroadphaseHandle().m_collisionFilterGroup = (BulletXNA.BulletCollision.CollisionFilterGroups) pGroup;
455 if ((uint) body.GetBroadphaseHandle().m_collisionFilterGroup == 0)
456 return false;
457 return true;
458 }
459
460 internal static void ClearAllForces2(object pBody)
461 {
462 CollisionObject body = pBody as CollisionObject;
463 IndexedVector3 zeroVector = new IndexedVector3(0, 0, 0);
464 body.SetInterpolationLinearVelocity(ref zeroVector);
465 body.SetInterpolationAngularVelocity(ref zeroVector);
466 IndexedMatrix bodytransform = body.GetWorldTransform();
467
468 body.SetInterpolationWorldTransform(ref bodytransform);
469
470 if (body is RigidBody)
471 {
472 RigidBody rigidbody = body as RigidBody;
473 rigidbody.SetLinearVelocity(zeroVector);
474 rigidbody.SetAngularVelocity(zeroVector);
475 rigidbody.ClearForces();
476 }
477 }
478
479 internal static void SetInterpolationAngularVelocity2(object pBody, Vector3 pVector3)
480 {
481 RigidBody body = pBody as RigidBody;
482 IndexedVector3 vec = new IndexedVector3(pVector3.X, pVector3.Y, pVector3.Z);
483 body.SetInterpolationAngularVelocity(ref vec);
484 }
485
486 internal static void SetAngularVelocity2(object pBody, Vector3 pVector3)
487 {
488 RigidBody body = pBody as RigidBody;
489 IndexedVector3 vec = new IndexedVector3(pVector3.X, pVector3.Y, pVector3.Z);
490 body.SetAngularVelocity(ref vec);
491 }
492
493 internal static void ClearForces2(object pBody)
494 {
495 RigidBody body = pBody as RigidBody;
496 body.ClearForces();
497 }
498
499 internal static void SetTranslation2(object pBody, Vector3 _position, Quaternion _orientation)
500 {
501 RigidBody body = pBody as RigidBody;
502 IndexedVector3 vposition = new IndexedVector3(_position.X, _position.Y, _position.Z);
503 IndexedQuaternion vquaternion = new IndexedQuaternion(_orientation.X, _orientation.Y, _orientation.Z,
504 _orientation.W);
505 IndexedMatrix mat = IndexedMatrix.CreateFromQuaternion(vquaternion);
506 mat._origin = vposition;
507 body.SetWorldTransform(mat);
508
509 }
510
511 internal static Vector3 GetPosition2(object pBody)
512 {
513 RigidBody body = pBody as RigidBody;
514 IndexedVector3 pos = body.GetInterpolationWorldTransform()._origin;
515 return new Vector3(pos.X, pos.Y, pos.Z);
516 }
517
518 internal static Vector3 CalculateLocalInertia2(object pShape, float pphysMass)
519 {
520 CollisionShape shape = pShape as CollisionShape;
521 IndexedVector3 inertia = IndexedVector3.Zero;
522 shape.CalculateLocalInertia(pphysMass, out inertia);
523 return new Vector3(inertia.X, inertia.Y, inertia.Z);
524 }
525
526 internal static void SetMassProps2(object pBody, float pphysMass, Vector3 plocalInertia)
527 {
528 RigidBody body = pBody as RigidBody;
529 IndexedVector3 inertia = new IndexedVector3(plocalInertia.X, plocalInertia.Y, plocalInertia.Z);
530 body.SetMassProps(pphysMass, inertia);
531 }
532
533
534 internal static void SetObjectForce2(object pBody, Vector3 _force)
535 {
536 RigidBody body = pBody as RigidBody;
537 IndexedVector3 force = new IndexedVector3(_force.X, _force.Y, _force.Z);
538 body.SetTotalForce(ref force);
539 }
540
541 internal static void SetFriction2(object pBody, float _currentFriction)
542 {
543 RigidBody body = pBody as RigidBody;
544 body.SetFriction(_currentFriction);
545 }
546
547 internal static void SetLinearVelocity2(object pBody, Vector3 _velocity)
548 {
549 RigidBody body = pBody as RigidBody;
550 IndexedVector3 velocity = new IndexedVector3(_velocity.X, _velocity.Y, _velocity.Z);
551 body.SetLinearVelocity(velocity);
552 }
553
554 internal static void Activate2(object pBody, bool pforceactivation)
555 {
556 RigidBody body = pBody as RigidBody;
557 body.Activate(pforceactivation);
558
559 }
560
561 internal static Quaternion GetOrientation2(object pBody)
562 {
563 RigidBody body = pBody as RigidBody;
564 IndexedQuaternion mat = body.GetInterpolationWorldTransform().GetRotation();
565 return new Quaternion(mat.X, mat.Y, mat.Z, mat.W);
566 }
567
568 internal static CollisionFlags RemoveFromCollisionFlags2(object pBody, CollisionFlags pcollisionFlags)
569 {
570 RigidBody body = pBody as RigidBody;
571 CollisionFlags existingcollisionFlags = (CollisionFlags)(uint)body.GetCollisionFlags();
572 existingcollisionFlags &= ~pcollisionFlags;
573 body.SetCollisionFlags((BulletXNA.BulletCollision.CollisionFlags)(uint)existingcollisionFlags);
574 return (CollisionFlags)(uint)existingcollisionFlags;
575 }
576
577 internal static void SetGravity2(object pBody, Vector3 pGravity)
578 {
579 RigidBody body = pBody as RigidBody;
580 IndexedVector3 gravity = new IndexedVector3(pGravity.X, pGravity.Y, pGravity.Z);
581 body.SetGravity(gravity);
582 }
583
584 internal static bool DestroyConstraint2(object pBody, object pConstraint)
585 {
586 RigidBody body = pBody as RigidBody;
587 TypedConstraint constraint = pConstraint as TypedConstraint;
588 body.RemoveConstraintRef(constraint);
589 return true;
590 }
591
592 internal static bool SetLinearLimits2(object pConstraint, Vector3 low, Vector3 high)
593 {
594 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
595 IndexedVector3 lowlimit = new IndexedVector3(low.X, low.Y, low.Z);
596 IndexedVector3 highlimit = new IndexedVector3(high.X, high.Y, high.Z);
597 constraint.SetLinearLowerLimit(lowlimit);
598 constraint.SetLinearUpperLimit(highlimit);
599 return true;
600 }
601
602 internal static bool SetAngularLimits2(object pConstraint, Vector3 low, Vector3 high)
603 {
604 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
605 IndexedVector3 lowlimit = new IndexedVector3(low.X, low.Y, low.Z);
606 IndexedVector3 highlimit = new IndexedVector3(high.X, high.Y, high.Z);
607 constraint.SetAngularLowerLimit(lowlimit);
608 constraint.SetAngularUpperLimit(highlimit);
609 return true;
610 }
611
612 internal static void SetConstraintNumSolverIterations2(object pConstraint, float cnt)
613 {
614 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
615 constraint.SetOverrideNumSolverIterations((int)cnt);
616 }
617
618 internal static void CalculateTransforms2(object pConstraint)
619 {
620 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
621 constraint.CalculateTransforms();
622 }
623
624 internal static void SetConstraintEnable2(object pConstraint, float p_2)
625 {
626 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
627 constraint.SetEnabled((p_2 == 0) ? false : true);
628 }
629
630
631 //BulletSimAPI.Create6DofConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,frame1, frame1rot,frame2, frame2rot,useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
632 internal static object Create6DofConstraint2(object pWorld, object pBody1, object pBody2, Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies)
633
634 {
635 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
636 RigidBody body1 = pBody1 as RigidBody;
637 RigidBody body2 = pBody2 as RigidBody;
638 IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z);
639 IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W);
640 IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot);
641 frame1._origin = frame1v;
642
643 IndexedVector3 frame2v = new IndexedVector3(pframe2.X, pframe2.Y, pframe2.Z);
644 IndexedQuaternion frame2rot = new IndexedQuaternion(pframe2rot.X, pframe2rot.Y, pframe2rot.Z, pframe2rot.W);
645 IndexedMatrix frame2 = IndexedMatrix.CreateFromQuaternion(frame2rot);
646 frame2._origin = frame1v;
647
648 Generic6DofConstraint consttr = new Generic6DofConstraint(body1, body2, ref frame1, ref frame2,
649 puseLinearReferenceFrameA);
650 consttr.CalculateTransforms();
651 world.AddConstraint(consttr,pdisableCollisionsBetweenLinkedBodies);
652
653 return consttr;
654 }
655
656
657 /// <summary>
658 ///
659 /// </summary>
660 /// <param name="pWorld"></param>
661 /// <param name="pBody1"></param>
662 /// <param name="pBody2"></param>
663 /// <param name="pjoinPoint"></param>
664 /// <param name="puseLinearReferenceFrameA"></param>
665 /// <param name="pdisableCollisionsBetweenLinkedBodies"></param>
666 /// <returns></returns>
667 internal static object Create6DofConstraintToPoint2(object pWorld, object pBody1, object pBody2, Vector3 pjoinPoint, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies)
668 {
669 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
670 RigidBody body1 = pBody1 as RigidBody;
671 RigidBody body2 = pBody2 as RigidBody;
672 IndexedMatrix frame1 = new IndexedMatrix(IndexedBasisMatrix.Identity, new IndexedVector3(0, 0, 0));
673 IndexedMatrix frame2 = new IndexedMatrix(IndexedBasisMatrix.Identity, new IndexedVector3(0, 0, 0));
674
675 IndexedVector3 joinPoint = new IndexedVector3(pjoinPoint.X, pjoinPoint.Y, pjoinPoint.Z);
676 IndexedMatrix mat = IndexedMatrix.Identity;
677 mat._origin = new IndexedVector3(pjoinPoint.X, pjoinPoint.Y, pjoinPoint.Z);
678 frame1._origin = body1.GetWorldTransform().Inverse()*joinPoint;
679 frame2._origin = body2.GetWorldTransform().Inverse()*joinPoint;
680
681 Generic6DofConstraint consttr = new Generic6DofConstraint(body1, body2, ref frame1, ref frame2, puseLinearReferenceFrameA);
682 consttr.CalculateTransforms();
683 world.AddConstraint(consttr, pdisableCollisionsBetweenLinkedBodies);
684
685 return consttr;
686 }
687 //SetFrames2(m_constraint.ptr, frameA, frameArot, frameB, frameBrot);
688 internal static void SetFrames2(object pConstraint, Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot)
689 {
690 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
691 IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z);
692 IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W);
693 IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot);
694 frame1._origin = frame1v;
695
696 IndexedVector3 frame2v = new IndexedVector3(pframe2.X, pframe2.Y, pframe2.Z);
697 IndexedQuaternion frame2rot = new IndexedQuaternion(pframe2rot.X, pframe2rot.Y, pframe2rot.Z, pframe2rot.W);
698 IndexedMatrix frame2 = IndexedMatrix.CreateFromQuaternion(frame2rot);
699 frame2._origin = frame1v;
700 constraint.SetFrames(ref frame1, ref frame2);
701 }
702
703
704
705
706 internal static bool IsInWorld2(object pWorld, object pShapeObj)
707 {
708 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
709 CollisionObject shape = pShapeObj as CollisionObject;
710 return world.IsInWorld(shape);
711 }
712
713 internal static void SetInterpolationLinearVelocity2(object pBody, Vector3 VehicleVelocity)
714 {
715 RigidBody body = pBody as RigidBody;
716 IndexedVector3 velocity = new IndexedVector3(VehicleVelocity.X, VehicleVelocity.Y, VehicleVelocity.Z);
717 body.SetInterpolationLinearVelocity(ref velocity);
718 }
719
720 internal static bool UseFrameOffset2(object pConstraint, float onOff)
721 {
722 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
723 constraint.SetUseFrameOffset((onOff == 0) ? false : true);
724 return true;
725 }
726 //SetBreakingImpulseThreshold2(m_constraint.ptr, threshold);
727 internal static bool SetBreakingImpulseThreshold2(object pConstraint, float threshold)
728 {
729 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
730 constraint.SetBreakingImpulseThreshold(threshold);
731 return true;
732 }
733 //BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping);
734 internal static void SetAngularDamping2(object pBody, float angularDamping)
735 {
736 RigidBody body = pBody as RigidBody;
737 float lineardamping = body.GetLinearDamping();
738 body.SetDamping(lineardamping, angularDamping);
739
740 }
741
742 internal static void UpdateInertiaTensor2(object pBody)
743 {
744 RigidBody body = pBody as RigidBody;
745 body.UpdateInertiaTensor();
746 }
747
748 internal static void RecalculateCompoundShapeLocalAabb2( object pCompoundShape)
749 {
750
751 CompoundShape shape = pCompoundShape as CompoundShape;
752 shape.RecalculateLocalAabb();
753 }
754
755 //BulletSimAPI.GetCollisionFlags2(PhysBody.ptr)
756 internal static CollisionFlags GetCollisionFlags2(object pBody)
757 {
758 RigidBody body = pBody as RigidBody;
759 uint flags = (uint)body.GetCollisionFlags();
760 return (CollisionFlags) flags;
761 }
762
763 internal static void SetDamping2(object pBody, float pLinear, float pAngular)
764 {
765 RigidBody body = pBody as RigidBody;
766 body.SetDamping(pLinear, pAngular);
767 }
768 //PhysBody.ptr, PhysicsScene.Params.deactivationTime);
769 internal static void SetDeactivationTime2(object pBody, float pDeactivationTime)
770 {
771 RigidBody body = pBody as RigidBody;
772 body.SetDeactivationTime(pDeactivationTime);
773 }
774 //SetSleepingThresholds2(PhysBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold);
775 internal static void SetSleepingThresholds2(object pBody, float plinearSleepingThreshold, float pangularSleepingThreshold)
776 {
777 RigidBody body = pBody as RigidBody;
778 body.SetSleepingThresholds(plinearSleepingThreshold, pangularSleepingThreshold);
779 }
780
781 internal static CollisionObjectTypes GetBodyType2(object pBody)
782 {
783 RigidBody body = pBody as RigidBody;
784 return (CollisionObjectTypes)(int) body.GetInternalType();
785 }
786
787 //BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, fSum);
788 internal static void ApplyCentralForce2(object pBody, Vector3 pfSum)
789 {
790 RigidBody body = pBody as RigidBody;
791 IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z);
792 body.ApplyCentralForce(ref fSum);
793 }
794 internal static void ApplyCentralImpulse2(object pBody, Vector3 pfSum)
795 {
796 RigidBody body = pBody as RigidBody;
797 IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z);
798 body.ApplyCentralImpulse(ref fSum);
799 }
800 internal static void ApplyTorque2(object pBody, Vector3 pfSum)
801 {
802 RigidBody body = pBody as RigidBody;
803 IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z);
804 body.ApplyTorque(ref fSum);
805 }
806 internal static void ApplyTorqueImpulse2(object pBody, Vector3 pfSum)
807 {
808 RigidBody body = pBody as RigidBody;
809 IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z);
810 body.ApplyTorqueImpulse(ref fSum);
811 }
812
813 internal static void DumpRigidBody2(object p, object p_2)
814 {
815 //TODO:
816 }
817
818 internal static void DumpCollisionShape2(object p, object p_2)
819 {
820 //TODO:
821 }
822
823 internal static void DestroyObject2(object p, object p_2)
824 {
825 //TODO:
826 }
827
828 internal static void Shutdown2(object pWorld)
829 {
830 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
831 world.Cleanup();
832 }
833
834 internal static void DeleteCollisionShape2(object p, object p_2)
835 {
836 //TODO:
837 }
838 //(sim.ptr, shape.ptr, prim.LocalID, prim.RawPosition, prim.RawOrientation);
839
840 internal static object CreateBodyFromShape2(object pWorld, object pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation)
841 {
842 CollisionWorld world = pWorld as CollisionWorld;
843 IndexedMatrix mat =
844 IndexedMatrix.CreateFromQuaternion(new IndexedQuaternion(pRawOrientation.X, pRawOrientation.Y,
845 pRawOrientation.Z, pRawOrientation.W));
846 mat._origin = new IndexedVector3(pRawPosition.X, pRawPosition.Y, pRawPosition.Z);
847 CollisionShape shape = pShape as CollisionShape;
848 //UpdateSingleAabb2(world, shape);
849 // TODO: Feed Update array into null
850 RigidBody body = new RigidBody(0,new SimMotionState(world,pLocalID,mat,null),shape,IndexedVector3.Zero);
851
852 body.SetUserPointer(pLocalID);
853 return body;
854 }
855
856
857 internal static object CreateBodyWithDefaultMotionState2( object pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation)
858 {
859
860 IndexedMatrix mat =
861 IndexedMatrix.CreateFromQuaternion(new IndexedQuaternion(pRawOrientation.X, pRawOrientation.Y,
862 pRawOrientation.Z, pRawOrientation.W));
863 mat._origin = new IndexedVector3(pRawPosition.X, pRawPosition.Y, pRawPosition.Z);
864
865 CollisionShape shape = pShape as CollisionShape;
866
867 // TODO: Feed Update array into null
868 RigidBody body = new RigidBody(0, new DefaultMotionState( mat, IndexedMatrix.Identity), shape, IndexedVector3.Zero);
869 body.SetWorldTransform(mat);
870 body.SetUserPointer(pLocalID);
871 return body;
872 }
873 //(m_mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
874 internal static void SetCollisionFlags2(object pBody, CollisionFlags collisionFlags)
875 {
876 RigidBody body = pBody as RigidBody;
877 body.SetCollisionFlags((BulletXNA.BulletCollision.CollisionFlags) (uint) collisionFlags);
878 }
879 //(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
880 internal static void SetHitFraction2(object pBody, float pHitFraction)
881 {
882 RigidBody body = pBody as RigidBody;
883 body.SetHitFraction(pHitFraction);
884 }
885 //BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale);
886 internal static object BuildCapsuleShape2(object pWorld, float pRadius, float pHeight, Vector3 pScale)
887 {
888 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
889 IndexedVector3 scale = new IndexedVector3(pScale.X, pScale.Y, pScale.Z);
890 CapsuleShapeZ capsuleShapeZ = new CapsuleShapeZ(pRadius, pHeight);
891 capsuleShapeZ.SetMargin(world.WorldSettings.Params.collisionMargin);
892 capsuleShapeZ.SetLocalScaling(ref scale);
893
894 return capsuleShapeZ;
895 }
896
897 public static object Initialize2(Vector3 worldExtent, ConfigurationParameters[] o, int mMaxCollisionsPerFrame, ref List<BulletXNA.CollisionDesc> collisionArray, int mMaxUpdatesPerFrame, ref List<BulletXNA.EntityProperties> updateArray, object mDebugLogCallbackHandle)
898 {
899 CollisionWorld.WorldData.ParamData p = new CollisionWorld.WorldData.ParamData();
900
901 p.angularDamping = o[0].XangularDamping;
902 p.defaultFriction = o[0].defaultFriction;
903 p.defaultFriction = o[0].defaultFriction;
904 p.defaultDensity = o[0].defaultDensity;
905 p.defaultRestitution = o[0].defaultRestitution;
906 p.collisionMargin = o[0].collisionMargin;
907 p.gravity = o[0].gravity;
908
909 p.linearDamping = o[0].XlinearDamping;
910 p.angularDamping = o[0].XangularDamping;
911 p.deactivationTime = o[0].XdeactivationTime;
912 p.linearSleepingThreshold = o[0].XlinearSleepingThreshold;
913 p.angularSleepingThreshold = o[0].XangularSleepingThreshold;
914 p.ccdMotionThreshold = o[0].XccdMotionThreshold;
915 p.ccdSweptSphereRadius = o[0].XccdSweptSphereRadius;
916 p.contactProcessingThreshold = o[0].XcontactProcessingThreshold;
917
918 p.terrainImplementation = o[0].XterrainImplementation;
919 p.terrainFriction = o[0].XterrainFriction;
920
921 p.terrainHitFraction = o[0].XterrainHitFraction;
922 p.terrainRestitution = o[0].XterrainRestitution;
923 p.terrainCollisionMargin = o[0].XterrainCollisionMargin;
924
925 p.avatarFriction = o[0].XavatarFriction;
926 p.avatarStandingFriction = o[0].XavatarStandingFriction;
927 p.avatarDensity = o[0].XavatarDensity;
928 p.avatarRestitution = o[0].XavatarRestitution;
929 p.avatarCapsuleWidth = o[0].XavatarCapsuleWidth;
930 p.avatarCapsuleDepth = o[0].XavatarCapsuleDepth;
931 p.avatarCapsuleHeight = o[0].XavatarCapsuleHeight;
932 p.avatarContactProcessingThreshold = o[0].XavatarContactProcessingThreshold;
933
934 p.vehicleAngularDamping = o[0].XvehicleAngularDamping;
935
936 p.maxPersistantManifoldPoolSize = o[0].maxPersistantManifoldPoolSize;
937 p.maxCollisionAlgorithmPoolSize = o[0].maxCollisionAlgorithmPoolSize;
938 p.shouldDisableContactPoolDynamicAllocation = o[0].shouldDisableContactPoolDynamicAllocation;
939 p.shouldForceUpdateAllAabbs = o[0].shouldForceUpdateAllAabbs;
940 p.shouldRandomizeSolverOrder = o[0].shouldRandomizeSolverOrder;
941 p.shouldSplitSimulationIslands = o[0].shouldSplitSimulationIslands;
942 p.shouldEnableFrictionCaching = o[0].shouldEnableFrictionCaching;
943 p.numberOfSolverIterations = o[0].numberOfSolverIterations;
944
945 p.linksetImplementation = o[0].XlinksetImplementation;
946 p.linkConstraintUseFrameOffset = o[0].XlinkConstraintUseFrameOffset;
947 p.linkConstraintEnableTransMotor = o[0].XlinkConstraintEnableTransMotor;
948 p.linkConstraintTransMotorMaxVel = o[0].XlinkConstraintTransMotorMaxVel;
949 p.linkConstraintTransMotorMaxForce = o[0].XlinkConstraintTransMotorMaxForce;
950 p.linkConstraintERP = o[0].XlinkConstraintERP;
951 p.linkConstraintCFM = o[0].XlinkConstraintCFM;
952 p.linkConstraintSolverIterations = o[0].XlinkConstraintSolverIterations;
953 p.physicsLoggingFrames = o[0].physicsLoggingFrames;
954 DefaultCollisionConstructionInfo ccci = new DefaultCollisionConstructionInfo();
955
956 DefaultCollisionConfiguration cci = new DefaultCollisionConfiguration();
957 CollisionDispatcher m_dispatcher = new CollisionDispatcher(cci);
958
959
960 if (p.maxPersistantManifoldPoolSize > 0)
961 cci.m_persistentManifoldPoolSize = (int)p.maxPersistantManifoldPoolSize;
962 if (p.shouldDisableContactPoolDynamicAllocation !=0)
963 m_dispatcher.SetDispatcherFlags(DispatcherFlags.CD_DISABLE_CONTACTPOOL_DYNAMIC_ALLOCATION);
964 //if (p.maxCollisionAlgorithmPoolSize >0 )
965
966 DbvtBroadphase m_broadphase = new DbvtBroadphase();
967 //IndexedVector3 aabbMin = new IndexedVector3(0, 0, 0);
968 //IndexedVector3 aabbMax = new IndexedVector3(256, 256, 256);
969
970 //AxisSweep3Internal m_broadphase2 = new AxisSweep3Internal(ref aabbMin, ref aabbMax, Convert.ToInt32(0xfffe), 0xffff, ushort.MaxValue/2, null, true);
971 m_broadphase.GetOverlappingPairCache().SetInternalGhostPairCallback(new GhostPairCallback());
972
973 SequentialImpulseConstraintSolver m_solver = new SequentialImpulseConstraintSolver();
974
975 DiscreteDynamicsWorld world = new DiscreteDynamicsWorld(m_dispatcher, m_broadphase, m_solver, cci);
976 world.UpdatedObjects = updateArray;
977 world.UpdatedCollisions = collisionArray;
978 world.WorldSettings.Params = p;
979 world.SetForceUpdateAllAabbs(p.shouldForceUpdateAllAabbs != 0);
980 world.GetSolverInfo().m_solverMode = SolverMode.SOLVER_USE_WARMSTARTING | SolverMode.SOLVER_SIMD;
981 if (p.shouldRandomizeSolverOrder != 0)
982 world.GetSolverInfo().m_solverMode |= SolverMode.SOLVER_RANDMIZE_ORDER;
983
984 world.GetSimulationIslandManager().SetSplitIslands(p.shouldSplitSimulationIslands != 0);
985 //world.GetDispatchInfo().m_enableSatConvex Not implemented in C# port
986
987 if (p.shouldEnableFrictionCaching != 0)
988 world.GetSolverInfo().m_solverMode |= SolverMode.SOLVER_ENABLE_FRICTION_DIRECTION_CACHING;
989
990 if (p.numberOfSolverIterations > 0)
991 world.GetSolverInfo().m_numIterations = (int) p.numberOfSolverIterations;
992
993
994 world.GetSolverInfo().m_damping = world.WorldSettings.Params.linearDamping;
995 world.GetSolverInfo().m_restitution = world.WorldSettings.Params.defaultRestitution;
996 world.GetSolverInfo().m_globalCfm = 0.0f;
997 world.GetSolverInfo().m_tau = 0.6f;
998 world.GetSolverInfo().m_friction = 0.3f;
999 world.GetSolverInfo().m_maxErrorReduction = 20f;
1000 world.GetSolverInfo().m_numIterations = 10;
1001 world.GetSolverInfo().m_erp = 0.2f;
1002 world.GetSolverInfo().m_erp2 = 0.1f;
1003 world.GetSolverInfo().m_sor = 1.0f;
1004 world.GetSolverInfo().m_splitImpulse = false;
1005 world.GetSolverInfo().m_splitImpulsePenetrationThreshold = -0.02f;
1006 world.GetSolverInfo().m_linearSlop = 0.0f;
1007 world.GetSolverInfo().m_warmstartingFactor = 0.85f;
1008 world.GetSolverInfo().m_restingContactRestitutionThreshold = 2;
1009 world.SetForceUpdateAllAabbs(true);
1010
1011
1012 world.SetGravity(new IndexedVector3(0,0,p.gravity));
1013
1014 return world;
1015 }
1016 //m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL
1017 internal static bool SetConstraintParam2(object pConstraint, ConstraintParams paramIndex, float paramvalue, ConstraintParamAxis axis)
1018 {
1019 Generic6DofConstraint constrain = pConstraint as Generic6DofConstraint;
1020 if (axis == ConstraintParamAxis.AXIS_LINEAR_ALL || axis == ConstraintParamAxis.AXIS_ALL)
1021 {
1022 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams) (int) paramIndex, paramvalue, 0);
1023 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams) (int) paramIndex, paramvalue, 1);
1024 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams) (int) paramIndex, paramvalue, 2);
1025 }
1026 if (axis == ConstraintParamAxis.AXIS_ANGULAR_ALL || axis == ConstraintParamAxis.AXIS_ALL)
1027 {
1028 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, 3);
1029 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, 4);
1030 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, 5);
1031 }
1032 if (axis == ConstraintParamAxis.AXIS_LINEAR_ALL)
1033 {
1034 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, (int)axis);
1035 }
1036 return true;
1037 }
1038
1039 internal static bool PushUpdate2(object pCollisionObject)
1040 {
1041 bool ret = false;
1042 RigidBody rb = pCollisionObject as RigidBody;
1043 if (rb != null)
1044 {
1045 SimMotionState sms = rb.GetMotionState() as SimMotionState;
1046 if (sms != null)
1047 {
1048 IndexedMatrix wt = IndexedMatrix.Identity;
1049 sms.GetWorldTransform(out wt);
1050 sms.SetWorldTransform(ref wt, true);
1051 ret = true;
1052 }
1053 }
1054 return ret;
1055
1056 }
1057
1058 internal static bool IsCompound2(object pShape)
1059 {
1060 CollisionShape shape = pShape as CollisionShape;
1061 return shape.IsCompound();
1062 }
1063 internal static bool IsPloyhedral2(object pShape)
1064 {
1065 CollisionShape shape = pShape as CollisionShape;
1066 return shape.IsPolyhedral();
1067 }
1068 internal static bool IsConvex2d2(object pShape)
1069 {
1070 CollisionShape shape = pShape as CollisionShape;
1071 return shape.IsConvex2d();
1072 }
1073 internal static bool IsConvex2(object pShape)
1074 {
1075 CollisionShape shape = pShape as CollisionShape;
1076 return shape.IsConvex();
1077 }
1078 internal static bool IsNonMoving2(object pShape)
1079 {
1080 CollisionShape shape = pShape as CollisionShape;
1081 return shape.IsNonMoving();
1082 }
1083 internal static bool IsConcave2(object pShape)
1084 {
1085 CollisionShape shape = pShape as CollisionShape;
1086 return shape.IsConcave();
1087 }
1088 internal static bool IsInfinite2(object pShape)
1089 {
1090 CollisionShape shape = pShape as CollisionShape;
1091 return shape.IsInfinite();
1092 }
1093 internal static bool IsNativeShape2(object pShape)
1094 {
1095 CollisionShape shape = pShape as CollisionShape;
1096 bool ret;
1097 switch (shape.GetShapeType())
1098 {
1099 case BroadphaseNativeTypes.BOX_SHAPE_PROXYTYPE:
1100 case BroadphaseNativeTypes.CONE_SHAPE_PROXYTYPE:
1101 case BroadphaseNativeTypes.SPHERE_SHAPE_PROXYTYPE:
1102 case BroadphaseNativeTypes.CYLINDER_SHAPE_PROXYTYPE:
1103 ret = true;
1104 break;
1105 default:
1106 ret = false;
1107 break;
1108 }
1109 return ret;
1110 }
1111 //sim.ptr, shape.ptr,prim.LocalID, prim.RawPosition, prim.RawOrientation
1112 internal static object CreateGhostFromShape2(object pWorld, object pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation)
1113 {
1114 IndexedMatrix bodyTransform = new IndexedMatrix();
1115 bodyTransform._origin = new IndexedVector3(pRawPosition.X, pRawPosition.Y, pRawPosition.Z);
1116 bodyTransform.SetRotation(new IndexedQuaternion(pRawOrientation.X,pRawOrientation.Y,pRawOrientation.Z,pRawOrientation.W));
1117 GhostObject gObj = new PairCachingGhostObject();
1118 gObj.SetWorldTransform(bodyTransform);
1119 CollisionShape shape = pShape as CollisionShape;
1120 gObj.SetCollisionShape(shape);
1121 gObj.SetUserPointer(pLocalID);
1122 // TODO: Add to Special CollisionObjects!
1123 return gObj;
1124 }
1125
1126 public static void SetCollisionShape2(object pWorld, object pObj, object pShape)
1127 {
1128 var world = pWorld as DiscreteDynamicsWorld;
1129 var obj = pObj as CollisionObject;
1130 var shape = pShape as CollisionShape;
1131 obj.SetCollisionShape(shape);
1132
1133 }
1134 //(PhysicsScene.World.ptr, nativeShapeData)
1135 internal static object BuildNativeShape2(object pWorld, ShapeData pShapeData)
1136 {
1137 var world = pWorld as DiscreteDynamicsWorld;
1138 CollisionShape shape = null;
1139 switch (pShapeData.Type)
1140 {
1141 case BSPhysicsShapeType.SHAPE_BOX:
1142 shape = new BoxShape(new IndexedVector3(0.5f,0.5f,0.5f));
1143 break;
1144 case BSPhysicsShapeType.SHAPE_CONE:
1145 shape = new ConeShapeZ(0.5f, 1.0f);
1146 break;
1147 case BSPhysicsShapeType.SHAPE_CYLINDER:
1148 shape = new CylinderShapeZ(new IndexedVector3(0.5f, 0.5f, 0.5f));
1149 break;
1150 case BSPhysicsShapeType.SHAPE_SPHERE:
1151 shape = new SphereShape(0.5f);
1152 break;
1153
1154 }
1155 if (shape != null)
1156 {
1157 IndexedVector3 scaling = new IndexedVector3(pShapeData.Scale.X, pShapeData.Scale.Y, pShapeData.Scale.Z);
1158 shape.SetMargin(world.WorldSettings.Params.collisionMargin);
1159 shape.SetLocalScaling(ref scaling);
1160
1161 }
1162 return shape;
1163 }
1164 //PhysicsScene.World.ptr, false
1165 internal static object CreateCompoundShape2(object pWorld, bool enableDynamicAabbTree)
1166 {
1167 return new CompoundShape(enableDynamicAabbTree);
1168 }
1169
1170 internal static int GetNumberOfCompoundChildren2(object pCompoundShape)
1171 {
1172 var compoundshape = pCompoundShape as CompoundShape;
1173 return compoundshape.GetNumChildShapes();
1174 }
1175 //LinksetRoot.PhysShape.ptr, newShape.ptr, displacementPos, displacementRot
1176 internal static void AddChildShapeToCompoundShape2(object pCShape, object paddShape, Vector3 displacementPos, Quaternion displacementRot)
1177 {
1178 IndexedMatrix relativeTransform = new IndexedMatrix();
1179 var compoundshape = pCShape as CompoundShape;
1180 var addshape = paddShape as CollisionShape;
1181
1182 relativeTransform._origin = new IndexedVector3(displacementPos.X, displacementPos.Y, displacementPos.Z);
1183 relativeTransform.SetRotation(new IndexedQuaternion(displacementRot.X,displacementRot.Y,displacementRot.Z,displacementRot.W));
1184 compoundshape.AddChildShape(ref relativeTransform, addshape);
1185
1186 }
1187
1188 internal static object RemoveChildShapeFromCompoundShapeIndex2(object pCShape, int pii)
1189 {
1190 var compoundshape = pCShape as CompoundShape;
1191 CollisionShape ret = null;
1192 ret = compoundshape.GetChildShape(pii);
1193 compoundshape.RemoveChildShapeByIndex(pii);
1194 return ret;
1195 }
1196
1197 internal static object CreateGroundPlaneShape2(uint pLocalId, float pheight, float pcollisionMargin)
1198 {
1199 StaticPlaneShape m_planeshape = new StaticPlaneShape(new IndexedVector3(0,0,1),(int)pheight );
1200 m_planeshape.SetMargin(pcollisionMargin);
1201 m_planeshape.SetUserPointer(pLocalId);
1202 return m_planeshape;
1203 }
1204
1205 internal static object CreateHingeConstraint2(object pWorld, object pBody1, object ppBody2, Vector3 ppivotInA, Vector3 ppivotInB, Vector3 paxisInA, Vector3 paxisInB, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies)
1206 {
1207 HingeConstraint constrain = null;
1208 var rb1 = pBody1 as RigidBody;
1209 var rb2 = ppBody2 as RigidBody;
1210 if (rb1 != null && rb2 != null)
1211 {
1212 IndexedVector3 pivotInA = new IndexedVector3(ppivotInA.X, ppivotInA.Y, ppivotInA.Z);
1213 IndexedVector3 pivotInB = new IndexedVector3(ppivotInB.X, ppivotInB.Y, ppivotInB.Z);
1214 IndexedVector3 axisInA = new IndexedVector3(paxisInA.X, paxisInA.Y, paxisInA.Z);
1215 IndexedVector3 axisInB = new IndexedVector3(paxisInB.X, paxisInB.Y, paxisInB.Z);
1216 var world = pWorld as DiscreteDynamicsWorld;
1217 world.AddConstraint(constrain, pdisableCollisionsBetweenLinkedBodies);
1218 }
1219 return constrain;
1220 }
1221
1222 internal static bool ReleaseHeightMapInfo2(object pMapInfo)
1223 {
1224 if (pMapInfo != null)
1225 {
1226 BulletHeightMapInfo mapinfo = pMapInfo as BulletHeightMapInfo;
1227 if (mapinfo.heightMap != null)
1228 mapinfo.heightMap = null;
1229
1230
1231 }
1232 return true;
1233 }
1234
1235 internal static object CreateHullShape2(object pWorld, int pHullCount, float[] pConvHulls)
1236 {
1237 CompoundShape compoundshape = new CompoundShape(false);
1238 var world = pWorld as DiscreteDynamicsWorld;
1239
1240
1241 compoundshape.SetMargin(world.WorldSettings.Params.collisionMargin);
1242 int ii = 1;
1243
1244 for (int i = 0; i < pHullCount; i++)
1245 {
1246 int vertexCount = (int) pConvHulls[ii];
1247
1248 IndexedVector3 centroid = new IndexedVector3(pConvHulls[ii + 1], pConvHulls[ii + 2], pConvHulls[ii + 3]);
1249 IndexedMatrix childTrans = IndexedMatrix.Identity;
1250 childTrans._origin = centroid;
1251
1252 List<IndexedVector3> virts = new List<IndexedVector3>();
1253 int ender = ((ii + 4) + (vertexCount*3));
1254 for (int iii = ii + 4; iii < ender; iii+=3)
1255 {
1256
1257 virts.Add(new IndexedVector3(pConvHulls[iii], pConvHulls[iii + 1], pConvHulls[iii +2]));
1258 }
1259 ConvexHullShape convexShape = new ConvexHullShape(virts, vertexCount);
1260 convexShape.SetMargin(world.WorldSettings.Params.collisionMargin);
1261 compoundshape.AddChildShape(ref childTrans, convexShape);
1262 ii += (vertexCount*3 + 4);
1263 }
1264
1265
1266 return compoundshape;
1267 }
1268
1269 internal static object CreateMeshShape2(object pWorld, int pIndicesCount, int[] indices, int pVerticesCount, float[] verticesAsFloats)
1270 {
1271 //DumpRaw(indices,verticesAsFloats,pIndicesCount,pVerticesCount);
1272
1273 for (int iter = 0; iter < pVerticesCount; iter++)
1274 {
1275 if (verticesAsFloats[iter] > 0 && verticesAsFloats[iter] < 0.0001) verticesAsFloats[iter] = 0;
1276 if (verticesAsFloats[iter] < 0 && verticesAsFloats[iter] > -0.0001) verticesAsFloats[iter] = 0;
1277 }
1278
1279 ObjectArray<int> indicesarr = new ObjectArray<int>(indices);
1280 ObjectArray<float> vertices = new ObjectArray<float>(verticesAsFloats);
1281 DumpRaw(indicesarr,vertices,pIndicesCount,pVerticesCount);
1282 var world = pWorld as DiscreteDynamicsWorld;
1283 IndexedMesh mesh = new IndexedMesh();
1284 mesh.m_indexType = PHY_ScalarType.PHY_INTEGER;
1285 mesh.m_numTriangles = pIndicesCount/3;
1286 mesh.m_numVertices = pVerticesCount;
1287 mesh.m_triangleIndexBase = indicesarr;
1288 mesh.m_vertexBase = vertices;
1289 mesh.m_vertexStride = 3;
1290 mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT;
1291 mesh.m_triangleIndexStride = 3;
1292
1293 TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray();
1294 tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER);
1295 BvhTriangleMeshShape meshShape = new BvhTriangleMeshShape(tribuilder, true,true);
1296 meshShape.SetMargin(world.WorldSettings.Params.collisionMargin);
1297 // world.UpdateSingleAabb(meshShape);
1298 return meshShape;
1299
1300 }
1301 public static void DumpRaw(ObjectArray<int>indices, ObjectArray<float> vertices, int pIndicesCount,int pVerticesCount )
1302 {
1303
1304 String fileName = "objTest3.raw";
1305 String completePath = System.IO.Path.Combine(Util.configDir(), fileName);
1306 StreamWriter sw = new StreamWriter(completePath);
1307 IndexedMesh mesh = new IndexedMesh();
1308
1309 mesh.m_indexType = PHY_ScalarType.PHY_INTEGER;
1310 mesh.m_numTriangles = pIndicesCount / 3;
1311 mesh.m_numVertices = pVerticesCount;
1312 mesh.m_triangleIndexBase = indices;
1313 mesh.m_vertexBase = vertices;
1314 mesh.m_vertexStride = 3;
1315 mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT;
1316 mesh.m_triangleIndexStride = 3;
1317
1318 TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray();
1319 tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER);
1320
1321
1322
1323 for (int i = 0; i < pVerticesCount; i++)
1324 {
1325
1326 string s = vertices[indices[i * 3]].ToString("0.0000");
1327 s += " " + vertices[indices[i * 3 + 1]].ToString("0.0000");
1328 s += " " + vertices[indices[i * 3 + 2]].ToString("0.0000");
1329
1330 sw.Write(s + "\n");
1331 }
1332
1333 sw.Close();
1334 }
1335 public static void DumpRaw(int[] indices, float[] vertices, int pIndicesCount, int pVerticesCount)
1336 {
1337
1338 String fileName = "objTest6.raw";
1339 String completePath = System.IO.Path.Combine(Util.configDir(), fileName);
1340 StreamWriter sw = new StreamWriter(completePath);
1341 IndexedMesh mesh = new IndexedMesh();
1342
1343 mesh.m_indexType = PHY_ScalarType.PHY_INTEGER;
1344 mesh.m_numTriangles = pIndicesCount / 3;
1345 mesh.m_numVertices = pVerticesCount;
1346 mesh.m_triangleIndexBase = indices;
1347 mesh.m_vertexBase = vertices;
1348 mesh.m_vertexStride = 3;
1349 mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT;
1350 mesh.m_triangleIndexStride = 3;
1351
1352 TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray();
1353 tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER);
1354
1355
1356 sw.WriteLine("Indices");
1357 sw.WriteLine(string.Format("int[] indices = new int[{0}];",pIndicesCount));
1358 for (int iter = 0; iter < indices.Length; iter++)
1359 {
1360 sw.WriteLine(string.Format("indices[{0}]={1};",iter,indices[iter]));
1361 }
1362 sw.WriteLine("VerticesFloats");
1363 sw.WriteLine(string.Format("float[] vertices = new float[{0}];", pVerticesCount));
1364 for (int iter = 0; iter < vertices.Length; iter++)
1365 {
1366 sw.WriteLine(string.Format("Vertices[{0}]={1};", iter, vertices[iter].ToString("0.0000")));
1367 }
1368
1369 // for (int i = 0; i < pVerticesCount; i++)
1370 // {
1371 //
1372 // string s = vertices[indices[i * 3]].ToString("0.0000");
1373 // s += " " + vertices[indices[i * 3 + 1]].ToString("0.0000");
1374 // s += " " + vertices[indices[i * 3 + 2]].ToString("0.0000");
1375 //
1376 // sw.Write(s + "\n");
1377 //}
1378
1379 sw.Close();
1380 }
1381 //PhysicsScene.World.ptr, m_mapInfo.ID, m_mapInfo.minCoords, m_mapInfo.maxCoords, m_mapInfo.heightMap, PhysicsScene.Params.terrainCollisionMargin
1382 internal static object CreateHeightMapInfo2(object pWorld, uint pId, Vector3 pminCoords, Vector3 pmaxCoords, float[] pheightMap, float pCollisionMargin)
1383 {
1384 BulletHeightMapInfo mapInfo = new BulletHeightMapInfo(pId, pheightMap, null);
1385 mapInfo.heightMap = null;
1386 mapInfo.minCoords = pminCoords;
1387 mapInfo.maxCoords = pmaxCoords;
1388 mapInfo.sizeX = (int) (pmaxCoords.X - pminCoords.X);
1389 mapInfo.sizeY = (int) (pmaxCoords.Y - pminCoords.Y);
1390 mapInfo.ID = pId;
1391 mapInfo.minZ = pminCoords.Z;
1392 mapInfo.maxZ = pmaxCoords.Z;
1393 mapInfo.collisionMargin = pCollisionMargin;
1394 if (mapInfo.minZ == mapInfo.maxZ)
1395 mapInfo.minZ -= 0.2f;
1396 mapInfo.heightMap = pheightMap;
1397
1398 return mapInfo;
1399
1400 }
1401
1402 internal static object CreateTerrainShape2(object pMapInfo)
1403 {
1404 BulletHeightMapInfo mapinfo = pMapInfo as BulletHeightMapInfo;
1405 const int upAxis = 2;
1406 const float scaleFactor = 1.0f;
1407 HeightfieldTerrainShape terrainShape = new HeightfieldTerrainShape((int)mapinfo.sizeX, (int)mapinfo.sizeY,
1408 mapinfo.heightMap, scaleFactor,
1409 mapinfo.minZ, mapinfo.maxZ, upAxis,
1410 false);
1411 terrainShape.SetMargin(mapinfo.collisionMargin + 0.5f);
1412 terrainShape.SetUseDiamondSubdivision(true);
1413 terrainShape.SetUserPointer(mapinfo.ID);
1414 return terrainShape;
1415 }
1416
1417 internal static bool TranslationalLimitMotor2(object pConstraint, float ponOff, float targetVelocity, float maxMotorForce)
1418 {
1419 TypedConstraint tconstrain = pConstraint as TypedConstraint;
1420 bool onOff = ponOff != 0;
1421 bool ret = false;
1422
1423 switch (tconstrain.GetConstraintType())
1424 {
1425 case TypedConstraintType.D6_CONSTRAINT_TYPE:
1426 Generic6DofConstraint constrain = pConstraint as Generic6DofConstraint;
1427 constrain.GetTranslationalLimitMotor().m_enableMotor[0] = onOff;
1428 constrain.GetTranslationalLimitMotor().m_targetVelocity[0] = targetVelocity;
1429 constrain.GetTranslationalLimitMotor().m_maxMotorForce[0] = maxMotorForce;
1430 ret = true;
1431 break;
1432 }
1433
1434
1435 return ret;
1436
1437 }
1438
1439 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)
1440 {
1441 int epic = PhysicsStepint2(pWorld, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out updatedEntities,
1442 out collidersCount, out colliders);
1443 return epic;
1444 }
1445
1446 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)
1447 {
1448 int numSimSteps = 0;
1449
1450
1451 //if (updatedEntities is null)
1452 // updatedEntities = new List<BulletXNA.EntityProperties>();
1453
1454 //if (colliders is null)
1455 // colliders = new List<BulletXNA.CollisionDesc>();
1456
1457
1458 if (pWorld is DiscreteDynamicsWorld)
1459 {
1460 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
1461
1462 numSimSteps = world.StepSimulation(timeStep, m_maxSubSteps, m_fixedTimeStep);
1463 int updates = 0;
1464
1465 updatedEntityCount = world.UpdatedObjects.Count;
1466 updatedEntities = new List<BulletXNA.EntityProperties>(world.UpdatedObjects);
1467 updatedEntityCount = updatedEntities.Count;
1468 world.UpdatedObjects.Clear();
1469
1470
1471 collidersCount = world.UpdatedCollisions.Count;
1472 colliders = new List<BulletXNA.CollisionDesc>(world.UpdatedCollisions);
1473
1474 world.UpdatedCollisions.Clear();
1475 m_collisionsThisFrame = 0;
1476 int numManifolds = world.GetDispatcher().GetNumManifolds();
1477 for (int j = 0; j < numManifolds; j++)
1478 {
1479 PersistentManifold contactManifold = world.GetDispatcher().GetManifoldByIndexInternal(j);
1480 int numContacts = contactManifold.GetNumContacts();
1481 if (numContacts == 0)
1482 continue;
1483
1484 CollisionObject objA = contactManifold.GetBody0() as CollisionObject;
1485 CollisionObject objB = contactManifold.GetBody1() as CollisionObject;
1486
1487 ManifoldPoint manifoldPoint = contactManifold.GetContactPoint(0);
1488 IndexedVector3 contactPoint = manifoldPoint.GetPositionWorldOnB();
1489 IndexedVector3 contactNormal = -manifoldPoint.m_normalWorldOnB; // make relative to A
1490
1491 RecordCollision(world, objA, objB, contactPoint, contactNormal);
1492 m_collisionsThisFrame ++;
1493 if (m_collisionsThisFrame >= 9999999)
1494 break;
1495
1496
1497 }
1498
1499
1500 }
1501 else
1502 {
1503 //if (updatedEntities is null)
1504 updatedEntities = new List<BulletXNA.EntityProperties>();
1505 updatedEntityCount = 0;
1506 //if (colliders is null)
1507 colliders = new List<BulletXNA.CollisionDesc>();
1508 collidersCount = 0;
1509 }
1510 return numSimSteps;
1511 }
1512
1513 private static void RecordCollision(CollisionWorld world,CollisionObject objA, CollisionObject objB, IndexedVector3 contact, IndexedVector3 norm)
1514 {
1515
1516 IndexedVector3 contactNormal = norm;
1517 if ((objA.GetCollisionFlags() & BulletXNA.BulletCollision.CollisionFlags.BS_WANTS_COLLISIONS) == 0 &&
1518 (objB.GetCollisionFlags() & BulletXNA.BulletCollision.CollisionFlags.BS_WANTS_COLLISIONS) == 0)
1519 {
1520 return;
1521 }
1522 uint idA = (uint)objA.GetUserPointer();
1523 uint idB = (uint)objB.GetUserPointer();
1524 if (idA > idB)
1525 {
1526 uint temp = idA;
1527 idA = idB;
1528 idB = temp;
1529 contactNormal = -contactNormal;
1530 }
1531
1532 ulong collisionID = ((ulong) idA << 32) | idB;
1533
1534 BulletXNA.CollisionDesc cDesc = new BulletXNA.CollisionDesc()
1535 {
1536 aID = idA,
1537 bID = idB,
1538 point = contact,
1539 normal = contactNormal
1540 };
1541 world.UpdatedCollisions.Add(cDesc);
1542 m_collisionsThisFrame++;
1543
1544
1545 }
1546 private static EntityProperties GetDebugProperties(object pWorld, object pBody)
1547 {
1548 EntityProperties ent = new EntityProperties();
1549 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
1550 RigidBody body = pBody as RigidBody;
1551 IndexedMatrix transform = body.GetWorldTransform();
1552 IndexedVector3 LinearVelocity = body.GetInterpolationLinearVelocity();
1553 IndexedVector3 AngularVelocity = body.GetInterpolationAngularVelocity();
1554 IndexedQuaternion rotation = transform.GetRotation();
1555 ent.Acceleration = Vector3.Zero;
1556 ent.ID = (uint)body.GetUserPointer();
1557 ent.Position = new Vector3(transform._origin.X,transform._origin.Y,transform._origin.Z);
1558 ent.Rotation = new Quaternion(rotation.X,rotation.Y,rotation.Z,rotation.W);
1559 ent.Velocity = new Vector3(LinearVelocity.X, LinearVelocity.Y, LinearVelocity.Z);
1560 ent.RotationalVelocity = new Vector3(AngularVelocity.X, AngularVelocity.Y, AngularVelocity.Z);
1561 return ent;
1562
1563
1564 }
1565
1566
1567 internal static Vector3 GetLocalScaling2(object pBody)
1568 {
1569 CollisionShape shape = pBody as CollisionShape;
1570 IndexedVector3 scale = shape.GetLocalScaling();
1571 return new Vector3(scale.X,scale.Y,scale.Z);
1572 }
1573
1574 internal static bool RayCastGround(object pWorld, Vector3 _RayOrigin, float pRayHeight, object NotMe)
1575 {
1576 DynamicsWorld world = pWorld as DynamicsWorld;
1577 if (world != null)
1578 {
1579 if (NotMe is CollisionObject || NotMe is RigidBody)
1580 {
1581 CollisionObject AvoidBody = NotMe as CollisionObject;
1582
1583 IndexedVector3 rOrigin = new IndexedVector3(_RayOrigin.X, _RayOrigin.Y, _RayOrigin.Z);
1584 IndexedVector3 rEnd = new IndexedVector3(_RayOrigin.X, _RayOrigin.Y, _RayOrigin.Z - pRayHeight);
1585 using (
1586 ClosestNotMeRayResultCallback rayCallback = new ClosestNotMeRayResultCallback(rOrigin,
1587 rEnd, AvoidBody)
1588 )
1589 {
1590 world.RayTest(ref rOrigin, ref rEnd, rayCallback);
1591 if (rayCallback.HasHit())
1592 {
1593 IndexedVector3 hitLocation = rayCallback.m_hitPointWorld;
1594
1595 }
1596 return rayCallback.HasHit();
1597 }
1598 }
1599 }
1600 return false;
1601 }
1602}
1603}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BulletSimData.cs b/OpenSim/Region/Physics/BulletSNPlugin/BulletSimData.cs
new file mode 100644
index 0000000..f509dc4
--- /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 BulletWorld
39{
40 public BulletWorld(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}