aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorteravus2012-12-23 15:21:25 -0500
committerteravus2012-12-23 15:21:25 -0500
commit92e4f9f412046f8f7926c99c9e56c3a8b6b2edbf (patch)
treeeabcbb758a7512222e84cb51b51b6822cdbf561b
parentRevert "Whitespace change to trigger bot" (diff)
downloadopensim-SC-92e4f9f412046f8f7926c99c9e56c3a8b6b2edbf.zip
opensim-SC-92e4f9f412046f8f7926c99c9e56c3a8b6b2edbf.tar.gz
opensim-SC-92e4f9f412046f8f7926c99c9e56c3a8b6b2edbf.tar.bz2
opensim-SC-92e4f9f412046f8f7926c99c9e56c3a8b6b2edbf.tar.xz
* Initial commit of BulletSimN (BulletSNPlugin). Purely C# implementation of BulletSim. This is designed to be /as close as possible/ to the BulletSim plugin while still being entirely in the managed space to make keeping it up to date easy as possible (no thinking work). This implementation is /slower/ then the c++ version just because it's fully managed, so it's not appropriate for huge sims, but it will run small ones OK. At the moment, it supports all known features of BulletSim. Think of it like.. POS but everything works. To use this plugin, set the physics plugin to BulletSimN.
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSCharacter.cs814
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSConstraint.cs135
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSConstraint6Dof.cs153
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSConstraintCollection.cs180
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSConstraintHinge.cs57
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSDynamics.cs1374
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSLinkset.cs333
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs396
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSLinksetConstraints.cs314
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSMaterials.cs200
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSMotors.cs347
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSParam.cs559
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSPhysObject.cs345
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSPlugin.cs81
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs1467
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSScene.cs954
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSShapeCollection.cs1015
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSShapes.cs208
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSTerrainHeightmap.cs175
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSTerrainManager.cs460
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BSTerrainMesh.cs267
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BulletSimAPI.cs1604
-rw-r--r--OpenSim/Region/Physics/BulletSNPlugin/BulletSimData.cs280
-rw-r--r--bin/BulletXNA.dllbin0 -> 614400 bytes
-rw-r--r--bin/BulletXNA.pdbbin0 -> 1875456 bytes
-rw-r--r--prebuild.xml34
26 files changed, 11752 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSCharacter.cs
new file mode 100644
index 0000000..4c4e950
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSCharacter.cs
@@ -0,0 +1,814 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Reflection;
30using log4net;
31using OMV = OpenMetaverse;
32using OpenSim.Framework;
33using OpenSim.Region.Physics.Manager;
34
35namespace OpenSim.Region.Physics.BulletSNPlugin
36{
37public sealed class BSCharacter : BSPhysObject
38{
39 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
40 private static readonly string LogHeader = "[BULLETS CHAR]";
41
42 // private bool _stopped;
43 private OMV.Vector3 _size;
44 private bool _grabbed;
45 private bool _selected;
46 private OMV.Vector3 _position;
47 private float _mass;
48 private float _avatarDensity;
49 private float _avatarVolume;
50 private OMV.Vector3 _force;
51 private OMV.Vector3 _velocity;
52 private OMV.Vector3 _torque;
53 private float _collisionScore;
54 private OMV.Vector3 _acceleration;
55 private OMV.Quaternion _orientation;
56 private int _physicsActorType;
57 private bool _isPhysical;
58 private bool _flying;
59 private bool _setAlwaysRun;
60 private bool _throttleUpdates;
61 private bool _isColliding;
62 private bool _collidingObj;
63 private bool _floatOnWater;
64 private OMV.Vector3 _rotationalVelocity;
65 private bool _kinematic;
66 private float _buoyancy;
67
68 // The friction and velocity of the avatar is modified depending on whether walking or not.
69 private OMV.Vector3 _appliedVelocity; // the last velocity applied to the avatar
70 private float _currentFriction; // the friction currently being used (changed by setVelocity).
71
72 private BSVMotor _velocityMotor;
73
74 private OMV.Vector3 _PIDTarget;
75 private bool _usePID;
76 private float _PIDTau;
77 private bool _useHoverPID;
78 private float _PIDHoverHeight;
79 private PIDHoverType _PIDHoverType;
80 private float _PIDHoverTao;
81
82 public BSCharacter(uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, bool isFlying)
83 : base(parent_scene, localID, avName, "BSCharacter")
84 {
85 _physicsActorType = (int)ActorTypes.Agent;
86 _position = pos;
87
88 // Old versions of ScenePresence passed only the height. If width and/or depth are zero,
89 // replace with the default values.
90 _size = size;
91 if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth;
92 if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth;
93
94 // A motor to control the acceleration and deceleration of the avatar movement.
95 // _velocityMotor = new BSVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f);
96 // _velocityMotor = new BSPIDVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f);
97 // Infinite decay and timescale values so motor only changes current to target values.
98 _velocityMotor = new BSVMotor("BSCharacter.Velocity",
99 0.2f, // time scale
100 BSMotor.Infinite, // decay time scale
101 BSMotor.InfiniteVector, // friction timescale
102 1f // efficiency
103 );
104 _velocityMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages.
105
106 _flying = isFlying;
107 _orientation = OMV.Quaternion.Identity;
108 _velocity = OMV.Vector3.Zero;
109 _appliedVelocity = OMV.Vector3.Zero;
110 _buoyancy = ComputeBuoyancyFromFlying(isFlying);
111 _currentFriction = BSParam.AvatarStandingFriction;
112 _avatarDensity = BSParam.AvatarDensity;
113
114 // The dimensions of the avatar capsule are kept in the scale.
115 // Physics creates a unit capsule which is scaled by the physics engine.
116 ComputeAvatarScale(_size);
117 // set _avatarVolume and _mass based on capsule size, _density and Scale
118 ComputeAvatarVolumeAndMass();
119 DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}",
120 LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
121
122 // do actual creation in taint time
123 PhysicsScene.TaintedObject("BSCharacter.create", delegate()
124 {
125 DetailLog("{0},BSCharacter.create,taint", LocalID);
126 // New body and shape into PhysBody and PhysShape
127 PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this);
128
129 SetPhysicalProperties();
130 });
131 return;
132 }
133
134 // called when this character is being destroyed and the resources should be released
135 public override void Destroy()
136 {
137 base.Destroy();
138
139 DetailLog("{0},BSCharacter.Destroy", LocalID);
140 PhysicsScene.TaintedObject("BSCharacter.destroy", delegate()
141 {
142 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
143 PhysBody.Clear();
144 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
145 PhysShape.Clear();
146 });
147 }
148
149 private void SetPhysicalProperties()
150 {
151 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
152
153 ZeroMotion(true);
154 ForcePosition = _position;
155 // Set the velocity and compute the proper friction
156 ForceVelocity = _velocity;
157 // Setting the current and target in the motor will cause it to start computing any deceleration.
158 _velocityMotor.Reset();
159 _velocityMotor.SetCurrent(_velocity);
160 _velocityMotor.SetTarget(_velocity);
161 _velocityMotor.Enabled = false;
162
163 // This will enable or disable the flying buoyancy of the avatar.
164 // Needs to be reset especially when an avatar is recreated after crossing a region boundry.
165 Flying = _flying;
166
167 BulletSimAPI.SetRestitution2(PhysBody.ptr, BSParam.AvatarRestitution);
168 BulletSimAPI.SetMargin2(PhysShape.ptr, PhysicsScene.Params.collisionMargin);
169 BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
170 BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, BSParam.ContactProcessingThreshold);
171 if (BSParam.CcdMotionThreshold > 0f)
172 {
173 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold);
174 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius);
175 }
176
177 UpdatePhysicalMassProperties(RawMass);
178
179 // Make so capsule does not fall over
180 BulletSimAPI.SetAngularFactorV2(PhysBody.ptr, OMV.Vector3.Zero);
181
182 BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_CHARACTER_OBJECT);
183
184 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr, _position, _orientation);
185
186 // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG);
187 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_DEACTIVATION);
188 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
189
190 // Do this after the object has been added to the world
191 PhysBody.collisionType = CollisionType.Avatar;
192 PhysBody.ApplyCollisionMask();
193 }
194
195 public override void RequestPhysicsterseUpdate()
196 {
197 base.RequestPhysicsterseUpdate();
198 }
199 // No one calls this method so I don't know what it could possibly mean
200 public override bool Stopped { get { return false; } }
201
202 public override OMV.Vector3 Size {
203 get
204 {
205 // Avatar capsule size is kept in the scale parameter.
206 return _size;
207 }
208
209 set {
210 // When an avatar's size is set, only the height is changed.
211 _size = value;
212 // Old versions of ScenePresence passed only the height. If width and/or depth are zero,
213 // replace with the default values.
214 if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth;
215 if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth;
216
217 ComputeAvatarScale(_size);
218 ComputeAvatarVolumeAndMass();
219 DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}",
220 LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
221
222 PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
223 {
224 if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape)
225 {
226 BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
227 UpdatePhysicalMassProperties(RawMass);
228 // Make sure this change appears as a property update event
229 BulletSimAPI.PushUpdate2(PhysBody.ptr);
230 }
231 });
232
233 }
234 }
235
236 public override PrimitiveBaseShape Shape
237 {
238 set { BaseShape = value; }
239 }
240 // I want the physics engine to make an avatar capsule
241 public override BSPhysicsShapeType PreferredPhysicalShape
242 {
243 get {return BSPhysicsShapeType.SHAPE_CAPSULE; }
244 }
245
246 public override bool Grabbed {
247 set { _grabbed = value; }
248 }
249 public override bool Selected {
250 set { _selected = value; }
251 }
252 public override void CrossingFailure() { return; }
253 public override void link(PhysicsActor obj) { return; }
254 public override void delink() { return; }
255
256 // Set motion values to zero.
257 // Do it to the properties so the values get set in the physics engine.
258 // Push the setting of the values to the viewer.
259 // Called at taint time!
260 public override void ZeroMotion(bool inTaintTime)
261 {
262 _velocity = OMV.Vector3.Zero;
263 _velocityMotor.Zero();
264 _acceleration = OMV.Vector3.Zero;
265 _rotationalVelocity = OMV.Vector3.Zero;
266
267 // Zero some other properties directly into the physics engine
268 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
269 {
270 if (PhysBody.HasPhysicalBody)
271 BulletSimAPI.ClearAllForces2(PhysBody.ptr);
272 });
273 }
274 public override void ZeroAngularMotion(bool inTaintTime)
275 {
276 _rotationalVelocity = OMV.Vector3.Zero;
277
278 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
279 {
280 if (PhysBody.HasPhysicalBody)
281 {
282 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
283 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
284 // The next also get rid of applied linear force but the linear velocity is untouched.
285 BulletSimAPI.ClearForces2(PhysBody.ptr);
286 }
287 });
288 }
289
290
291 public override void LockAngularMotion(OMV.Vector3 axis) { return; }
292
293 public override OMV.Vector3 RawPosition
294 {
295 get { return _position; }
296 set { _position = value; }
297 }
298 public override OMV.Vector3 Position {
299 get {
300 // Don't refetch the position because this function is called a zillion times
301 // _position = BulletSimAPI.GetObjectPosition2(Scene.World.ptr, LocalID);
302 return _position;
303 }
304 set {
305 _position = value;
306 PositionSanityCheck();
307
308 PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
309 {
310 DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
311 if (PhysBody.HasPhysicalBody)
312 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
313 });
314 }
315 }
316 public override OMV.Vector3 ForcePosition {
317 get {
318 _position = BulletSimAPI.GetPosition2(PhysBody.ptr);
319 return _position;
320 }
321 set {
322 _position = value;
323 PositionSanityCheck();
324 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
325 }
326 }
327
328
329 // Check that the current position is sane and, if not, modify the position to make it so.
330 // Check for being below terrain or on water.
331 // Returns 'true' of the position was made sane by some action.
332 private bool PositionSanityCheck()
333 {
334 bool ret = false;
335
336 // TODO: check for out of bounds
337 if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position))
338 {
339 // The character is out of the known/simulated area.
340 // Upper levels of code will handle the transition to other areas so, for
341 // the time, we just ignore the position.
342 return ret;
343 }
344
345 // If below the ground, move the avatar up
346 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
347 if (Position.Z < terrainHeight)
348 {
349 DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
350 _position.Z = terrainHeight + 2.0f;
351 ret = true;
352 }
353 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
354 {
355 float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
356 if (Position.Z < waterHeight)
357 {
358 _position.Z = waterHeight;
359 ret = true;
360 }
361 }
362
363 return ret;
364 }
365
366 // A version of the sanity check that also makes sure a new position value is
367 // pushed back to the physics engine. This routine would be used by anyone
368 // who is not already pushing the value.
369 private bool PositionSanityCheck(bool inTaintTime)
370 {
371 bool ret = false;
372 if (PositionSanityCheck())
373 {
374 // The new position value must be pushed into the physics engine but we can't
375 // just assign to "Position" because of potential call loops.
376 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate()
377 {
378 DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
379 if (PhysBody.HasPhysicalBody)
380 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
381 });
382 ret = true;
383 }
384 return ret;
385 }
386
387 public override float Mass { get { return _mass; } }
388
389 // used when we only want this prim's mass and not the linkset thing
390 public override float RawMass {
391 get {return _mass; }
392 }
393 public override void UpdatePhysicalMassProperties(float physMass)
394 {
395 OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass);
396 BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, localInertia);
397 }
398
399 public override OMV.Vector3 Force {
400 get { return _force; }
401 set {
402 _force = value;
403 // m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force);
404 PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
405 {
406 DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
407 if (PhysBody.HasPhysicalBody)
408 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
409 });
410 }
411 }
412
413 public bool TouchingGround()
414 {
415 bool ret = BulletSimAPI.RayCastGround(PhysicsScene.World.ptr,_position,_size.Z * 0.55f, PhysBody.ptr);
416 return ret;
417 }
418 // Avatars don't do vehicles
419 public override int VehicleType { get { return (int)Vehicle.TYPE_NONE; } set { return; } }
420 public override void VehicleFloatParam(int param, float value) { }
421 public override void VehicleVectorParam(int param, OMV.Vector3 value) {}
422 public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { }
423 public override void VehicleFlags(int param, bool remove) { }
424
425 // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more
426 public override void SetVolumeDetect(int param) { return; }
427
428 public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } }
429 public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } }
430
431 // Sets the target in the motor. This starts the changing of the avatar's velocity.
432 public override OMV.Vector3 TargetVelocity
433 {
434 get
435 {
436 return _velocityMotor.TargetValue;
437 }
438 set
439 {
440 DetailLog("{0},BSCharacter.setTargetVelocity,call,vel={1}", LocalID, value);
441
442 if (!_flying)
443 if ((value.Z >= 0.0001f) || (value.Z <= -0.0001f) || _velocity.Z < -0.0001f)
444 if (!TouchingGround())
445 value.Z = _velocity.Z;
446 if (_setAlwaysRun)
447 value *= 1.3f;
448
449 OMV.Vector3 targetVel = value;
450
451 PhysicsScene.TaintedObject("BSCharacter.setTargetVelocity", delegate()
452 {
453
454 _velocityMotor.Reset();
455 _velocityMotor.SetTarget(targetVel);
456 _velocityMotor.SetCurrent(_velocity);
457 _velocityMotor.Enabled = true;
458
459 // Make sure a property update happens next step so the motor gets incorporated.
460 BulletSimAPI.PushUpdate2(PhysBody.ptr);
461 });
462 }
463 }
464 // Directly setting velocity means this is what the user really wants now.
465 public override OMV.Vector3 Velocity {
466 get { return _velocity; }
467 set {
468 _velocity = value;
469 // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity);
470 PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate()
471 {
472 _velocityMotor.Reset();
473 _velocityMotor.SetCurrent(_velocity);
474 _velocityMotor.SetTarget(_velocity);
475 // Even though the motor is initialized, it's not used and the velocity goes straight into the avatar.
476 _velocityMotor.Enabled = false;
477
478 DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity);
479 ForceVelocity = _velocity;
480 });
481 }
482 }
483 public override OMV.Vector3 ForceVelocity {
484 get { return _velocity; }
485 set {
486 PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity");
487
488 _velocity = value;
489 // Depending on whether the avatar is moving or not, change the friction
490 // to keep the avatar from slipping around
491 if (_velocity.Length() == 0)
492 {
493 if (_currentFriction != BSParam.AvatarStandingFriction)
494 {
495 _currentFriction = BSParam.AvatarStandingFriction;
496 if (PhysBody.HasPhysicalBody)
497 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
498 }
499 }
500 else
501 {
502 if (_currentFriction != BSParam.AvatarFriction)
503 {
504 _currentFriction = BSParam.AvatarFriction;
505 if (PhysBody.HasPhysicalBody)
506 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
507 }
508 }
509 // Remember the set velocity so we can suppress the reduction by friction, ...
510 _appliedVelocity = value;
511
512 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
513 BulletSimAPI.Activate2(PhysBody.ptr, true);
514 }
515 }
516 public override OMV.Vector3 Torque {
517 get { return _torque; }
518 set { _torque = value;
519 }
520 }
521 public override float CollisionScore {
522 get { return _collisionScore; }
523 set { _collisionScore = value;
524 }
525 }
526 public override OMV.Vector3 Acceleration {
527 get { return _acceleration; }
528 set { _acceleration = value; }
529 }
530 public override OMV.Quaternion RawOrientation
531 {
532 get { return _orientation; }
533 set { _orientation = value; }
534 }
535 public override OMV.Quaternion Orientation {
536 get { return _orientation; }
537 set {
538 _orientation = value;
539 // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation);
540 PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
541 {
542 if (PhysBody.HasPhysicalBody)
543 {
544 // _position = BulletSimAPI.GetPosition2(BSBody.ptr);
545 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
546 }
547 });
548 }
549 }
550 // Go directly to Bullet to get/set the value.
551 public override OMV.Quaternion ForceOrientation
552 {
553 get
554 {
555 _orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr);
556 return _orientation;
557 }
558 set
559 {
560 _orientation = value;
561 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
562 }
563 }
564 public override int PhysicsActorType {
565 get { return _physicsActorType; }
566 set { _physicsActorType = value;
567 }
568 }
569 public override bool IsPhysical {
570 get { return _isPhysical; }
571 set { _isPhysical = value;
572 }
573 }
574 public override bool IsSolid {
575 get { return true; }
576 }
577 public override bool IsStatic {
578 get { return false; }
579 }
580 public override bool Flying {
581 get { return _flying; }
582 set {
583 _flying = value;
584
585 // simulate flying by changing the effect of gravity
586 Buoyancy = ComputeBuoyancyFromFlying(_flying);
587 }
588 }
589 // Flying is implimented by changing the avatar's buoyancy.
590 // Would this be done better with a vehicle type?
591 private float ComputeBuoyancyFromFlying(bool ifFlying) {
592 return ifFlying ? 1f : 0f;
593 }
594 public override bool
595 SetAlwaysRun {
596 get { return _setAlwaysRun; }
597 set { _setAlwaysRun = value; }
598 }
599 public override bool ThrottleUpdates {
600 get { return _throttleUpdates; }
601 set { _throttleUpdates = value; }
602 }
603 public override bool IsColliding {
604 get { return (CollidingStep == PhysicsScene.SimulationStep); }
605 set { _isColliding = value; }
606 }
607 public override bool CollidingGround {
608 get { return (CollidingGroundStep == PhysicsScene.SimulationStep); }
609 set { CollidingGround = value; }
610 }
611 public override bool CollidingObj {
612 get { return _collidingObj; }
613 set { _collidingObj = value; }
614 }
615 public override bool FloatOnWater {
616 set {
617 _floatOnWater = value;
618 PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate()
619 {
620 if (PhysBody.HasPhysicalBody)
621 {
622 if (_floatOnWater)
623 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
624 else
625 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
626 }
627 });
628 }
629 }
630 public override OMV.Vector3 RotationalVelocity {
631 get { return _rotationalVelocity; }
632 set { _rotationalVelocity = value; }
633 }
634 public override OMV.Vector3 ForceRotationalVelocity {
635 get { return _rotationalVelocity; }
636 set { _rotationalVelocity = value; }
637 }
638 public override bool Kinematic {
639 get { return _kinematic; }
640 set { _kinematic = value; }
641 }
642 // neg=fall quickly, 0=1g, 1=0g, pos=float up
643 public override float Buoyancy {
644 get { return _buoyancy; }
645 set { _buoyancy = value;
646 PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate()
647 {
648 DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
649 ForceBuoyancy = _buoyancy;
650 });
651 }
652 }
653 public override float ForceBuoyancy {
654 get { return _buoyancy; }
655 set {
656 PhysicsScene.AssertInTaintTime("BSCharacter.ForceBuoyancy");
657
658 _buoyancy = value;
659 DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
660 // Buoyancy is faked by changing the gravity applied to the object
661 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
662 if (PhysBody.HasPhysicalBody)
663 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
664 }
665 }
666
667 // Used for MoveTo
668 public override OMV.Vector3 PIDTarget {
669 set { _PIDTarget = value; }
670 }
671 public override bool PIDActive {
672 set { _usePID = value; }
673 }
674 public override float PIDTau {
675 set { _PIDTau = value; }
676 }
677
678 // Used for llSetHoverHeight and maybe vehicle height
679 // Hover Height will override MoveTo target's Z
680 public override bool PIDHoverActive {
681 set { _useHoverPID = value; }
682 }
683 public override float PIDHoverHeight {
684 set { _PIDHoverHeight = value; }
685 }
686 public override PIDHoverType PIDHoverType {
687 set { _PIDHoverType = value; }
688 }
689 public override float PIDHoverTau {
690 set { _PIDHoverTao = value; }
691 }
692
693 // For RotLookAt
694 public override OMV.Quaternion APIDTarget { set { return; } }
695 public override bool APIDActive { set { return; } }
696 public override float APIDStrength { set { return; } }
697 public override float APIDDamping { set { return; } }
698
699 public override void AddForce(OMV.Vector3 force, bool pushforce) {
700 if (force.IsFinite())
701 {
702 _force.X += force.X;
703 _force.Y += force.Y;
704 _force.Z += force.Z;
705 // m_log.DebugFormat("{0}: AddForce. adding={1}, newForce={2}", LogHeader, force, _force);
706 PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate()
707 {
708 DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force);
709 if (PhysBody.HasPhysicalBody)
710 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
711 });
712 }
713 else
714 {
715 m_log.ErrorFormat("{0}: Got a NaN force applied to a Character", LogHeader);
716 }
717 //m_lastUpdateSent = false;
718 }
719
720 public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
721 }
722 public override void SetMomentum(OMV.Vector3 momentum) {
723 }
724
725 private void ComputeAvatarScale(OMV.Vector3 size)
726 {
727 OMV.Vector3 newScale = size;
728 // newScale.X = PhysicsScene.Params.avatarCapsuleWidth;
729 // newScale.Y = PhysicsScene.Params.avatarCapsuleDepth;
730
731 // From the total height, remove the capsule half spheres that are at each end
732 // The 1.15f came from ODE. Not sure what this factors in.
733 // newScale.Z = (size.Z * 1.15f) - (newScale.X + newScale.Y);
734
735 // The total scale height is the central cylindar plus the caps on the two ends.
736 newScale.Z = size.Z + (Math.Min(size.X, size.Y) * 2f);
737
738 // Convert diameters to radii and height to half height -- the way Bullet expects it.
739 Scale = newScale / 2f;
740 }
741
742 // set _avatarVolume and _mass based on capsule size, _density and Scale
743 private void ComputeAvatarVolumeAndMass()
744 {
745 _avatarVolume = (float)(
746 Math.PI
747 * Scale.X
748 * Scale.Y // the area of capsule cylinder
749 * Scale.Z // times height of capsule cylinder
750 + 1.33333333f
751 * Math.PI
752 * Scale.X
753 * Math.Min(Scale.X, Scale.Y)
754 * Scale.Y // plus the volume of the capsule end caps
755 );
756 _mass = _avatarDensity * _avatarVolume;
757 }
758
759 // The physics engine says that properties have updated. Update same and inform
760 // the world that things have changed.
761 public override void UpdateProperties(EntityProperties entprop)
762 {
763 _position = entprop.Position;
764 _orientation = entprop.Rotation;
765 _velocity = entprop.Velocity;
766 _acceleration = entprop.Acceleration;
767 _rotationalVelocity = entprop.RotationalVelocity;
768
769 // Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
770 PositionSanityCheck(true);
771
772 if (_velocityMotor.Enabled)
773 {
774 // TODO: Decide if the step parameters should be changed depending on the avatar's
775 // state (flying, colliding, ...).
776
777 OMV.Vector3 stepVelocity = _velocityMotor.Step(PhysicsScene.LastTimeStep);
778
779 // If falling, we keep the world's downward vector no matter what the other axis specify.
780 if (!Flying && !IsColliding)
781 {
782 stepVelocity.Z = entprop.Velocity.Z;
783 DetailLog("{0},BSCharacter.UpdateProperties,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity);
784 }
785
786 // If the user has said stop and we've stopped applying velocity correction,
787 // the motor can be turned off. Set the velocity to zero so the zero motion is sent to the viewer.
788 if (_velocityMotor.TargetValue.ApproxEquals(OMV.Vector3.Zero, 0.01f) && _velocityMotor.ErrorIsZero)
789 {
790 stepVelocity = OMV.Vector3.Zero;
791 _velocityMotor.Enabled = false;
792 DetailLog("{0},BSCharacter.UpdateProperties,taint,disableVelocityMotor,m={1}", LocalID, _velocityMotor);
793 }
794
795 _velocity = stepVelocity;
796 entprop.Velocity = _velocity;
797 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
798 }
799
800 // remember the current and last set values
801 LastEntityProperties = CurrentEntityProperties;
802 CurrentEntityProperties = entprop;
803
804 // Tell the linkset about value changes
805 Linkset.UpdateProperties(this, true);
806
807 // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
808 // base.RequestPhysicsterseUpdate();
809
810 DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
811 LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity);
812 }
813}
814}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSConstraint.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraint.cs
new file mode 100644
index 0000000..426bdc2
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraint.cs
@@ -0,0 +1,135 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using OpenMetaverse;
31
32namespace OpenSim.Region.Physics.BulletSNPlugin
33{
34
35public abstract class BSConstraint : IDisposable
36{
37 private static string LogHeader = "[BULLETSIM CONSTRAINT]";
38
39 protected BulletSim m_world;
40 protected BulletBody m_body1;
41 protected BulletBody m_body2;
42 protected BulletConstraint m_constraint;
43 protected bool m_enabled = false;
44
45 public BulletBody Body1 { get { return m_body1; } }
46 public BulletBody Body2 { get { return m_body2; } }
47 public BulletConstraint Constraint { get { return m_constraint; } }
48 public abstract ConstraintType Type { get; }
49 public bool IsEnabled { get { return m_enabled; } }
50
51 public BSConstraint()
52 {
53 }
54
55 public virtual void Dispose()
56 {
57 if (m_enabled)
58 {
59 m_enabled = false;
60 if (m_constraint.HasPhysicalConstraint)
61 {
62 bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr);
63 m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}",
64 BSScene.DetailLogZero,
65 m_body1.ID, m_body1.ptr.ToString(),
66 m_body2.ID, m_body2.ptr.ToString(),
67 success);
68 m_constraint.Clear();
69 }
70 }
71 }
72
73 public virtual bool SetLinearLimits(Vector3 low, Vector3 high)
74 {
75 bool ret = false;
76 if (m_enabled)
77 ret = BulletSimAPI.SetLinearLimits2(m_constraint.ptr, low, high);
78 return ret;
79 }
80
81 public virtual bool SetAngularLimits(Vector3 low, Vector3 high)
82 {
83 bool ret = false;
84 if (m_enabled)
85 ret = BulletSimAPI.SetAngularLimits2(m_constraint.ptr, low, high);
86 return ret;
87 }
88
89 public virtual bool SetSolverIterations(float cnt)
90 {
91 bool ret = false;
92 if (m_enabled)
93 {
94 BulletSimAPI.SetConstraintNumSolverIterations2(m_constraint.ptr, cnt);
95 ret = true;
96 }
97 return ret;
98 }
99
100 public virtual bool CalculateTransforms()
101 {
102 bool ret = false;
103 if (m_enabled)
104 {
105 // Recompute the internal transforms
106 BulletSimAPI.CalculateTransforms2(m_constraint.ptr);
107 ret = true;
108 }
109 return ret;
110 }
111
112 // Reset this constraint making sure it has all its internal structures
113 // recomputed and is enabled and ready to go.
114 public virtual bool RecomputeConstraintVariables(float mass)
115 {
116 bool ret = false;
117 if (m_enabled)
118 {
119 ret = CalculateTransforms();
120 if (ret)
121 {
122 // Setting an object's mass to zero (making it static like when it's selected)
123 // automatically disables the constraints.
124 // If the link is enabled, be sure to set the constraint itself to enabled.
125 BulletSimAPI.SetConstraintEnable2(m_constraint.ptr, BSParam.NumericBool(true));
126 }
127 else
128 {
129 m_world.physicsScene.Logger.ErrorFormat("{0} CalculateTransforms failed. A={1}, B={2}", LogHeader, Body1.ID, Body2.ID);
130 }
131 }
132 return ret;
133 }
134}
135}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSConstraint6Dof.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraint6Dof.cs
new file mode 100644
index 0000000..0181d9d
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraint6Dof.cs
@@ -0,0 +1,153 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using OpenMetaverse;
31
32namespace OpenSim.Region.Physics.BulletSNPlugin
33{
34
35public sealed class BSConstraint6Dof : BSConstraint
36{
37 private static string LogHeader = "[BULLETSIM 6DOF CONSTRAINT]";
38
39 public override ConstraintType Type { get { return ConstraintType.D6_CONSTRAINT_TYPE; } }
40
41 // Create a btGeneric6DofConstraint
42 public BSConstraint6Dof(BulletSim world, BulletBody obj1, BulletBody obj2,
43 Vector3 frame1, Quaternion frame1rot,
44 Vector3 frame2, Quaternion frame2rot,
45 bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
46 {
47 m_world = world;
48 m_body1 = obj1;
49 m_body2 = obj2;
50 m_constraint = new BulletConstraint(
51 BulletSimAPI.Create6DofConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
52 frame1, frame1rot,
53 frame2, frame2rot,
54 useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
55 m_enabled = true;
56 world.physicsScene.DetailLog("{0},BS6DofConstraint,createFrame,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
57 BSScene.DetailLogZero, world.worldID,
58 obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
59 }
60
61 public BSConstraint6Dof(BulletSim world, BulletBody obj1, BulletBody obj2,
62 Vector3 joinPoint,
63 bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
64 {
65 m_world = world;
66 m_body1 = obj1;
67 m_body2 = obj2;
68 if (!obj1.HasPhysicalBody || !obj2.HasPhysicalBody)
69 {
70 world.physicsScene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
71 BSScene.DetailLogZero, world.worldID,
72 obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
73 world.physicsScene.Logger.ErrorFormat("{0} Attempt to build 6DOF constraint with missing bodies: wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
74 LogHeader, world.worldID, obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
75 m_enabled = false;
76 }
77 else
78 {
79 m_constraint = new BulletConstraint(
80 BulletSimAPI.Create6DofConstraintToPoint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
81 joinPoint,
82 useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
83 world.physicsScene.DetailLog("{0},BS6DofConstraint,createMidPoint,wID={1}, csrt={2}, rID={3}, rBody={4}, cID={5}, cBody={6}",
84 BSScene.DetailLogZero, world.worldID, m_constraint.ptr.ToString(),
85 obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
86 if (!m_constraint.HasPhysicalConstraint)
87 {
88 world.physicsScene.Logger.ErrorFormat("{0} Failed creation of 6Dof constraint. rootID={1}, childID={2}",
89 LogHeader, obj1.ID, obj2.ID);
90 m_enabled = false;
91 }
92 else
93 {
94 m_enabled = true;
95 }
96 }
97 }
98
99 public bool SetFrames(Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot)
100 {
101 bool ret = false;
102 if (m_enabled)
103 {
104 BulletSimAPI.SetFrames2(m_constraint.ptr, frameA, frameArot, frameB, frameBrot);
105 ret = true;
106 }
107 return ret;
108 }
109
110 public bool SetCFMAndERP(float cfm, float erp)
111 {
112 bool ret = false;
113 if (m_enabled)
114 {
115 BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
116 BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL);
117 BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
118 ret = true;
119 }
120 return ret;
121 }
122
123 public bool UseFrameOffset(bool useOffset)
124 {
125 bool ret = false;
126 float onOff = useOffset ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
127 if (m_enabled)
128 ret = BulletSimAPI.UseFrameOffset2(m_constraint.ptr, onOff);
129 return ret;
130 }
131
132 public bool TranslationalLimitMotor(bool enable, float targetVelocity, float maxMotorForce)
133 {
134 bool ret = false;
135 float onOff = enable ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
136 if (m_enabled)
137 {
138 ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.ptr, onOff, targetVelocity, maxMotorForce);
139 m_world.physicsScene.DetailLog("{0},BS6DOFConstraint,TransLimitMotor,enable={1},vel={2},maxForce={3}",
140 BSScene.DetailLogZero, enable, targetVelocity, maxMotorForce);
141 }
142 return ret;
143 }
144
145 public bool SetBreakingImpulseThreshold(float threshold)
146 {
147 bool ret = false;
148 if (m_enabled)
149 ret = BulletSimAPI.SetBreakingImpulseThreshold2(m_constraint.ptr, threshold);
150 return ret;
151 }
152}
153}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSConstraintCollection.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraintCollection.cs
new file mode 100644
index 0000000..5c00b1a
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraintCollection.cs
@@ -0,0 +1,180 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using log4net;
31using OpenMetaverse;
32
33namespace OpenSim.Region.Physics.BulletSNPlugin
34{
35
36public sealed class BSConstraintCollection : IDisposable
37{
38 // private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
39 // private static readonly string LogHeader = "[CONSTRAINT COLLECTION]";
40
41 delegate bool ConstraintAction(BSConstraint constrain);
42
43 private List<BSConstraint> m_constraints;
44 private BulletSim m_world;
45
46 public BSConstraintCollection(BulletSim world)
47 {
48 m_world = world;
49 m_constraints = new List<BSConstraint>();
50 }
51
52 public void Dispose()
53 {
54 this.Clear();
55 }
56
57 public void Clear()
58 {
59 lock (m_constraints)
60 {
61 foreach (BSConstraint cons in m_constraints)
62 {
63 cons.Dispose();
64 }
65 m_constraints.Clear();
66 }
67 }
68
69 public bool AddConstraint(BSConstraint cons)
70 {
71 lock (m_constraints)
72 {
73 // There is only one constraint between any bodies. Remove any old just to make sure.
74 RemoveAndDestroyConstraint(cons.Body1, cons.Body2);
75
76 m_constraints.Add(cons);
77 }
78
79 return true;
80 }
81
82 // Get the constraint between two bodies. There can be only one.
83 // Return 'true' if a constraint was found.
84 public bool TryGetConstraint(BulletBody body1, BulletBody body2, out BSConstraint returnConstraint)
85 {
86 bool found = false;
87 BSConstraint foundConstraint = null;
88
89 uint lookingID1 = body1.ID;
90 uint lookingID2 = body2.ID;
91 lock (m_constraints)
92 {
93 foreach (BSConstraint constrain in m_constraints)
94 {
95 if ((constrain.Body1.ID == lookingID1 && constrain.Body2.ID == lookingID2)
96 || (constrain.Body1.ID == lookingID2 && constrain.Body2.ID == lookingID1))
97 {
98 foundConstraint = constrain;
99 found = true;
100 break;
101 }
102 }
103 }
104 returnConstraint = foundConstraint;
105 return found;
106 }
107
108 // Remove any constraint between the passed bodies.
109 // Presumed there is only one such constraint possible.
110 // Return 'true' if a constraint was found and destroyed.
111 public bool RemoveAndDestroyConstraint(BulletBody body1, BulletBody body2)
112 {
113 bool ret = false;
114 lock (m_constraints)
115 {
116 BSConstraint constrain;
117 if (this.TryGetConstraint(body1, body2, out constrain))
118 {
119 // remove the constraint from our collection
120 RemoveAndDestroyConstraint(constrain);
121 ret = true;
122 }
123 }
124
125 return ret;
126 }
127
128 // The constraint MUST exist in the collection
129 public bool RemoveAndDestroyConstraint(BSConstraint constrain)
130 {
131 lock (m_constraints)
132 {
133 // remove the constraint from our collection
134 m_constraints.Remove(constrain);
135 }
136 // tell the engine that all its structures need to be freed
137 constrain.Dispose();
138 // we destroyed something
139 return true;
140 }
141
142 // Remove all constraints that reference the passed body.
143 // Return 'true' if any constraints were destroyed.
144 public bool RemoveAndDestroyConstraint(BulletBody body1)
145 {
146 List<BSConstraint> toRemove = new List<BSConstraint>();
147 uint lookingID = body1.ID;
148 lock (m_constraints)
149 {
150 foreach (BSConstraint constrain in m_constraints)
151 {
152 if (constrain.Body1.ID == lookingID || constrain.Body2.ID == lookingID)
153 {
154 toRemove.Add(constrain);
155 }
156 }
157 foreach (BSConstraint constrain in toRemove)
158 {
159 m_constraints.Remove(constrain);
160 constrain.Dispose();
161 }
162 }
163 return (toRemove.Count > 0);
164 }
165
166 public bool RecalculateAllConstraints()
167 {
168 bool ret = false;
169 lock (m_constraints)
170 {
171 foreach (BSConstraint constrain in m_constraints)
172 {
173 constrain.CalculateTransforms();
174 ret = true;
175 }
176 }
177 return ret;
178 }
179}
180}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSConstraintHinge.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraintHinge.cs
new file mode 100644
index 0000000..7951f06
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSConstraintHinge.cs
@@ -0,0 +1,57 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using OpenMetaverse;
31
32namespace OpenSim.Region.Physics.BulletSNPlugin
33{
34
35public sealed class BSConstraintHinge : BSConstraint
36{
37 public override ConstraintType Type { get { return ConstraintType.HINGE_CONSTRAINT_TYPE; } }
38
39 public BSConstraintHinge(BulletSim world, BulletBody obj1, BulletBody obj2,
40 Vector3 pivotInA, Vector3 pivotInB,
41 Vector3 axisInA, Vector3 axisInB,
42 bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
43 {
44 m_world = world;
45 m_body1 = obj1;
46 m_body2 = obj2;
47 m_constraint = new BulletConstraint(
48 BulletSimAPI.CreateHingeConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
49 pivotInA, pivotInB,
50 axisInA, axisInB,
51 useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
52 m_enabled = true;
53 }
54
55}
56
57}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSDynamics.cs
new file mode 100644
index 0000000..72afacc
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSDynamics.cs
@@ -0,0 +1,1374 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 * The quotations from http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial
28 * are Copyright (c) 2009 Linden Research, Inc and are used under their license
29 * of Creative Commons Attribution-Share Alike 3.0
30 * (http://creativecommons.org/licenses/by-sa/3.0/).
31 */
32
33using System;
34using System.Collections.Generic;
35using System.Reflection;
36using System.Runtime.InteropServices;
37using OpenMetaverse;
38using OpenSim.Region.Physics.Manager;
39
40namespace OpenSim.Region.Physics.BulletSNPlugin
41{
42 public sealed class BSDynamics
43 {
44 private static string LogHeader = "[BULLETSIM VEHICLE]";
45
46 private BSScene PhysicsScene { get; set; }
47 // the prim this dynamic controller belongs to
48 private BSPrim Prim { get; set; }
49
50 // mass of the vehicle fetched each time we're calles
51 private float m_vehicleMass;
52
53 // Vehicle properties
54 public Vehicle Type { get; set; }
55
56 // private Quaternion m_referenceFrame = Quaternion.Identity; // Axis modifier
57 private VehicleFlag m_flags = (VehicleFlag) 0; // Boolean settings:
58 // HOVER_TERRAIN_ONLY
59 // HOVER_GLOBAL_HEIGHT
60 // NO_DEFLECTION_UP
61 // HOVER_WATER_ONLY
62 // HOVER_UP_ONLY
63 // LIMIT_MOTOR_UP
64 // LIMIT_ROLL_ONLY
65 private Vector3 m_BlockingEndPoint = Vector3.Zero;
66 private Quaternion m_RollreferenceFrame = Quaternion.Identity;
67 private Quaternion m_referenceFrame = Quaternion.Identity;
68
69 // Linear properties
70 private BSVMotor m_linearMotor = new BSVMotor("LinearMotor");
71 private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time
72 private Vector3 m_linearMotorOffset = Vector3.Zero; // the point of force can be offset from the center
73 private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL
74 private Vector3 m_linearFrictionTimescale = Vector3.Zero;
75 private float m_linearMotorDecayTimescale = 0;
76 private float m_linearMotorTimescale = 0;
77 private Vector3 m_lastLinearVelocityVector = Vector3.Zero;
78 private Vector3 m_lastPositionVector = Vector3.Zero;
79 // private bool m_LinearMotorSetLastFrame = false;
80 // private Vector3 m_linearMotorOffset = Vector3.Zero;
81
82 //Angular properties
83 private BSVMotor m_angularMotor = new BSVMotor("AngularMotor");
84 private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor
85 // private int m_angularMotorApply = 0; // application frame counter
86 private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity
87 private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate
88 private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate
89 private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate
90 private Vector3 m_lastAngularVelocity = Vector3.Zero;
91 private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body
92
93 //Deflection properties
94 private BSVMotor m_angularDeflectionMotor = new BSVMotor("AngularDeflection");
95 private float m_angularDeflectionEfficiency = 0;
96 private float m_angularDeflectionTimescale = 0;
97 private float m_linearDeflectionEfficiency = 0;
98 private float m_linearDeflectionTimescale = 0;
99
100 //Banking properties
101 private float m_bankingEfficiency = 0;
102 private float m_bankingMix = 0;
103 private float m_bankingTimescale = 0;
104
105 //Hover and Buoyancy properties
106 private BSVMotor m_hoverMotor = new BSVMotor("Hover");
107 private float m_VhoverHeight = 0f;
108 private float m_VhoverEfficiency = 0f;
109 private float m_VhoverTimescale = 0f;
110 private float m_VhoverTargetHeight = -1.0f; // if <0 then no hover, else its the current target height
111 private float m_VehicleBuoyancy = 0f; //KF: m_VehicleBuoyancy is set by VEHICLE_BUOYANCY for a vehicle.
112 // Modifies gravity. Slider between -1 (double-gravity) and 1 (full anti-gravity)
113 // KF: So far I have found no good method to combine a script-requested .Z velocity and gravity.
114 // Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity.
115
116 //Attractor properties
117 private BSVMotor m_verticalAttractionMotor = new BSVMotor("VerticalAttraction");
118 private float m_verticalAttractionEfficiency = 1.0f; // damped
119 private float m_verticalAttractionCutoff = 500f; // per the documentation
120 // Timescale > cutoff means no vert attractor.
121 private float m_verticalAttractionTimescale = 510f;
122
123 // Just some recomputed constants:
124 static readonly float PIOverFour = ((float)Math.PI) / 4f;
125 static readonly float PIOverTwo = ((float)Math.PI) / 2f;
126
127 public BSDynamics(BSScene myScene, BSPrim myPrim)
128 {
129 PhysicsScene = myScene;
130 Prim = myPrim;
131 Type = Vehicle.TYPE_NONE;
132 }
133
134 // Return 'true' if this vehicle is doing vehicle things
135 public bool IsActive
136 {
137 get { return Type != Vehicle.TYPE_NONE && Prim.IsPhysical; }
138 }
139
140 internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue)
141 {
142 VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue);
143 switch (pParam)
144 {
145 case Vehicle.ANGULAR_DEFLECTION_EFFICIENCY:
146 m_angularDeflectionEfficiency = Math.Max(pValue, 0.01f);
147 break;
148 case Vehicle.ANGULAR_DEFLECTION_TIMESCALE:
149 m_angularDeflectionTimescale = Math.Max(pValue, 0.01f);
150 break;
151 case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE:
152 m_angularMotorDecayTimescale = ClampInRange(0.01f, pValue, 120);
153 m_angularMotor.TargetValueDecayTimeScale = m_angularMotorDecayTimescale;
154 break;
155 case Vehicle.ANGULAR_MOTOR_TIMESCALE:
156 m_angularMotorTimescale = Math.Max(pValue, 0.01f);
157 m_angularMotor.TimeScale = m_angularMotorTimescale;
158 break;
159 case Vehicle.BANKING_EFFICIENCY:
160 m_bankingEfficiency = ClampInRange(-1f, pValue, 1f);
161 break;
162 case Vehicle.BANKING_MIX:
163 m_bankingMix = Math.Max(pValue, 0.01f);
164 break;
165 case Vehicle.BANKING_TIMESCALE:
166 m_bankingTimescale = Math.Max(pValue, 0.01f);
167 break;
168 case Vehicle.BUOYANCY:
169 m_VehicleBuoyancy = ClampInRange(-1f, pValue, 1f);
170 break;
171 case Vehicle.HOVER_EFFICIENCY:
172 m_VhoverEfficiency = ClampInRange(0f, pValue, 1f);
173 break;
174 case Vehicle.HOVER_HEIGHT:
175 m_VhoverHeight = pValue;
176 break;
177 case Vehicle.HOVER_TIMESCALE:
178 m_VhoverTimescale = Math.Max(pValue, 0.01f);
179 break;
180 case Vehicle.LINEAR_DEFLECTION_EFFICIENCY:
181 m_linearDeflectionEfficiency = Math.Max(pValue, 0.01f);
182 break;
183 case Vehicle.LINEAR_DEFLECTION_TIMESCALE:
184 m_linearDeflectionTimescale = Math.Max(pValue, 0.01f);
185 break;
186 case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE:
187 m_linearMotorDecayTimescale = ClampInRange(0.01f, pValue, 120);
188 m_linearMotor.TargetValueDecayTimeScale = m_linearMotorDecayTimescale;
189 break;
190 case Vehicle.LINEAR_MOTOR_TIMESCALE:
191 m_linearMotorTimescale = Math.Max(pValue, 0.01f);
192 m_linearMotor.TimeScale = m_linearMotorTimescale;
193 break;
194 case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY:
195 m_verticalAttractionEfficiency = ClampInRange(0.1f, pValue, 1f);
196 m_verticalAttractionMotor.Efficiency = m_verticalAttractionEfficiency;
197 break;
198 case Vehicle.VERTICAL_ATTRACTION_TIMESCALE:
199 m_verticalAttractionTimescale = Math.Max(pValue, 0.01f);
200 m_verticalAttractionMotor.TimeScale = m_verticalAttractionTimescale;
201 break;
202
203 // These are vector properties but the engine lets you use a single float value to
204 // set all of the components to the same value
205 case Vehicle.ANGULAR_FRICTION_TIMESCALE:
206 m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue);
207 m_angularMotor.FrictionTimescale = m_angularFrictionTimescale;
208 break;
209 case Vehicle.ANGULAR_MOTOR_DIRECTION:
210 m_angularMotorDirection = new Vector3(pValue, pValue, pValue);
211 m_angularMotor.SetTarget(m_angularMotorDirection);
212 break;
213 case Vehicle.LINEAR_FRICTION_TIMESCALE:
214 m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue);
215 m_linearMotor.FrictionTimescale = m_linearFrictionTimescale;
216 break;
217 case Vehicle.LINEAR_MOTOR_DIRECTION:
218 m_linearMotorDirection = new Vector3(pValue, pValue, pValue);
219 m_linearMotorDirectionLASTSET = new Vector3(pValue, pValue, pValue);
220 m_linearMotor.SetTarget(m_linearMotorDirection);
221 break;
222 case Vehicle.LINEAR_MOTOR_OFFSET:
223 m_linearMotorOffset = new Vector3(pValue, pValue, pValue);
224 break;
225
226 }
227 }//end ProcessFloatVehicleParam
228
229 internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue)
230 {
231 VDetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue);
232 switch (pParam)
233 {
234 case Vehicle.ANGULAR_FRICTION_TIMESCALE:
235 m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
236 m_angularMotor.FrictionTimescale = m_angularFrictionTimescale;
237 break;
238 case Vehicle.ANGULAR_MOTOR_DIRECTION:
239 // Limit requested angular speed to 2 rps= 4 pi rads/sec
240 pValue.X = ClampInRange(-12.56f, pValue.X, 12.56f);
241 pValue.Y = ClampInRange(-12.56f, pValue.Y, 12.56f);
242 pValue.Z = ClampInRange(-12.56f, pValue.Z, 12.56f);
243 m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
244 m_angularMotor.SetTarget(m_angularMotorDirection);
245 break;
246 case Vehicle.LINEAR_FRICTION_TIMESCALE:
247 m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
248 m_linearMotor.FrictionTimescale = m_linearFrictionTimescale;
249 break;
250 case Vehicle.LINEAR_MOTOR_DIRECTION:
251 m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
252 m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z);
253 m_linearMotor.SetTarget(m_linearMotorDirection);
254 break;
255 case Vehicle.LINEAR_MOTOR_OFFSET:
256 m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z);
257 break;
258 case Vehicle.BLOCK_EXIT:
259 m_BlockingEndPoint = new Vector3(pValue.X, pValue.Y, pValue.Z);
260 break;
261 }
262 }//end ProcessVectorVehicleParam
263
264 internal void ProcessRotationVehicleParam(Vehicle pParam, Quaternion pValue)
265 {
266 VDetailLog("{0},ProcessRotationalVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue);
267 switch (pParam)
268 {
269 case Vehicle.REFERENCE_FRAME:
270 m_referenceFrame = pValue;
271 break;
272 case Vehicle.ROLL_FRAME:
273 m_RollreferenceFrame = pValue;
274 break;
275 }
276 }//end ProcessRotationVehicleParam
277
278 internal void ProcessVehicleFlags(int pParam, bool remove)
279 {
280 VDetailLog("{0},ProcessVehicleFlags,param={1},remove={2}", Prim.LocalID, pParam, remove);
281 VehicleFlag parm = (VehicleFlag)pParam;
282 if (pParam == -1)
283 m_flags = (VehicleFlag)0;
284 else
285 {
286 if (remove)
287 m_flags &= ~parm;
288 else
289 m_flags |= parm;
290 }
291 }
292
293 internal void ProcessTypeChange(Vehicle pType)
294 {
295 VDetailLog("{0},ProcessTypeChange,type={1}", Prim.LocalID, pType);
296 // Set Defaults For Type
297 Type = pType;
298 switch (pType)
299 {
300 case Vehicle.TYPE_NONE:
301 m_linearMotorDirection = Vector3.Zero;
302 m_linearMotorTimescale = 0;
303 m_linearMotorDecayTimescale = 0;
304 m_linearFrictionTimescale = new Vector3(0, 0, 0);
305
306 m_angularMotorDirection = Vector3.Zero;
307 m_angularMotorDecayTimescale = 0;
308 m_angularMotorTimescale = 0;
309 m_angularFrictionTimescale = new Vector3(0, 0, 0);
310
311 m_VhoverHeight = 0;
312 m_VhoverEfficiency = 0;
313 m_VhoverTimescale = 0;
314 m_VehicleBuoyancy = 0;
315
316 m_linearDeflectionEfficiency = 1;
317 m_linearDeflectionTimescale = 1;
318
319 m_angularDeflectionEfficiency = 0;
320 m_angularDeflectionTimescale = 1000;
321
322 m_verticalAttractionEfficiency = 0;
323 m_verticalAttractionTimescale = 0;
324
325 m_bankingEfficiency = 0;
326 m_bankingTimescale = 1000;
327 m_bankingMix = 1;
328
329 m_referenceFrame = Quaternion.Identity;
330 m_flags = (VehicleFlag)0;
331
332 break;
333
334 case Vehicle.TYPE_SLED:
335 m_linearMotorDirection = Vector3.Zero;
336 m_linearMotorTimescale = 1000;
337 m_linearMotorDecayTimescale = 120;
338 m_linearFrictionTimescale = new Vector3(30, 1, 1000);
339
340 m_angularMotorDirection = Vector3.Zero;
341 m_angularMotorTimescale = 1000;
342 m_angularMotorDecayTimescale = 120;
343 m_angularFrictionTimescale = new Vector3(1000, 1000, 1000);
344
345 m_VhoverHeight = 0;
346 m_VhoverEfficiency = 10; // TODO: this looks wrong!!
347 m_VhoverTimescale = 10;
348 m_VehicleBuoyancy = 0;
349
350 m_linearDeflectionEfficiency = 1;
351 m_linearDeflectionTimescale = 1;
352
353 m_angularDeflectionEfficiency = 1;
354 m_angularDeflectionTimescale = 1000;
355
356 m_verticalAttractionEfficiency = 0;
357 m_verticalAttractionTimescale = 0;
358
359 m_bankingEfficiency = 0;
360 m_bankingTimescale = 10;
361 m_bankingMix = 1;
362
363 m_referenceFrame = Quaternion.Identity;
364 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
365 | VehicleFlag.HOVER_TERRAIN_ONLY
366 | VehicleFlag.HOVER_GLOBAL_HEIGHT
367 | VehicleFlag.HOVER_UP_ONLY);
368 m_flags |= (VehicleFlag.NO_DEFLECTION_UP
369 | VehicleFlag.LIMIT_ROLL_ONLY
370 | VehicleFlag.LIMIT_MOTOR_UP);
371
372 break;
373 case Vehicle.TYPE_CAR:
374 m_linearMotorDirection = Vector3.Zero;
375 m_linearMotorTimescale = 1;
376 m_linearMotorDecayTimescale = 60;
377 m_linearFrictionTimescale = new Vector3(100, 2, 1000);
378
379 m_angularMotorDirection = Vector3.Zero;
380 m_angularMotorTimescale = 1;
381 m_angularMotorDecayTimescale = 0.8f;
382 m_angularFrictionTimescale = new Vector3(1000, 1000, 1000);
383
384 m_VhoverHeight = 0;
385 m_VhoverEfficiency = 0;
386 m_VhoverTimescale = 1000;
387 m_VehicleBuoyancy = 0;
388
389 m_linearDeflectionEfficiency = 1;
390 m_linearDeflectionTimescale = 2;
391
392 m_angularDeflectionEfficiency = 0;
393 m_angularDeflectionTimescale = 10;
394
395 m_verticalAttractionEfficiency = 1f;
396 m_verticalAttractionTimescale = 10f;
397
398 m_bankingEfficiency = -0.2f;
399 m_bankingMix = 1;
400 m_bankingTimescale = 1;
401
402 m_referenceFrame = Quaternion.Identity;
403 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
404 | VehicleFlag.HOVER_TERRAIN_ONLY
405 | VehicleFlag.HOVER_GLOBAL_HEIGHT);
406 m_flags |= (VehicleFlag.NO_DEFLECTION_UP
407 | VehicleFlag.LIMIT_ROLL_ONLY
408 | VehicleFlag.LIMIT_MOTOR_UP
409 | VehicleFlag.HOVER_UP_ONLY);
410 break;
411 case Vehicle.TYPE_BOAT:
412 m_linearMotorDirection = Vector3.Zero;
413 m_linearMotorTimescale = 5;
414 m_linearMotorDecayTimescale = 60;
415 m_linearFrictionTimescale = new Vector3(10, 3, 2);
416
417 m_angularMotorDirection = Vector3.Zero;
418 m_angularMotorTimescale = 4;
419 m_angularMotorDecayTimescale = 4;
420 m_angularFrictionTimescale = new Vector3(10,10,10);
421
422 m_VhoverHeight = 0;
423 m_VhoverEfficiency = 0.5f;
424 m_VhoverTimescale = 2;
425 m_VehicleBuoyancy = 1;
426
427 m_linearDeflectionEfficiency = 0.5f;
428 m_linearDeflectionTimescale = 3;
429
430 m_angularDeflectionEfficiency = 0.5f;
431 m_angularDeflectionTimescale = 5;
432
433 m_verticalAttractionEfficiency = 0.5f;
434 m_verticalAttractionTimescale = 5f;
435
436 m_bankingEfficiency = -0.3f;
437 m_bankingMix = 0.8f;
438 m_bankingTimescale = 1;
439
440 m_referenceFrame = Quaternion.Identity;
441 m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY
442 | VehicleFlag.HOVER_GLOBAL_HEIGHT
443 | VehicleFlag.LIMIT_ROLL_ONLY
444 | VehicleFlag.HOVER_UP_ONLY);
445 m_flags |= (VehicleFlag.NO_DEFLECTION_UP
446 | VehicleFlag.LIMIT_MOTOR_UP
447 | VehicleFlag.HOVER_WATER_ONLY);
448 break;
449 case Vehicle.TYPE_AIRPLANE:
450 m_linearMotorDirection = Vector3.Zero;
451 m_linearMotorTimescale = 2;
452 m_linearMotorDecayTimescale = 60;
453 m_linearFrictionTimescale = new Vector3(200, 10, 5);
454
455 m_angularMotorDirection = Vector3.Zero;
456 m_angularMotorTimescale = 4;
457 m_angularMotorDecayTimescale = 4;
458 m_angularFrictionTimescale = new Vector3(20, 20, 20);
459
460 m_VhoverHeight = 0;
461 m_VhoverEfficiency = 0.5f;
462 m_VhoverTimescale = 1000;
463 m_VehicleBuoyancy = 0;
464
465 m_linearDeflectionEfficiency = 0.5f;
466 m_linearDeflectionTimescale = 3;
467
468 m_angularDeflectionEfficiency = 1;
469 m_angularDeflectionTimescale = 2;
470
471 m_verticalAttractionEfficiency = 0.9f;
472 m_verticalAttractionTimescale = 2f;
473
474 m_bankingEfficiency = 1;
475 m_bankingMix = 0.7f;
476 m_bankingTimescale = 2;
477
478 m_referenceFrame = Quaternion.Identity;
479 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
480 | VehicleFlag.HOVER_TERRAIN_ONLY
481 | VehicleFlag.HOVER_GLOBAL_HEIGHT
482 | VehicleFlag.HOVER_UP_ONLY
483 | VehicleFlag.NO_DEFLECTION_UP
484 | VehicleFlag.LIMIT_MOTOR_UP);
485 m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY);
486 break;
487 case Vehicle.TYPE_BALLOON:
488 m_linearMotorDirection = Vector3.Zero;
489 m_linearMotorTimescale = 5;
490 m_linearFrictionTimescale = new Vector3(5, 5, 5);
491 m_linearMotorDecayTimescale = 60;
492
493 m_angularMotorDirection = Vector3.Zero;
494 m_angularMotorTimescale = 6;
495 m_angularFrictionTimescale = new Vector3(10, 10, 10);
496 m_angularMotorDecayTimescale = 10;
497
498 m_VhoverHeight = 5;
499 m_VhoverEfficiency = 0.8f;
500 m_VhoverTimescale = 10;
501 m_VehicleBuoyancy = 1;
502
503 m_linearDeflectionEfficiency = 0;
504 m_linearDeflectionTimescale = 5;
505
506 m_angularDeflectionEfficiency = 0;
507 m_angularDeflectionTimescale = 5;
508
509 m_verticalAttractionEfficiency = 1f;
510 m_verticalAttractionTimescale = 100f;
511
512 m_bankingEfficiency = 0;
513 m_bankingMix = 0.7f;
514 m_bankingTimescale = 5;
515
516 m_referenceFrame = Quaternion.Identity;
517
518 m_referenceFrame = Quaternion.Identity;
519 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
520 | VehicleFlag.HOVER_TERRAIN_ONLY
521 | VehicleFlag.HOVER_UP_ONLY
522 | VehicleFlag.NO_DEFLECTION_UP
523 | VehicleFlag.LIMIT_MOTOR_UP);
524 m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY
525 | VehicleFlag.HOVER_GLOBAL_HEIGHT);
526 break;
527 }
528
529 // Update any physical parameters based on this type.
530 Refresh();
531
532 m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale,
533 m_linearMotorDecayTimescale, m_linearFrictionTimescale,
534 1f);
535 m_linearMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
536
537 m_angularMotor = new BSVMotor("AngularMotor", m_angularMotorTimescale,
538 m_angularMotorDecayTimescale, m_angularFrictionTimescale,
539 1f);
540 m_angularMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
541
542 m_verticalAttractionMotor = new BSVMotor("VerticalAttraction", m_verticalAttractionTimescale,
543 BSMotor.Infinite, BSMotor.InfiniteVector,
544 m_verticalAttractionEfficiency);
545 // Z goes away and we keep X and Y
546 m_verticalAttractionMotor.FrictionTimescale = new Vector3(BSMotor.Infinite, BSMotor.Infinite, 0.1f);
547 m_verticalAttractionMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
548 }
549
550 // Some of the properties of this prim may have changed.
551 // Do any updating needed for a vehicle
552 public void Refresh()
553 {
554 if (IsActive)
555 {
556 // Remember the mass so we don't have to fetch it every step
557 m_vehicleMass = Prim.Linkset.LinksetMass;
558
559 // Friction affects are handled by this vehicle code
560 float friction = 0f;
561 BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, friction);
562
563 // Moderate angular movement introduced by Bullet.
564 // TODO: possibly set AngularFactor and LinearFactor for the type of vehicle.
565 // Maybe compute linear and angular factor and damping from params.
566 float angularDamping = BSParam.VehicleAngularDamping;
567 BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping);
568
569 // Vehicles report collision events so we know when it's on the ground
570 BulletSimAPI.AddToCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
571
572 Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(Prim.PhysShape.ptr, m_vehicleMass);
573 BulletSimAPI.SetMassProps2(Prim.PhysBody.ptr, m_vehicleMass, localInertia);
574 BulletSimAPI.UpdateInertiaTensor2(Prim.PhysBody.ptr);
575
576 VDetailLog("{0},BSDynamics.Refresh,mass={1},frict={2},inert={3},aDamp={4}",
577 Prim.LocalID, m_vehicleMass, friction, localInertia, angularDamping);
578 }
579 else
580 {
581 BulletSimAPI.RemoveFromCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
582 }
583 }
584
585 public bool RemoveBodyDependencies(BSPhysObject prim)
586 {
587 // If active, we need to add our properties back when the body is rebuilt.
588 return IsActive;
589 }
590
591 public void RestoreBodyDependencies(BSPhysObject prim)
592 {
593 if (Prim.LocalID != prim.LocalID)
594 {
595 // The call should be on us by our prim. Error if not.
596 PhysicsScene.Logger.ErrorFormat("{0} RestoreBodyDependencies: called by not my prim. passedLocalID={1}, vehiclePrimLocalID={2}",
597 LogHeader, prim.LocalID, Prim.LocalID);
598 return;
599 }
600 Refresh();
601 }
602
603 #region Known vehicle value functions
604 // Vehicle physical parameters that we buffer from constant getting and setting.
605 // The "m_known*" values are unknown until they are fetched and the m_knownHas flag is set.
606 // Changing is remembered and the parameter is stored back into the physics engine only if updated.
607 // This does two things: 1) saves continuious calls into unmanaged code, and
608 // 2) signals when a physics property update must happen back to the simulator
609 // to update values modified for the vehicle.
610 private int m_knownChanged;
611 private int m_knownHas;
612 private float m_knownTerrainHeight;
613 private float m_knownWaterLevel;
614 private Vector3 m_knownPosition;
615 private Vector3 m_knownVelocity;
616 private Vector3 m_knownForce;
617 private Quaternion m_knownOrientation;
618 private Vector3 m_knownRotationalVelocity;
619 private Vector3 m_knownRotationalForce;
620 private Vector3 m_knownForwardVelocity; // vehicle relative forward speed
621
622 private const int m_knownChangedPosition = 1 << 0;
623 private const int m_knownChangedVelocity = 1 << 1;
624 private const int m_knownChangedForce = 1 << 2;
625 private const int m_knownChangedOrientation = 1 << 3;
626 private const int m_knownChangedRotationalVelocity = 1 << 4;
627 private const int m_knownChangedRotationalForce = 1 << 5;
628 private const int m_knownChangedTerrainHeight = 1 << 6;
629 private const int m_knownChangedWaterLevel = 1 << 7;
630 private const int m_knownChangedForwardVelocity = 1 << 8;
631
632 private void ForgetKnownVehicleProperties()
633 {
634 m_knownHas = 0;
635 m_knownChanged = 0;
636 }
637 // Push all the changed values back into the physics engine
638 private void PushKnownChanged()
639 {
640 if (m_knownChanged != 0)
641 {
642 if ((m_knownChanged & m_knownChangedPosition) != 0)
643 Prim.ForcePosition = m_knownPosition;
644
645 if ((m_knownChanged & m_knownChangedOrientation) != 0)
646 Prim.ForceOrientation = m_knownOrientation;
647
648 if ((m_knownChanged & m_knownChangedVelocity) != 0)
649 {
650 Prim.ForceVelocity = m_knownVelocity;
651 BulletSimAPI.SetInterpolationLinearVelocity2(Prim.PhysBody.ptr, VehicleVelocity);
652 }
653
654 if ((m_knownChanged & m_knownChangedForce) != 0)
655 Prim.AddForce((Vector3)m_knownForce, false, true);
656
657 if ((m_knownChanged & m_knownChangedRotationalVelocity) != 0)
658 {
659 Prim.ForceRotationalVelocity = m_knownRotationalVelocity;
660 // Fake out Bullet by making it think the velocity is the same as last time.
661 BulletSimAPI.SetInterpolationAngularVelocity2(Prim.PhysBody.ptr, m_knownRotationalVelocity);
662 }
663
664 if ((m_knownChanged & m_knownChangedRotationalForce) != 0)
665 Prim.AddAngularForce((Vector3)m_knownRotationalForce, false, true);
666
667 // If we set one of the values (ie, the physics engine didn't do it) we must force
668 // an UpdateProperties event to send the changes up to the simulator.
669 BulletSimAPI.PushUpdate2(Prim.PhysBody.ptr);
670 }
671 m_knownChanged = 0;
672 }
673
674 // Since the computation of terrain height can be a little involved, this routine
675 // is used to fetch the height only once for each vehicle simulation step.
676 private float GetTerrainHeight(Vector3 pos)
677 {
678 if ((m_knownHas & m_knownChangedTerrainHeight) == 0)
679 {
680 m_knownTerrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos);
681 m_knownHas |= m_knownChangedTerrainHeight;
682 }
683 return m_knownTerrainHeight;
684 }
685
686 // Since the computation of water level can be a little involved, this routine
687 // is used ot fetch the level only once for each vehicle simulation step.
688 private float GetWaterLevel(Vector3 pos)
689 {
690 if ((m_knownHas & m_knownChangedWaterLevel) == 0)
691 {
692 m_knownWaterLevel = Prim.PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(pos);
693 m_knownHas |= m_knownChangedWaterLevel;
694 }
695 return (float)m_knownWaterLevel;
696 }
697
698 private Vector3 VehiclePosition
699 {
700 get
701 {
702 if ((m_knownHas & m_knownChangedPosition) == 0)
703 {
704 m_knownPosition = Prim.ForcePosition;
705 m_knownHas |= m_knownChangedPosition;
706 }
707 return m_knownPosition;
708 }
709 set
710 {
711 m_knownPosition = value;
712 m_knownChanged |= m_knownChangedPosition;
713 m_knownHas |= m_knownChangedPosition;
714 }
715 }
716
717 private Quaternion VehicleOrientation
718 {
719 get
720 {
721 if ((m_knownHas & m_knownChangedOrientation) == 0)
722 {
723 m_knownOrientation = Prim.ForceOrientation;
724 m_knownHas |= m_knownChangedOrientation;
725 }
726 return m_knownOrientation;
727 }
728 set
729 {
730 m_knownOrientation = value;
731 m_knownChanged |= m_knownChangedOrientation;
732 m_knownHas |= m_knownChangedOrientation;
733 }
734 }
735
736 private Vector3 VehicleVelocity
737 {
738 get
739 {
740 if ((m_knownHas & m_knownChangedVelocity) == 0)
741 {
742 m_knownVelocity = Prim.ForceVelocity;
743 m_knownHas |= m_knownChangedVelocity;
744 }
745 return (Vector3)m_knownVelocity;
746 }
747 set
748 {
749 m_knownVelocity = value;
750 m_knownChanged |= m_knownChangedVelocity;
751 m_knownHas |= m_knownChangedVelocity;
752 }
753 }
754
755 private void VehicleAddForce(Vector3 aForce)
756 {
757 if ((m_knownHas & m_knownChangedForce) == 0)
758 {
759 m_knownForce = Vector3.Zero;
760 }
761 m_knownForce += aForce;
762 m_knownChanged |= m_knownChangedForce;
763 m_knownHas |= m_knownChangedForce;
764 }
765
766 private Vector3 VehicleRotationalVelocity
767 {
768 get
769 {
770 if ((m_knownHas & m_knownChangedRotationalVelocity) == 0)
771 {
772 m_knownRotationalVelocity = Prim.ForceRotationalVelocity;
773 m_knownHas |= m_knownChangedRotationalVelocity;
774 }
775 return (Vector3)m_knownRotationalVelocity;
776 }
777 set
778 {
779 m_knownRotationalVelocity = value;
780 m_knownChanged |= m_knownChangedRotationalVelocity;
781 m_knownHas |= m_knownChangedRotationalVelocity;
782 }
783 }
784 private void VehicleAddAngularForce(Vector3 aForce)
785 {
786 if ((m_knownHas & m_knownChangedRotationalForce) == 0)
787 {
788 m_knownRotationalForce = Vector3.Zero;
789 }
790 m_knownRotationalForce += aForce;
791 m_knownChanged |= m_knownChangedRotationalForce;
792 m_knownHas |= m_knownChangedRotationalForce;
793 }
794 // Vehicle relative forward velocity
795 private Vector3 VehicleForwardVelocity
796 {
797 get
798 {
799 if ((m_knownHas & m_knownChangedForwardVelocity) == 0)
800 {
801 m_knownForwardVelocity = VehicleVelocity * Quaternion.Inverse(Quaternion.Normalize(VehicleOrientation));
802 m_knownHas |= m_knownChangedForwardVelocity;
803 }
804 return m_knownForwardVelocity;
805 }
806 }
807 private float VehicleForwardSpeed
808 {
809 get
810 {
811 return VehicleForwardVelocity.X;
812 }
813 }
814
815 #endregion // Known vehicle value functions
816
817 // One step of the vehicle properties for the next 'pTimestep' seconds.
818 internal void Step(float pTimestep)
819 {
820 if (!IsActive) return;
821
822 ForgetKnownVehicleProperties();
823
824 MoveLinear(pTimestep);
825 MoveAngular(pTimestep);
826
827 LimitRotation(pTimestep);
828
829 // remember the position so next step we can limit absolute movement effects
830 m_lastPositionVector = VehiclePosition;
831
832 // If we forced the changing of some vehicle parameters, update the values and
833 // for the physics engine to note the changes so an UpdateProperties event will happen.
834 PushKnownChanged();
835
836 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
837 Prim.LocalID, VehiclePosition, Prim.Force, VehicleVelocity, VehicleRotationalVelocity);
838 }
839
840 // Apply the effect of the linear motor and other linear motions (like hover and float).
841 private void MoveLinear(float pTimestep)
842 {
843 Vector3 linearMotorContribution = m_linearMotor.Step(pTimestep);
844
845 // The movement computed in the linear motor is relative to the vehicle
846 // coordinates. Rotate the movement to world coordinates.
847 linearMotorContribution *= VehicleOrientation;
848
849 // ==================================================================
850 // Buoyancy: force to overcome gravity.
851 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
852 // So, if zero, don't change anything (let gravity happen). If one, negate the effect of gravity.
853 Vector3 buoyancyContribution = Prim.PhysicsScene.DefaultGravity * m_VehicleBuoyancy;
854
855 Vector3 terrainHeightContribution = ComputeLinearTerrainHeightCorrection(pTimestep);
856
857 Vector3 hoverContribution = ComputeLinearHover(pTimestep);
858
859 ComputeLinearBlockingEndPoint(pTimestep);
860
861 Vector3 limitMotorUpContribution = ComputeLinearMotorUp(pTimestep);
862
863 // ==================================================================
864 Vector3 newVelocity = linearMotorContribution
865 + terrainHeightContribution
866 + hoverContribution
867 + limitMotorUpContribution;
868
869 Vector3 newForce = buoyancyContribution;
870
871 // If not changing some axis, reduce out velocity
872 if ((m_flags & (VehicleFlag.NO_X)) != 0)
873 newVelocity.X = 0;
874 if ((m_flags & (VehicleFlag.NO_Y)) != 0)
875 newVelocity.Y = 0;
876 if ((m_flags & (VehicleFlag.NO_Z)) != 0)
877 newVelocity.Z = 0;
878
879 // ==================================================================
880 // Clamp high or low velocities
881 float newVelocityLengthSq = newVelocity.LengthSquared();
882 if (newVelocityLengthSq > 1000f)
883 {
884 newVelocity /= newVelocity.Length();
885 newVelocity *= 1000f;
886 }
887 else if (newVelocityLengthSq < 0.001f)
888 newVelocity = Vector3.Zero;
889
890 // ==================================================================
891 // Stuff new linear velocity into the vehicle.
892 // Since the velocity is just being set, it is not scaled by pTimeStep. Bullet will do that for us.
893 VehicleVelocity = newVelocity;
894
895 // Other linear forces are applied as forces.
896 Vector3 totalDownForce = newForce * m_vehicleMass;
897 if (!totalDownForce.ApproxEquals(Vector3.Zero, 0.01f))
898 {
899 VehicleAddForce(totalDownForce);
900 }
901
902 VDetailLog("{0}, MoveLinear,done,newVel={1},totDown={2},IsColliding={3}",
903 Prim.LocalID, newVelocity, totalDownForce, Prim.IsColliding);
904 VDetailLog("{0}, MoveLinear,done,linContrib={1},terrContrib={2},hoverContrib={3},limitContrib={4},buoyContrib={5}",
905 Prim.LocalID,
906 linearMotorContribution, terrainHeightContribution, hoverContribution,
907 limitMotorUpContribution, buoyancyContribution
908 );
909
910 } // end MoveLinear()
911
912 public Vector3 ComputeLinearTerrainHeightCorrection(float pTimestep)
913 {
914 Vector3 ret = Vector3.Zero;
915 // If below the terrain, move us above the ground a little.
916 // TODO: Consider taking the rotated size of the object or possibly casting a ray.
917 if (VehiclePosition.Z < GetTerrainHeight(VehiclePosition))
918 {
919 // TODO: correct position by applying force rather than forcing position.
920 Vector3 newPosition = VehiclePosition;
921 newPosition.Z = GetTerrainHeight(VehiclePosition) + 1f;
922 VehiclePosition = newPosition;
923 VDetailLog("{0}, MoveLinear,terrainHeight,terrainHeight={1},pos={2}",
924 Prim.LocalID, GetTerrainHeight(VehiclePosition), VehiclePosition);
925 }
926 return ret;
927 }
928
929 public Vector3 ComputeLinearHover(float pTimestep)
930 {
931 Vector3 ret = Vector3.Zero;
932
933 // m_VhoverEfficiency: 0=bouncy, 1=totally damped
934 // m_VhoverTimescale: time to achieve height
935 if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
936 {
937 // We should hover, get the target height
938 if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0)
939 {
940 m_VhoverTargetHeight = GetWaterLevel(VehiclePosition) + m_VhoverHeight;
941 }
942 if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
943 {
944 m_VhoverTargetHeight = GetTerrainHeight(VehiclePosition) + m_VhoverHeight;
945 }
946 if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
947 {
948 m_VhoverTargetHeight = m_VhoverHeight;
949 }
950
951 if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0)
952 {
953 // If body is already heigher, use its height as target height
954 if (VehiclePosition.Z > m_VhoverTargetHeight)
955 m_VhoverTargetHeight = VehiclePosition.Z;
956 }
957
958 if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0)
959 {
960 if (Math.Abs(VehiclePosition.Z - m_VhoverTargetHeight) > 0.2f)
961 {
962 Vector3 pos = VehiclePosition;
963 pos.Z = m_VhoverTargetHeight;
964 VehiclePosition = pos;
965 }
966 }
967 else
968 {
969 // Error is positive if below the target and negative if above.
970 float verticalError = m_VhoverTargetHeight - VehiclePosition.Z;
971 float verticalCorrectionVelocity = verticalError / m_VhoverTimescale;
972
973 // TODO: implement m_VhoverEfficiency correctly
974 if (Math.Abs(verticalError) > m_VhoverEfficiency)
975 {
976 ret = new Vector3(0f, 0f, verticalCorrectionVelocity);
977 }
978 }
979
980 VDetailLog("{0}, MoveLinear,hover,pos={1},ret={2},hoverTS={3},height={4},target={5}",
981 Prim.LocalID, VehiclePosition, ret, m_VhoverTimescale, m_VhoverHeight, m_VhoverTargetHeight);
982 }
983
984 return ret;
985 }
986
987 public bool ComputeLinearBlockingEndPoint(float pTimestep)
988 {
989 bool changed = false;
990
991 Vector3 pos = VehiclePosition;
992 Vector3 posChange = pos - m_lastPositionVector;
993 if (m_BlockingEndPoint != Vector3.Zero)
994 {
995 if (pos.X >= (m_BlockingEndPoint.X - (float)1))
996 {
997 pos.X -= posChange.X + 1;
998 changed = true;
999 }
1000 if (pos.Y >= (m_BlockingEndPoint.Y - (float)1))
1001 {
1002 pos.Y -= posChange.Y + 1;
1003 changed = true;
1004 }
1005 if (pos.Z >= (m_BlockingEndPoint.Z - (float)1))
1006 {
1007 pos.Z -= posChange.Z + 1;
1008 changed = true;
1009 }
1010 if (pos.X <= 0)
1011 {
1012 pos.X += posChange.X + 1;
1013 changed = true;
1014 }
1015 if (pos.Y <= 0)
1016 {
1017 pos.Y += posChange.Y + 1;
1018 changed = true;
1019 }
1020 if (changed)
1021 {
1022 VehiclePosition = pos;
1023 VDetailLog("{0}, MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}",
1024 Prim.LocalID, m_BlockingEndPoint, posChange, pos);
1025 }
1026 }
1027 return changed;
1028 }
1029
1030 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
1031 // Prevent ground vehicles from motoring into the sky. This flag has a subtle effect when
1032 // used with conjunction with banking: the strength of the banking will decay when the
1033 // vehicle no longer experiences collisions. The decay timescale is the same as
1034 // VEHICLE_BANKING_TIMESCALE. This is to help prevent ground vehicles from steering
1035 // when they are in mid jump.
1036 // TODO: this code is wrong. Also, what should it do for boats (height from water)?
1037 // This is just using the ground and a general collision check. Should really be using
1038 // a downward raycast to find what is below.
1039 public Vector3 ComputeLinearMotorUp(float pTimestep)
1040 {
1041 Vector3 ret = Vector3.Zero;
1042 float distanceAboveGround = 0f;
1043
1044 if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
1045 {
1046 float targetHeight = Type == Vehicle.TYPE_BOAT ? GetWaterLevel(VehiclePosition) : GetTerrainHeight(VehiclePosition);
1047 distanceAboveGround = VehiclePosition.Z - targetHeight;
1048 // Not colliding if the vehicle is off the ground
1049 if (!Prim.IsColliding)
1050 {
1051 // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale);
1052 ret = new Vector3(0, 0, -distanceAboveGround);
1053 }
1054 // TODO: this calculation is wrong. From the description at
1055 // (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce
1056 // has a decay factor. This says this force should
1057 // be computed with a motor.
1058 // TODO: add interaction with banking.
1059 }
1060 VDetailLog("{0}, MoveLinear,limitMotorUp,distAbove={1},colliding={2},ret={3}",
1061 Prim.LocalID, distanceAboveGround, Prim.IsColliding, ret);
1062 return ret;
1063 }
1064
1065 // =======================================================================
1066 // =======================================================================
1067 // Apply the effect of the angular motor.
1068 // The 'contribution' is how much angular correction velocity each function wants.
1069 // All the contributions are added together and the resulting velocity is
1070 // set directly on the vehicle.
1071 private void MoveAngular(float pTimestep)
1072 {
1073 // The user wants this many radians per second angular change?
1074 Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep);
1075
1076 // ==================================================================
1077 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
1078 // This flag prevents linear deflection parallel to world z-axis. This is useful
1079 // for preventing ground vehicles with large linear deflection, like bumper cars,
1080 // from climbing their linear deflection into the sky.
1081 // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement
1082 if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
1083 {
1084 angularMotorContribution.X = 0f;
1085 angularMotorContribution.Y = 0f;
1086 VDetailLog("{0}, MoveAngular,noDeflectionUp,angularMotorContrib={1}", Prim.LocalID, angularMotorContribution);
1087 }
1088
1089 Vector3 verticalAttractionContribution = ComputeAngularVerticalAttraction();
1090
1091 Vector3 deflectionContribution = ComputeAngularDeflection();
1092
1093 Vector3 bankingContribution = ComputeAngularBanking();
1094
1095 // ==================================================================
1096 m_lastVertAttractor = verticalAttractionContribution;
1097
1098 m_lastAngularVelocity = angularMotorContribution
1099 + verticalAttractionContribution
1100 + deflectionContribution
1101 + bankingContribution;
1102
1103 // ==================================================================
1104 // Apply the correction velocity.
1105 // TODO: Should this be applied as an angular force (torque)?
1106 if (!m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f))
1107 {
1108 VehicleRotationalVelocity = m_lastAngularVelocity;
1109
1110 VDetailLog("{0}, MoveAngular,done,nonZero,angMotorContrib={1},vertAttrContrib={2},bankContrib={3},deflectContrib={4},totalContrib={5}",
1111 Prim.LocalID,
1112 angularMotorContribution, verticalAttractionContribution,
1113 bankingContribution, deflectionContribution,
1114 m_lastAngularVelocity
1115 );
1116 }
1117 else
1118 {
1119 // The vehicle is not adding anything angular wise.
1120 VehicleRotationalVelocity = Vector3.Zero;
1121 VDetailLog("{0}, MoveAngular,done,zero", Prim.LocalID);
1122 }
1123
1124 // ==================================================================
1125 //Offset section
1126 if (m_linearMotorOffset != Vector3.Zero)
1127 {
1128 //Offset of linear velocity doesn't change the linear velocity,
1129 // but causes a torque to be applied, for example...
1130 //
1131 // IIIII >>> IIIII
1132 // IIIII >>> IIIII
1133 // IIIII >>> IIIII
1134 // ^
1135 // | Applying a force at the arrow will cause the object to move forward, but also rotate
1136 //
1137 //
1138 // The torque created is the linear velocity crossed with the offset
1139
1140 // TODO: this computation should be in the linear section
1141 // because that is where we know the impulse being applied.
1142 Vector3 torqueFromOffset = Vector3.Zero;
1143 // torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse);
1144 if (float.IsNaN(torqueFromOffset.X))
1145 torqueFromOffset.X = 0;
1146 if (float.IsNaN(torqueFromOffset.Y))
1147 torqueFromOffset.Y = 0;
1148 if (float.IsNaN(torqueFromOffset.Z))
1149 torqueFromOffset.Z = 0;
1150
1151 VehicleAddAngularForce(torqueFromOffset * m_vehicleMass);
1152 VDetailLog("{0}, BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset);
1153 }
1154
1155 }
1156 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
1157 // Some vehicles, like boats, should always keep their up-side up. This can be done by
1158 // enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to
1159 // the world z-axis (a.k.a. "up"). To take advantage of this feature you would set the
1160 // VEHICLE_VERTICAL_ATTRACTION_TIMESCALE to control the period of the spring frequency,
1161 // and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An
1162 // efficiency of 0.0 will cause the spring to wobble around its equilibrium, while an
1163 // efficiency of 1.0 will cause the spring to reach its equilibrium with exponential decay.
1164 public Vector3 ComputeAngularVerticalAttraction()
1165 {
1166 Vector3 ret = Vector3.Zero;
1167
1168 // If vertical attaction timescale is reasonable
1169 if (m_verticalAttractionTimescale < m_verticalAttractionCutoff)
1170 {
1171 // Take a vector pointing up and convert it from world to vehicle relative coords.
1172 Vector3 verticalError = Vector3.UnitZ * VehicleOrientation;
1173
1174 // If vertical attraction correction is needed, the vector that was pointing up (UnitZ)
1175 // is now:
1176 // leaning to one side: rotated around the X axis with the Y value going
1177 // from zero (nearly straight up) to one (completely to the side)) or
1178 // leaning front-to-back: rotated around the Y axis with the value of X being between
1179 // zero and one.
1180 // The value of Z is how far the rotation is off with 1 meaning none and 0 being 90 degrees.
1181
1182 // Y error means needed rotation around X axis and visa versa.
1183 // Since the error goes from zero to one, the asin is the corresponding angle.
1184 ret.X = (float)Math.Asin(verticalError.Y);
1185 // (Tilt forward (positive X) needs to tilt back (rotate negative) around Y axis.)
1186 ret.Y = -(float)Math.Asin(verticalError.X);
1187
1188 // If verticalError.Z is negative, the vehicle is upside down. Add additional push.
1189 if (verticalError.Z < 0f)
1190 {
1191 ret.X += PIOverFour;
1192 ret.Y += PIOverFour;
1193 }
1194
1195 // 'ret' is now the necessary velocity to correct tilt in one second.
1196 // Correction happens over a number of seconds.
1197 Vector3 unscaledContrib = ret;
1198 ret /= m_verticalAttractionTimescale;
1199
1200 VDetailLog("{0}, MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},eff={3},ts={4},vertAttr={5}",
1201 Prim.LocalID, verticalError, unscaledContrib, m_verticalAttractionEfficiency, m_verticalAttractionTimescale, ret);
1202 }
1203 return ret;
1204 }
1205
1206 // Return the angular correction to correct the direction the vehicle is pointing to be
1207 // the direction is should want to be pointing.
1208 // The vehicle is moving in some direction and correct its orientation to it is pointing
1209 // in that direction.
1210 // TODO: implement reference frame.
1211 public Vector3 ComputeAngularDeflection()
1212 {
1213 Vector3 ret = Vector3.Zero;
1214 return ret; // DEBUG DEBUG DEBUG
1215 // Disable angular deflection for the moment.
1216 // Since angularMotorUp and angularDeflection are computed independently, they will calculate
1217 // approximately the same X or Y correction. When added together (when contributions are combined)
1218 // this creates an over-correction and then wabbling as the target is overshot.
1219 // TODO: rethink how the different correction computations inter-relate.
1220
1221 if (m_angularDeflectionEfficiency != 0)
1222 {
1223 // The direction the vehicle is moving
1224 Vector3 movingDirection = VehicleVelocity;
1225 movingDirection.Normalize();
1226
1227 // The direction the vehicle is pointing
1228 Vector3 pointingDirection = Vector3.UnitX * VehicleOrientation;
1229 pointingDirection.Normalize();
1230
1231 // The difference between what is and what should be.
1232 Vector3 deflectionError = movingDirection - pointingDirection;
1233
1234 // Don't try to correct very large errors (not our job)
1235 if (Math.Abs(deflectionError.X) > PIOverFour) deflectionError.X = 0f;
1236 if (Math.Abs(deflectionError.Y) > PIOverFour) deflectionError.Y = 0f;
1237 if (Math.Abs(deflectionError.Z) > PIOverFour) deflectionError.Z = 0f;
1238
1239 // ret = m_angularDeflectionCorrectionMotor(1f, deflectionError);
1240
1241 // Scale the correction by recovery timescale and efficiency
1242 ret = (-deflectionError) * m_angularDeflectionEfficiency;
1243 ret /= m_angularDeflectionTimescale;
1244
1245 VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}",
1246 Prim.LocalID, movingDirection, pointingDirection, deflectionError, ret);
1247 VDetailLog("{0}, MoveAngular,Deflection,fwdSpd={1},defEff={2},defTS={3}",
1248 Prim.LocalID, VehicleForwardSpeed, m_angularDeflectionEfficiency, m_angularDeflectionTimescale);
1249 }
1250 return ret;
1251 }
1252
1253 // Return an angular change to rotate the vehicle around the Z axis when the vehicle
1254 // is tipped around the X axis.
1255 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
1256 // The vertical attractor feature must be enabled in order for the banking behavior to
1257 // function. The way banking works is this: a rotation around the vehicle's roll-axis will
1258 // produce a angular velocity around the yaw-axis, causing the vehicle to turn. The magnitude
1259 // of the yaw effect will be proportional to the
1260 // VEHICLE_BANKING_EFFICIENCY, the angle of the roll rotation, and sometimes the vehicle's
1261 // velocity along its preferred axis of motion.
1262 // The VEHICLE_BANKING_EFFICIENCY can vary between -1 and +1. When it is positive then any
1263 // positive rotation (by the right-hand rule) about the roll-axis will effect a
1264 // (negative) torque around the yaw-axis, making it turn to the right--that is the
1265 // vehicle will lean into the turn, which is how real airplanes and motorcycle's work.
1266 // Negating the banking coefficient will make it so that the vehicle leans to the
1267 // outside of the turn (not very "physical" but might allow interesting vehicles so why not?).
1268 // The VEHICLE_BANKING_MIX is a fake (i.e. non-physical) parameter that is useful for making
1269 // banking vehicles do what you want rather than what the laws of physics allow.
1270 // For example, consider a real motorcycle...it must be moving forward in order for
1271 // it to turn while banking, however video-game motorcycles are often configured
1272 // to turn in place when at a dead stop--because they are often easier to control
1273 // that way using the limited interface of the keyboard or game controller. The
1274 // VEHICLE_BANKING_MIX enables combinations of both realistic and non-realistic
1275 // banking by functioning as a slider between a banking that is correspondingly
1276 // totally static (0.0) and totally dynamic (1.0). By "static" we mean that the
1277 // banking effect depends only on the vehicle's rotation about its roll-axis compared
1278 // to "dynamic" where the banking is also proportional to its velocity along its
1279 // roll-axis. Finding the best value of the "mixture" will probably require trial and error.
1280 // The time it takes for the banking behavior to defeat a preexisting angular velocity about the
1281 // world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to
1282 // bank quickly then give it a banking timescale of about a second or less, otherwise you can
1283 // make a sluggish vehicle by giving it a timescale of several seconds.
1284 public Vector3 ComputeAngularBanking()
1285 {
1286 Vector3 ret = Vector3.Zero;
1287
1288 if (m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff)
1289 {
1290 // This works by rotating a unit vector to the orientation of the vehicle. The
1291 // roll (tilt) will be Y component of a tilting Z vector (zero for no tilt
1292 // up to one for full over).
1293 Vector3 rollComponents = Vector3.UnitZ * VehicleOrientation;
1294
1295 // Figure out the yaw value for this much roll.
1296 float turnComponent = rollComponents.Y * rollComponents.Y * m_bankingEfficiency;
1297 // Keep the sign
1298 if (rollComponents.Y < 0f)
1299 turnComponent = -turnComponent;
1300
1301 // TODO: there must be a better computation of the banking force.
1302 float bankingTurnForce = turnComponent;
1303
1304 // actual error = static turn error + dynamic turn error
1305 float mixedBankingError = bankingTurnForce * (1f - m_bankingMix) + bankingTurnForce * m_bankingMix * VehicleForwardSpeed;
1306 // TODO: the banking effect should not go to infinity but what to limit it to?
1307 mixedBankingError = ClampInRange(-20f, mixedBankingError, 20f);
1308
1309 // Build the force vector to change rotation from what it is to what it should be
1310 ret.Z = -mixedBankingError;
1311
1312 // Don't do it all at once.
1313 ret /= m_bankingTimescale;
1314
1315 VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},turnComp={3},bankErr={4},mixedBankErr={5},ret={6}",
1316 Prim.LocalID, rollComponents, VehicleForwardSpeed, turnComponent, bankingTurnForce, mixedBankingError, ret);
1317 }
1318 return ret;
1319 }
1320
1321 // This is from previous instantiations of XXXDynamics.cs.
1322 // Applies roll reference frame.
1323 // TODO: is this the right way to separate the code to do this operation?
1324 // Should this be in MoveAngular()?
1325 internal void LimitRotation(float timestep)
1326 {
1327 Quaternion rotq = VehicleOrientation;
1328 Quaternion m_rot = rotq;
1329 if (m_RollreferenceFrame != Quaternion.Identity)
1330 {
1331 if (rotq.X >= m_RollreferenceFrame.X)
1332 {
1333 m_rot.X = rotq.X - (m_RollreferenceFrame.X / 2);
1334 }
1335 if (rotq.Y >= m_RollreferenceFrame.Y)
1336 {
1337 m_rot.Y = rotq.Y - (m_RollreferenceFrame.Y / 2);
1338 }
1339 if (rotq.X <= -m_RollreferenceFrame.X)
1340 {
1341 m_rot.X = rotq.X + (m_RollreferenceFrame.X / 2);
1342 }
1343 if (rotq.Y <= -m_RollreferenceFrame.Y)
1344 {
1345 m_rot.Y = rotq.Y + (m_RollreferenceFrame.Y / 2);
1346 }
1347 }
1348 if ((m_flags & VehicleFlag.LOCK_ROTATION) != 0)
1349 {
1350 m_rot.X = 0;
1351 m_rot.Y = 0;
1352 }
1353 if (rotq != m_rot)
1354 {
1355 VehicleOrientation = m_rot;
1356 VDetailLog("{0}, LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot);
1357 }
1358
1359 }
1360
1361 private float ClampInRange(float low, float val, float high)
1362 {
1363 return Math.Max(low, Math.Min(val, high));
1364 // return Utils.Clamp(val, low, high);
1365 }
1366
1367 // Invoke the detailed logger and output something if it's enabled.
1368 private void VDetailLog(string msg, params Object[] args)
1369 {
1370 if (Prim.PhysicsScene.VehicleLoggingEnabled)
1371 Prim.PhysicsScene.DetailLog(msg, args);
1372 }
1373 }
1374}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSLinkset.cs
new file mode 100644
index 0000000..253128b
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSLinkset.cs
@@ -0,0 +1,333 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OMV = OpenMetaverse;
32
33namespace OpenSim.Region.Physics.BulletSNPlugin
34{
35
36// A BSPrim can get individual information about its linkedness attached
37// to it through an instance of a subclass of LinksetInfo.
38// Each type of linkset will define the information needed for its type.
39public abstract class BSLinksetInfo
40{
41 public virtual void Clear() { }
42}
43
44public abstract class BSLinkset
45{
46 // private static string LogHeader = "[BULLETSIM LINKSET]";
47
48 public enum LinksetImplementation
49 {
50 Constraint = 0, // linkset tied together with constraints
51 Compound = 1, // linkset tied together as a compound object
52 Manual = 2 // linkset tied together manually (code moves all the pieces)
53 }
54 // Create the correct type of linkset for this child
55 public static BSLinkset Factory(BSScene physScene, BSPhysObject parent)
56 {
57 BSLinkset ret = null;
58
59 switch ((int)BSParam.LinksetImplementation)
60 {
61 case (int)LinksetImplementation.Constraint:
62 ret = new BSLinksetConstraints(physScene, parent);
63 break;
64 case (int)LinksetImplementation.Compound:
65 ret = new BSLinksetCompound(physScene, parent);
66 break;
67 case (int)LinksetImplementation.Manual:
68 // ret = new BSLinksetManual(physScene, parent);
69 break;
70 default:
71 ret = new BSLinksetCompound(physScene, parent);
72 break;
73 }
74 return ret;
75 }
76
77 public BSPhysObject LinksetRoot { get; protected set; }
78
79 public BSScene PhysicsScene { get; private set; }
80
81 static int m_nextLinksetID = 1;
82 public int LinksetID { get; private set; }
83
84 // The children under the root in this linkset.
85 protected HashSet<BSPhysObject> m_children;
86
87 // We lock the diddling of linkset classes to prevent any badness.
88 // This locks the modification of the instances of this class. Changes
89 // to the physical representation is done via the tainting mechenism.
90 protected object m_linksetActivityLock = new Object();
91
92 // Some linksets have a preferred physical shape.
93 // Returns SHAPE_UNKNOWN if there is no preference. Causes the correct shape to be selected.
94 public virtual BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
95 {
96 return BSPhysicsShapeType.SHAPE_UNKNOWN;
97 }
98
99 // We keep the prim's mass in the linkset structure since it could be dependent on other prims
100 protected float m_mass;
101 public float LinksetMass
102 {
103 get
104 {
105 return m_mass;
106 }
107 }
108
109 public virtual bool LinksetIsColliding { get { return false; } }
110
111 public OMV.Vector3 CenterOfMass
112 {
113 get { return ComputeLinksetCenterOfMass(); }
114 }
115
116 public OMV.Vector3 GeometricCenter
117 {
118 get { return ComputeLinksetGeometricCenter(); }
119 }
120
121 protected BSLinkset(BSScene scene, BSPhysObject parent)
122 {
123 // A simple linkset of one (no children)
124 LinksetID = m_nextLinksetID++;
125 // We create LOTS of linksets.
126 if (m_nextLinksetID <= 0)
127 m_nextLinksetID = 1;
128 PhysicsScene = scene;
129 LinksetRoot = parent;
130 m_children = new HashSet<BSPhysObject>();
131 m_mass = parent.RawMass;
132 Rebuilding = false;
133 }
134
135 // Link to a linkset where the child knows the parent.
136 // Parent changing should not happen so do some sanity checking.
137 // We return the parent's linkset so the child can track its membership.
138 // Called at runtime.
139 public BSLinkset AddMeToLinkset(BSPhysObject child)
140 {
141 lock (m_linksetActivityLock)
142 {
143 // Don't add the root to its own linkset
144 if (!IsRoot(child))
145 AddChildToLinkset(child);
146 m_mass = ComputeLinksetMass();
147 }
148 return this;
149 }
150
151 // Remove a child from a linkset.
152 // Returns a new linkset for the child which is a linkset of one (just the
153 // orphened child).
154 // Called at runtime.
155 public BSLinkset RemoveMeFromLinkset(BSPhysObject child)
156 {
157 lock (m_linksetActivityLock)
158 {
159 if (IsRoot(child))
160 {
161 // Cannot remove the root from a linkset.
162 return this;
163 }
164 RemoveChildFromLinkset(child);
165 m_mass = ComputeLinksetMass();
166 }
167
168 // The child is down to a linkset of just itself
169 return BSLinkset.Factory(PhysicsScene, child);
170 }
171
172 // Return 'true' if the passed object is the root object of this linkset
173 public bool IsRoot(BSPhysObject requestor)
174 {
175 return (requestor.LocalID == LinksetRoot.LocalID);
176 }
177
178 public int NumberOfChildren { get { return m_children.Count; } }
179
180 // Return 'true' if this linkset has any children (more than the root member)
181 public bool HasAnyChildren { get { return (m_children.Count > 0); } }
182
183 // Return 'true' if this child is in this linkset
184 public bool HasChild(BSPhysObject child)
185 {
186 bool ret = false;
187 lock (m_linksetActivityLock)
188 {
189 ret = m_children.Contains(child);
190 /* Safer version but the above should work
191 foreach (BSPhysObject bp in m_children)
192 {
193 if (child.LocalID == bp.LocalID)
194 {
195 ret = true;
196 break;
197 }
198 }
199 */
200 }
201 return ret;
202 }
203
204 // Perform an action on each member of the linkset including root prim.
205 // Depends on the action on whether this should be done at taint time.
206 public delegate bool ForEachMemberAction(BSPhysObject obj);
207 public virtual bool ForEachMember(ForEachMemberAction action)
208 {
209 bool ret = false;
210 lock (m_linksetActivityLock)
211 {
212 action(LinksetRoot);
213 foreach (BSPhysObject po in m_children)
214 {
215 if (action(po))
216 break;
217 }
218 }
219 return ret;
220 }
221
222 // I am the root of a linkset and a new child is being added
223 // Called while LinkActivity is locked.
224 protected abstract void AddChildToLinkset(BSPhysObject child);
225
226 // I am the root of a linkset and one of my children is being removed.
227 // Safe to call even if the child is not really in my linkset.
228 protected abstract void RemoveChildFromLinkset(BSPhysObject child);
229
230 // When physical properties are changed the linkset needs to recalculate
231 // its internal properties.
232 // May be called at runtime or taint-time.
233 public abstract void Refresh(BSPhysObject requestor);
234
235 // Flag denoting the linkset is in the process of being rebuilt.
236 // Used to know not the schedule a rebuild in the middle of a rebuild.
237 protected bool Rebuilding { get; set; }
238
239 // The object is going dynamic (physical). Do any setup necessary
240 // for a dynamic linkset.
241 // Only the state of the passed object can be modified. The rest of the linkset
242 // has not yet been fully constructed.
243 // Return 'true' if any properties updated on the passed object.
244 // Called at taint-time!
245 public abstract bool MakeDynamic(BSPhysObject child);
246
247 // The object is going static (non-physical). Do any setup necessary
248 // for a static linkset.
249 // Return 'true' if any properties updated on the passed object.
250 // Called at taint-time!
251 public abstract bool MakeStatic(BSPhysObject child);
252
253 // Called when a parameter update comes from the physics engine for any object
254 // of the linkset is received.
255 // Passed flag is update came from physics engine (true) or the user (false).
256 // Called at taint-time!!
257 public abstract void UpdateProperties(BSPhysObject physObject, bool physicalUpdate);
258
259 // Routine used when rebuilding the body of the root of the linkset
260 // Destroy all the constraints have have been made to root.
261 // This is called when the root body is changing.
262 // Returns 'true' of something was actually removed and would need restoring
263 // Called at taint-time!!
264 public abstract bool RemoveBodyDependencies(BSPrim child);
265
266 // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
267 // this routine will restore the removed constraints.
268 // Called at taint-time!!
269 public abstract void RestoreBodyDependencies(BSPrim child);
270
271 // ================================================================
272 protected virtual float ComputeLinksetMass()
273 {
274 float mass = LinksetRoot.RawMass;
275 if (HasAnyChildren)
276 {
277 lock (m_linksetActivityLock)
278 {
279 foreach (BSPhysObject bp in m_children)
280 {
281 mass += bp.RawMass;
282 }
283 }
284 }
285 return mass;
286 }
287
288 protected virtual OMV.Vector3 ComputeLinksetCenterOfMass()
289 {
290 OMV.Vector3 com;
291 lock (m_linksetActivityLock)
292 {
293 com = LinksetRoot.Position * LinksetRoot.RawMass;
294 float totalMass = LinksetRoot.RawMass;
295
296 foreach (BSPhysObject bp in m_children)
297 {
298 com += bp.Position * bp.RawMass;
299 totalMass += bp.RawMass;
300 }
301 if (totalMass != 0f)
302 com /= totalMass;
303 }
304
305 return com;
306 }
307
308 protected virtual OMV.Vector3 ComputeLinksetGeometricCenter()
309 {
310 OMV.Vector3 com;
311 lock (m_linksetActivityLock)
312 {
313 com = LinksetRoot.Position;
314
315 foreach (BSPhysObject bp in m_children)
316 {
317 com += bp.Position * bp.RawMass;
318 }
319 com /= (m_children.Count + 1);
320 }
321
322 return com;
323 }
324
325 // Invoke the detailed logger and output something if it's enabled.
326 protected void DetailLog(string msg, params Object[] args)
327 {
328 if (PhysicsScene.PhysicsLogging.Enabled)
329 PhysicsScene.DetailLog(msg, args);
330 }
331
332}
333}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs
new file mode 100644
index 0000000..23a0b8b
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs
@@ -0,0 +1,396 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OpenSim.Framework;
32
33using OMV = OpenMetaverse;
34
35namespace OpenSim.Region.Physics.BulletSNPlugin
36{
37
38// When a child is linked, the relationship position of the child to the parent
39// is remembered so the child's world position can be recomputed when it is
40// removed from the linkset.
41sealed class BSLinksetCompoundInfo : BSLinksetInfo
42{
43 public OMV.Vector3 OffsetPos;
44 public OMV.Quaternion OffsetRot;
45 public BSLinksetCompoundInfo(OMV.Vector3 p, OMV.Quaternion r)
46 {
47 OffsetPos = p;
48 OffsetRot = r;
49 }
50 public override void Clear()
51 {
52 OffsetPos = OMV.Vector3.Zero;
53 OffsetRot = OMV.Quaternion.Identity;
54 }
55 public override string ToString()
56 {
57 StringBuilder buff = new StringBuilder();
58 buff.Append("<p=");
59 buff.Append(OffsetPos.ToString());
60 buff.Append(",r=");
61 buff.Append(OffsetRot.ToString());
62 buff.Append(">");
63 return buff.ToString();
64 }
65};
66
67public sealed class BSLinksetCompound : BSLinkset
68{
69 private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]";
70
71 public BSLinksetCompound(BSScene scene, BSPhysObject parent) : base(scene, parent)
72 {
73 }
74
75 // For compound implimented linksets, if there are children, use compound shape for the root.
76 public override BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
77 {
78 // Returning 'unknown' means we don't have a preference.
79 BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN;
80 if (IsRoot(requestor) && HasAnyChildren)
81 {
82 ret = BSPhysicsShapeType.SHAPE_COMPOUND;
83 }
84 // DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret);
85 return ret;
86 }
87
88 // When physical properties are changed the linkset needs to recalculate
89 // its internal properties.
90 public override void Refresh(BSPhysObject requestor)
91 {
92 // Something changed so do the rebuilding thing
93 // ScheduleRebuild();
94 }
95
96 // Schedule a refresh to happen after all the other taint processing.
97 private void ScheduleRebuild(BSPhysObject requestor)
98 {
99 DetailLog("{0},BSLinksetCompound.Refresh,schedulingRefresh,rebuilding={1}",
100 requestor.LocalID, Rebuilding);
101 // When rebuilding, it is possible to set properties that would normally require a rebuild.
102 // If already rebuilding, don't request another rebuild.
103 if (!Rebuilding)
104 {
105 PhysicsScene.PostTaintObject("BSLinksetCompound.Refresh", LinksetRoot.LocalID, delegate()
106 {
107 if (HasAnyChildren)
108 RecomputeLinksetCompound();
109 });
110 }
111 }
112
113 // The object is going dynamic (physical). Do any setup necessary
114 // for a dynamic linkset.
115 // Only the state of the passed object can be modified. The rest of the linkset
116 // has not yet been fully constructed.
117 // Return 'true' if any properties updated on the passed object.
118 // Called at taint-time!
119 public override bool MakeDynamic(BSPhysObject child)
120 {
121 bool ret = false;
122 DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child));
123 if (IsRoot(child))
124 {
125 // The root is going dynamic. Make sure mass is properly set.
126 m_mass = ComputeLinksetMass();
127 ScheduleRebuild(LinksetRoot);
128 }
129 else
130 {
131 // The origional prims are removed from the world as the shape of the root compound
132 // shape takes over.
133 BulletSimAPI.AddToCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
134 BulletSimAPI.ForceActivationState2(child.PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
135 // We don't want collisions from the old linkset children.
136 BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
137
138 child.PhysBody.collisionType = CollisionType.LinksetChild;
139
140 ret = true;
141 }
142 return ret;
143 }
144
145 // The object is going static (non-physical). Do any setup necessary for a static linkset.
146 // Return 'true' if any properties updated on the passed object.
147 // This doesn't normally happen -- OpenSim removes the objects from the physical
148 // world if it is a static linkset.
149 // Called at taint-time!
150 public override bool MakeStatic(BSPhysObject child)
151 {
152 bool ret = false;
153 DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child));
154 if (IsRoot(child))
155 {
156 ScheduleRebuild(LinksetRoot);
157 }
158 else
159 {
160 // The non-physical children can come back to life.
161 BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
162
163 child.PhysBody.collisionType = CollisionType.LinksetChild;
164
165 // Don't force activation so setting of DISABLE_SIMULATION can stay if used.
166 BulletSimAPI.Activate2(child.PhysBody.ptr, false);
167 ret = true;
168 }
169 return ret;
170 }
171
172 public override void UpdateProperties(BSPhysObject updated, bool physicalUpdate)
173 {
174 // The user moving a child around requires the rebuilding of the linkset compound shape
175 // One problem is this happens when a border is crossed -- the simulator implementation
176 // is to store the position into the group which causes the move of the object
177 // but it also means all the child positions get updated.
178 // What would cause an unnecessary rebuild so we make sure the linkset is in a
179 // region before bothering to do a rebuild.
180 if (!IsRoot(updated)
181 && !physicalUpdate
182 && PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
183 {
184 updated.LinksetInfo = null;
185 ScheduleRebuild(updated);
186 }
187 }
188
189 // Routine called when rebuilding the body of some member of the linkset.
190 // Since we don't keep in world relationships, do nothing unless it's a child changing.
191 // Returns 'true' of something was actually removed and would need restoring
192 // Called at taint-time!!
193 public override bool RemoveBodyDependencies(BSPrim child)
194 {
195 bool ret = false;
196
197 DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}",
198 child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(), IsRoot(child));
199
200 if (!IsRoot(child))
201 {
202 // Because it is a convenient time, recompute child world position and rotation based on
203 // its position in the linkset.
204 RecomputeChildWorldPosition(child, true);
205 }
206
207 // Cannot schedule a refresh/rebuild here because this routine is called when
208 // the linkset is being rebuilt.
209 // InternalRefresh(LinksetRoot);
210
211 return ret;
212 }
213
214 // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
215 // this routine will restore the removed constraints.
216 // Called at taint-time!!
217 public override void RestoreBodyDependencies(BSPrim child)
218 {
219 }
220
221 // When the linkset is built, the child shape is added to the compound shape relative to the
222 // root shape. The linkset then moves around but this does not move the actual child
223 // prim. The child prim's location must be recomputed based on the location of the root shape.
224 private void RecomputeChildWorldPosition(BSPhysObject child, bool inTaintTime)
225 {
226 BSLinksetCompoundInfo lci = child.LinksetInfo as BSLinksetCompoundInfo;
227 if (lci != null)
228 {
229 if (inTaintTime)
230 {
231 OMV.Vector3 oldPos = child.RawPosition;
232 child.ForcePosition = LinksetRoot.RawPosition + lci.OffsetPos;
233 child.ForceOrientation = LinksetRoot.RawOrientation * lci.OffsetRot;
234 DetailLog("{0},BSLinksetCompound.RecomputeChildWorldPosition,oldPos={1},lci={2},newPos={3}",
235 child.LocalID, oldPos, lci, child.RawPosition);
236 }
237 else
238 {
239 // TaintedObject is not used here so the raw position is set now and not at taint-time.
240 child.Position = LinksetRoot.RawPosition + lci.OffsetPos;
241 child.Orientation = LinksetRoot.RawOrientation * lci.OffsetRot;
242 }
243 }
244 else
245 {
246 // This happens when children have been added to the linkset but the linkset
247 // has not been constructed yet. So like, at taint time, adding children to a linkset
248 // and then changing properties of the children (makePhysical, for instance)
249 // but the post-print action of actually rebuilding the linkset has not yet happened.
250 // PhysicsScene.Logger.WarnFormat("{0} Restoring linkset child position failed because of no relative position computed. ID={1}",
251 // LogHeader, child.LocalID);
252 DetailLog("{0},BSLinksetCompound.recomputeChildWorldPosition,noRelativePositonInfo", child.LocalID);
253 }
254 }
255
256 // ================================================================
257
258 // Add a new child to the linkset.
259 // Called while LinkActivity is locked.
260 protected override void AddChildToLinkset(BSPhysObject child)
261 {
262 if (!HasChild(child))
263 {
264 m_children.Add(child);
265
266 DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
267
268 // Rebuild the compound shape with the new child shape included
269 ScheduleRebuild(child);
270 }
271 return;
272 }
273
274 // Remove the specified child from the linkset.
275 // Safe to call even if the child is not really in the linkset.
276 protected override void RemoveChildFromLinkset(BSPhysObject child)
277 {
278 if (m_children.Remove(child))
279 {
280 DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
281 child.LocalID,
282 LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(),
283 child.LocalID, child.PhysBody.ptr.ToString());
284
285 // Cause the child's body to be rebuilt and thus restored to normal operation
286 RecomputeChildWorldPosition(child, false);
287 child.ForceBodyShapeRebuild(false);
288
289 if (!HasAnyChildren)
290 {
291 // The linkset is now empty. The root needs rebuilding.
292 LinksetRoot.ForceBodyShapeRebuild(false);
293 }
294 else
295 {
296 // Rebuild the compound shape with the child removed
297 ScheduleRebuild(child);
298 }
299 }
300 return;
301 }
302
303 // Called before the simulation step to make sure the compound based linkset
304 // is all initialized.
305 // Constraint linksets are rebuilt every time.
306 // Note that this works for rebuilding just the root after a linkset is taken apart.
307 // Called at taint time!!
308 private void RecomputeLinksetCompound()
309 {
310 try
311 {
312 // Suppress rebuilding while rebuilding
313 Rebuilding = true;
314
315 // Cause the root shape to be rebuilt as a compound object with just the root in it
316 LinksetRoot.ForceBodyShapeRebuild(true);
317
318 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}",
319 LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren);
320
321 // Add a shape for each of the other children in the linkset
322 ForEachMember(delegate(BSPhysObject cPrim)
323 {
324 if (!IsRoot(cPrim))
325 {
326 // Compute the displacement of the child from the root of the linkset.
327 // This info is saved in the child prim so the relationship does not
328 // change over time and the new child position can be computed
329 // when the linkset is being disassembled (the linkset may have moved).
330 BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo;
331 if (lci == null)
332 {
333 // Each child position and rotation is given relative to the root.
334 OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
335 OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation;
336 OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation;
337
338 // Save relative position for recomputing child's world position after moving linkset.
339 lci = new BSLinksetCompoundInfo(displacementPos, displacementRot);
340 cPrim.LinksetInfo = lci;
341 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci);
342 }
343
344 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}",
345 LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, lci.OffsetPos, lci.OffsetRot);
346
347 if (cPrim.PhysShape.isNativeShape)
348 {
349 // A native shape is turning into a hull collision shape because native
350 // shapes are not shared so we have to hullify it so it will be tracked
351 // and freed at the correct time. This also solves the scaling problem
352 // (native shapes scaled but hull/meshes are assumed to not be).
353 // TODO: decide of the native shape can just be used in the compound shape.
354 // Use call to CreateGeomNonSpecial().
355 BulletShape saveShape = cPrim.PhysShape;
356 cPrim.PhysShape.Clear(); // Don't let the create free the child's shape
357 // PhysicsScene.Shapes.CreateGeomNonSpecial(true, cPrim, null);
358 PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null);
359 BulletShape newShape = cPrim.PhysShape;
360 cPrim.PhysShape = saveShape;
361 BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, lci.OffsetPos, lci.OffsetRot);
362 }
363 else
364 {
365 // For the shared shapes (meshes and hulls), just use the shape in the child.
366 // The reference count added here will be decremented when the compound shape
367 // is destroyed in BSShapeCollection (the child shapes are looped over and dereferenced).
368 if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape))
369 {
370 PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}",
371 LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape);
372 }
373 BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, lci.OffsetPos, lci.OffsetRot);
374 }
375 }
376 return false; // 'false' says to move onto the next child in the list
377 });
378
379 // With all of the linkset packed into the root prim, it has the mass of everyone.
380 float linksetMass = LinksetMass;
381 LinksetRoot.UpdatePhysicalMassProperties(linksetMass);
382 }
383 finally
384 {
385 Rebuilding = false;
386 }
387
388 BulletSimAPI.RecalculateCompoundShapeLocalAabb2(LinksetRoot.PhysShape.ptr);
389
390 // DEBUG: see of inter-linkset collisions are causing problems for constraint linksets.
391 // BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
392 // (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
393
394 }
395}
396} \ No newline at end of file
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetConstraints.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetConstraints.cs
new file mode 100644
index 0000000..5757e64
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetConstraints.cs
@@ -0,0 +1,314 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OMV = OpenMetaverse;
32
33namespace OpenSim.Region.Physics.BulletSNPlugin
34{
35public sealed class BSLinksetConstraints : BSLinkset
36{
37 // private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]";
38
39 public BSLinksetConstraints(BSScene scene, BSPhysObject parent) : base(scene, parent)
40 {
41 }
42
43 // When physical properties are changed the linkset needs to recalculate
44 // its internal properties.
45 // This is queued in the 'post taint' queue so the
46 // refresh will happen once after all the other taints are applied.
47 public override void Refresh(BSPhysObject requestor)
48 {
49 // Queue to happen after all the other taint processing
50 PhysicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate()
51 {
52 if (HasAnyChildren && IsRoot(requestor))
53 RecomputeLinksetConstraints();
54 });
55 }
56
57 // The object is going dynamic (physical). Do any setup necessary
58 // for a dynamic linkset.
59 // Only the state of the passed object can be modified. The rest of the linkset
60 // has not yet been fully constructed.
61 // Return 'true' if any properties updated on the passed object.
62 // Called at taint-time!
63 public override bool MakeDynamic(BSPhysObject child)
64 {
65 // What is done for each object in BSPrim is what we want.
66 return false;
67 }
68
69 // The object is going static (non-physical). Do any setup necessary for a static linkset.
70 // Return 'true' if any properties updated on the passed object.
71 // This doesn't normally happen -- OpenSim removes the objects from the physical
72 // world if it is a static linkset.
73 // Called at taint-time!
74 public override bool MakeStatic(BSPhysObject child)
75 {
76 // What is done for each object in BSPrim is what we want.
77 return false;
78 }
79
80 // Called at taint-time!!
81 public override void UpdateProperties(BSPhysObject updated, bool inTaintTime)
82 {
83 // Nothing to do for constraints on property updates
84 }
85
86 // Routine called when rebuilding the body of some member of the linkset.
87 // Destroy all the constraints have have been made to root and set
88 // up to rebuild the constraints before the next simulation step.
89 // Returns 'true' of something was actually removed and would need restoring
90 // Called at taint-time!!
91 public override bool RemoveBodyDependencies(BSPrim child)
92 {
93 bool ret = false;
94
95 DetailLog("{0},BSLinksetConstraint.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}",
96 child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString());
97
98 lock (m_linksetActivityLock)
99 {
100 // Just undo all the constraints for this linkset. Rebuild at the end of the step.
101 ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot);
102 // Cause the constraints, et al to be rebuilt before the next simulation step.
103 Refresh(LinksetRoot);
104 }
105 return ret;
106 }
107
108 // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
109 // this routine will restore the removed constraints.
110 // Called at taint-time!!
111 public override void RestoreBodyDependencies(BSPrim child)
112 {
113 // The Refresh operation queued by RemoveBodyDependencies() will build any missing constraints.
114 }
115
116 // ================================================================
117
118 // Add a new child to the linkset.
119 // Called while LinkActivity is locked.
120 protected override void AddChildToLinkset(BSPhysObject child)
121 {
122 if (!HasChild(child))
123 {
124 m_children.Add(child);
125
126 DetailLog("{0},BSLinksetConstraints.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
127
128 // Cause constraints and assorted properties to be recomputed before the next simulation step.
129 Refresh(LinksetRoot);
130 }
131 return;
132 }
133
134 // Remove the specified child from the linkset.
135 // Safe to call even if the child is not really in my linkset.
136 protected override void RemoveChildFromLinkset(BSPhysObject child)
137 {
138 if (m_children.Remove(child))
139 {
140 BSPhysObject rootx = LinksetRoot; // capture the root and body as of now
141 BSPhysObject childx = child;
142
143 DetailLog("{0},BSLinksetConstraints.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
144 childx.LocalID,
145 rootx.LocalID, rootx.PhysBody.ptr.ToString(),
146 childx.LocalID, childx.PhysBody.ptr.ToString());
147
148 PhysicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate()
149 {
150 PhysicallyUnlinkAChildFromRoot(rootx, childx);
151 });
152 // See that the linkset parameters are recomputed at the end of the taint time.
153 Refresh(LinksetRoot);
154 }
155 else
156 {
157 // Non-fatal occurance.
158 // PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader);
159 }
160 return;
161 }
162
163 // Create a constraint between me (root of linkset) and the passed prim (the child).
164 // Called at taint time!
165 private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
166 {
167 // Don't build the constraint when asked. Put it off until just before the simulation step.
168 Refresh(rootPrim);
169 }
170
171 private BSConstraint BuildConstraint(BSPhysObject rootPrim, BSPhysObject childPrim)
172 {
173 // Zero motion for children so they don't interpolate
174 childPrim.ZeroMotion(true);
175
176 // Relative position normalized to the root prim
177 // Essentually a vector pointing from center of rootPrim to center of childPrim
178 OMV.Vector3 childRelativePosition = childPrim.Position - rootPrim.Position;
179
180 // real world coordinate of midpoint between the two objects
181 OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2);
182
183 DetailLog("{0},BSLinksetConstraint.BuildConstraint,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}",
184 rootPrim.LocalID,
185 rootPrim.LocalID, rootPrim.PhysBody.ptr.ToString(),
186 childPrim.LocalID, childPrim.PhysBody.ptr.ToString(),
187 rootPrim.Position, childPrim.Position, midPoint);
188
189 // create a constraint that allows no freedom of movement between the two objects
190 // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
191
192 BSConstraint6Dof constrain = new BSConstraint6Dof(
193 PhysicsScene.World, rootPrim.PhysBody, childPrim.PhysBody, midPoint, true, true );
194 // PhysicsScene.World, childPrim.BSBody, rootPrim.BSBody, midPoint, true, true );
195
196 /* NOTE: below is an attempt to build constraint with full frame computation, etc.
197 * Using the midpoint is easier since it lets the Bullet code manipulate the transforms
198 * of the objects.
199 * Code left for future programmers.
200 // ==================================================================================
201 // relative position normalized to the root prim
202 OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation);
203 OMV.Vector3 childRelativePosition = (childPrim.Position - rootPrim.Position) * invThisOrientation;
204
205 // relative rotation of the child to the parent
206 OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation;
207 OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation);
208
209 DetailLog("{0},BSLinksetConstraint.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID);
210 BS6DofConstraint constrain = new BS6DofConstraint(
211 PhysicsScene.World, rootPrim.Body, childPrim.Body,
212 OMV.Vector3.Zero,
213 OMV.Quaternion.Inverse(rootPrim.Orientation),
214 OMV.Vector3.Zero,
215 OMV.Quaternion.Inverse(childPrim.Orientation),
216 true,
217 true
218 );
219 // ==================================================================================
220 */
221
222 PhysicsScene.Constraints.AddConstraint(constrain);
223
224 // zero linear and angular limits makes the objects unable to move in relation to each other
225 constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
226 constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
227
228 // tweek the constraint to increase stability
229 constrain.UseFrameOffset(BSParam.BoolNumeric(BSParam.LinkConstraintUseFrameOffset));
230 constrain.TranslationalLimitMotor(BSParam.BoolNumeric(BSParam.LinkConstraintEnableTransMotor),
231 BSParam.LinkConstraintTransMotorMaxVel,
232 BSParam.LinkConstraintTransMotorMaxForce);
233 constrain.SetCFMAndERP(BSParam.LinkConstraintCFM, BSParam.LinkConstraintERP);
234 if (BSParam.LinkConstraintSolverIterations != 0f)
235 {
236 constrain.SetSolverIterations(BSParam.LinkConstraintSolverIterations);
237 }
238 return constrain;
239 }
240
241 // Remove linkage between the linkset root and a particular child
242 // The root and child bodies are passed in because we need to remove the constraint between
243 // the bodies that were present at unlink time.
244 // Called at taint time!
245 private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
246 {
247 bool ret = false;
248 DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}",
249 rootPrim.LocalID,
250 rootPrim.LocalID, rootPrim.PhysBody.ptr.ToString(),
251 childPrim.LocalID, childPrim.PhysBody.ptr.ToString());
252
253 // Find the constraint for this link and get rid of it from the overall collection and from my list
254 if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody))
255 {
256 // Make the child refresh its location
257 BulletSimAPI.PushUpdate2(childPrim.PhysBody.ptr);
258 ret = true;
259 }
260
261 return ret;
262 }
263
264 // Remove linkage between myself and any possible children I might have.
265 // Returns 'true' of any constraints were destroyed.
266 // Called at taint time!
267 private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim)
268 {
269 DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID);
270
271 return PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody);
272 }
273
274 // Call each of the constraints that make up this linkset and recompute the
275 // various transforms and variables. Create constraints of not created yet.
276 // Called before the simulation step to make sure the constraint based linkset
277 // is all initialized.
278 // Called at taint time!!
279 private void RecomputeLinksetConstraints()
280 {
281 float linksetMass = LinksetMass;
282 LinksetRoot.UpdatePhysicalMassProperties(linksetMass);
283
284 // DEBUG: see of inter-linkset collisions are causing problems
285 // BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
286 // (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
287 DetailLog("{0},BSLinksetConstraint.RecomputeLinksetConstraints,set,rBody={1},linksetMass={2}",
288 LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(), linksetMass);
289
290 foreach (BSPhysObject child in m_children)
291 {
292 // A child in the linkset physically shows the mass of the whole linkset.
293 // This allows Bullet to apply enough force on the child to move the whole linkset.
294 // (Also do the mass stuff before recomputing the constraint so mass is not zero.)
295 child.UpdatePhysicalMassProperties(linksetMass);
296
297 BSConstraint constrain;
298 if (!PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain))
299 {
300 // If constraint doesn't exist yet, create it.
301 constrain = BuildConstraint(LinksetRoot, child);
302 }
303 constrain.RecomputeConstraintVariables(linksetMass);
304
305 // DEBUG: see of inter-linkset collisions are causing problems
306 // BulletSimAPI.SetCollisionFilterMask2(child.BSBody.ptr,
307 // (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
308
309 // BulletSimAPI.DumpConstraint2(PhysicsScene.World.ptr, constrain.Constraint.ptr); // DEBUG DEBUG
310 }
311
312 }
313}
314}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSMaterials.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSMaterials.cs
new file mode 100644
index 0000000..732191f
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSMaterials.cs
@@ -0,0 +1,200 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using System.Reflection;
31using Nini.Config;
32
33namespace OpenSim.Region.Physics.BulletSNPlugin
34{
35
36public struct MaterialAttributes
37{
38 // Material type values that correspond with definitions for LSL
39 public enum Material : int
40 {
41 Stone = 0,
42 Metal,
43 Glass,
44 Wood,
45 Flesh,
46 Plastic,
47 Rubber,
48 Light,
49 // Hereafter are BulletSim additions
50 Avatar,
51 NumberOfTypes // the count of types in the enum.
52 }
53
54 // Names must be in the order of the above enum.
55 // These names must coorespond to the lower case field names in the MaterialAttributes
56 // structure as reflection is used to select the field to put the value in.
57 public static readonly string[] MaterialAttribs = { "Density", "Friction", "Restitution"};
58
59 public MaterialAttributes(string t, float d, float f, float r)
60 {
61 type = t;
62 density = d;
63 friction = f;
64 restitution = r;
65 }
66 public string type;
67 public float density;
68 public float friction;
69 public float restitution;
70}
71
72public static class BSMaterials
73{
74 // Attributes for each material type
75 private static readonly MaterialAttributes[] Attributes;
76
77 // Map of material name to material type code
78 public static readonly Dictionary<string, MaterialAttributes.Material> MaterialMap;
79
80 static BSMaterials()
81 {
82 // Attribute sets for both the non-physical and physical instances of materials.
83 Attributes = new MaterialAttributes[(int)MaterialAttributes.Material.NumberOfTypes * 2];
84
85 // Map of name to type code.
86 MaterialMap = new Dictionary<string, MaterialAttributes.Material>();
87 MaterialMap.Add("Stone", MaterialAttributes.Material.Stone);
88 MaterialMap.Add("Metal", MaterialAttributes.Material.Metal);
89 MaterialMap.Add("Glass", MaterialAttributes.Material.Glass);
90 MaterialMap.Add("Wood", MaterialAttributes.Material.Wood);
91 MaterialMap.Add("Flesh", MaterialAttributes.Material.Flesh);
92 MaterialMap.Add("Plastic", MaterialAttributes.Material.Plastic);
93 MaterialMap.Add("Rubber", MaterialAttributes.Material.Rubber);
94 MaterialMap.Add("Light", MaterialAttributes.Material.Light);
95 MaterialMap.Add("Avatar", MaterialAttributes.Material.Avatar);
96 }
97
98 // This is where all the default material attributes are defined.
99 public static void InitializeFromDefaults(ConfigurationParameters parms)
100 {
101 // Values from http://wiki.secondlife.com/wiki/PRIM_MATERIAL
102 float dDensity = parms.defaultDensity;
103 float dFriction = parms.defaultFriction;
104 float dRestitution = parms.defaultRestitution;
105 Attributes[(int)MaterialAttributes.Material.Stone] =
106 new MaterialAttributes("stone",dDensity, 0.8f, 0.4f);
107 Attributes[(int)MaterialAttributes.Material.Metal] =
108 new MaterialAttributes("metal",dDensity, 0.3f, 0.4f);
109 Attributes[(int)MaterialAttributes.Material.Glass] =
110 new MaterialAttributes("glass",dDensity, 0.2f, 0.7f);
111 Attributes[(int)MaterialAttributes.Material.Wood] =
112 new MaterialAttributes("wood",dDensity, 0.6f, 0.5f);
113 Attributes[(int)MaterialAttributes.Material.Flesh] =
114 new MaterialAttributes("flesh",dDensity, 0.9f, 0.3f);
115 Attributes[(int)MaterialAttributes.Material.Plastic] =
116 new MaterialAttributes("plastic",dDensity, 0.4f, 0.7f);
117 Attributes[(int)MaterialAttributes.Material.Rubber] =
118 new MaterialAttributes("rubber",dDensity, 0.9f, 0.9f);
119 Attributes[(int)MaterialAttributes.Material.Light] =
120 new MaterialAttributes("light",dDensity, dFriction, dRestitution);
121 Attributes[(int)MaterialAttributes.Material.Avatar] =
122 new MaterialAttributes("avatar",60f, 0.2f, 0f);
123
124 Attributes[(int)MaterialAttributes.Material.Stone + (int)MaterialAttributes.Material.NumberOfTypes] =
125 new MaterialAttributes("stonePhysical",dDensity, 0.8f, 0.4f);
126 Attributes[(int)MaterialAttributes.Material.Metal + (int)MaterialAttributes.Material.NumberOfTypes] =
127 new MaterialAttributes("metalPhysical",dDensity, 0.8f, 0.4f);
128 Attributes[(int)MaterialAttributes.Material.Glass + (int)MaterialAttributes.Material.NumberOfTypes] =
129 new MaterialAttributes("glassPhysical",dDensity, 0.8f, 0.7f);
130 Attributes[(int)MaterialAttributes.Material.Wood + (int)MaterialAttributes.Material.NumberOfTypes] =
131 new MaterialAttributes("woodPhysical",dDensity, 0.8f, 0.5f);
132 Attributes[(int)MaterialAttributes.Material.Flesh + (int)MaterialAttributes.Material.NumberOfTypes] =
133 new MaterialAttributes("fleshPhysical",dDensity, 0.8f, 0.3f);
134 Attributes[(int)MaterialAttributes.Material.Plastic + (int)MaterialAttributes.Material.NumberOfTypes] =
135 new MaterialAttributes("plasticPhysical",dDensity, 0.8f, 0.7f);
136 Attributes[(int)MaterialAttributes.Material.Rubber + (int)MaterialAttributes.Material.NumberOfTypes] =
137 new MaterialAttributes("rubberPhysical",dDensity, 0.8f, 0.9f);
138 Attributes[(int)MaterialAttributes.Material.Light + (int)MaterialAttributes.Material.NumberOfTypes] =
139 new MaterialAttributes("lightPhysical",dDensity, dFriction, dRestitution);
140 Attributes[(int)MaterialAttributes.Material.Avatar + (int)MaterialAttributes.Material.NumberOfTypes] =
141 new MaterialAttributes("avatarPhysical",60f, 0.2f, 0f);
142 }
143
144 // Under the [BulletSim] section, one can change the individual material
145 // attribute values. The format of the configuration parameter is:
146 // <materialName><Attribute>["Physical"] = floatValue
147 // For instance:
148 // [BulletSim]
149 // StoneFriction = 0.2
150 // FleshRestitutionPhysical = 0.8
151 // Materials can have different parameters for their static and
152 // physical instantiations. When setting the non-physical value,
153 // both values are changed. Setting the physical value only changes
154 // the physical value.
155 public static void InitializefromParameters(IConfig pConfig)
156 {
157 foreach (KeyValuePair<string, MaterialAttributes.Material> kvp in MaterialMap)
158 {
159 string matName = kvp.Key;
160 foreach (string attribName in MaterialAttributes.MaterialAttribs)
161 {
162 string paramName = matName + attribName;
163 if (pConfig.Contains(paramName))
164 {
165 float paramValue = pConfig.GetFloat(paramName);
166 SetAttributeValue((int)kvp.Value, attribName, paramValue);
167 // set the physical value also
168 SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
169 }
170 paramName += "Physical";
171 if (pConfig.Contains(paramName))
172 {
173 float paramValue = pConfig.GetFloat(paramName);
174 SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
175 }
176 }
177 }
178 }
179
180 // Use reflection to set the value in the attribute structure.
181 private static void SetAttributeValue(int matType, string attribName, float val)
182 {
183 MaterialAttributes thisAttrib = Attributes[matType];
184 FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName.ToLower());
185 if (fieldInfo != null)
186 {
187 fieldInfo.SetValue(thisAttrib, val);
188 Attributes[matType] = thisAttrib;
189 }
190 }
191
192 // Given a material type, return a structure of attributes.
193 public static MaterialAttributes GetAttributes(MaterialAttributes.Material type, bool isPhysical)
194 {
195 int ind = (int)type;
196 if (isPhysical) ind += (int)MaterialAttributes.Material.NumberOfTypes;
197 return Attributes[ind];
198 }
199}
200}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSMotors.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSMotors.cs
new file mode 100644
index 0000000..7abc9b2
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSMotors.cs
@@ -0,0 +1,347 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 */
28using System;
29using System.Collections.Generic;
30using System.Text;
31using OpenMetaverse;
32using OpenSim.Framework;
33
34namespace OpenSim.Region.Physics.BulletSNPlugin
35{
36public abstract class BSMotor
37{
38 // Timescales and other things can be turned off by setting them to 'infinite'.
39 public const float Infinite = 12345.6f;
40 public readonly static Vector3 InfiniteVector = new Vector3(BSMotor.Infinite, BSMotor.Infinite, BSMotor.Infinite);
41
42 public BSMotor(string useName)
43 {
44 UseName = useName;
45 PhysicsScene = null;
46 Enabled = true;
47 }
48 public virtual bool Enabled { get; set; }
49 public virtual void Reset() { }
50 public virtual void Zero() { }
51 public virtual void GenerateTestOutput(float timeStep) { }
52
53 // A name passed at motor creation for easily identifyable debugging messages.
54 public string UseName { get; private set; }
55
56 // Used only for outputting debug information. Might not be set so check for null.
57 public BSScene PhysicsScene { get; set; }
58 protected void MDetailLog(string msg, params Object[] parms)
59 {
60 if (PhysicsScene != null)
61 {
62 if (PhysicsScene.VehicleLoggingEnabled)
63 {
64 PhysicsScene.DetailLog(msg, parms);
65 }
66 }
67 }
68}
69
70// Motor which moves CurrentValue to TargetValue over TimeScale seconds.
71// The TargetValue decays in TargetValueDecayTimeScale and
72// the CurrentValue will be held back by FrictionTimeScale.
73// This motor will "zero itself" over time in that the targetValue will
74// decay to zero and the currentValue will follow it to that zero.
75// The overall effect is for the returned correction value to go from large
76// values (the total difference between current and target minus friction)
77// to small and eventually zero values.
78// TimeScale and TargetDelayTimeScale may be 'infinite' which means no decay.
79
80// For instance, if something is moving at speed X and the desired speed is Y,
81// CurrentValue is X and TargetValue is Y. As the motor is stepped, new
82// values of CurrentValue are returned that approach the TargetValue.
83// The feature of decaying TargetValue is so vehicles will eventually
84// come to a stop rather than run forever. This can be disabled by
85// setting TargetValueDecayTimescale to 'infinite'.
86// The change from CurrentValue to TargetValue is linear over TimeScale seconds.
87public class BSVMotor : BSMotor
88{
89 // public Vector3 FrameOfReference { get; set; }
90 // public Vector3 Offset { get; set; }
91
92 public virtual float TimeScale { get; set; }
93 public virtual float TargetValueDecayTimeScale { get; set; }
94 public virtual Vector3 FrictionTimescale { get; set; }
95 public virtual float Efficiency { get; set; }
96
97 public virtual float ErrorZeroThreshold { get; set; }
98
99 public virtual Vector3 TargetValue { get; protected set; }
100 public virtual Vector3 CurrentValue { get; protected set; }
101 public virtual Vector3 LastError { get; protected set; }
102
103 public virtual bool ErrorIsZero
104 { get {
105 return (LastError == Vector3.Zero || LastError.LengthSquared() <= ErrorZeroThreshold);
106 }
107 }
108
109 public BSVMotor(string useName)
110 : base(useName)
111 {
112 TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite;
113 Efficiency = 1f;
114 FrictionTimescale = BSMotor.InfiniteVector;
115 CurrentValue = TargetValue = Vector3.Zero;
116 ErrorZeroThreshold = 0.001f;
117 }
118 public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency)
119 : this(useName)
120 {
121 TimeScale = timeScale;
122 TargetValueDecayTimeScale = decayTimeScale;
123 FrictionTimescale = frictionTimeScale;
124 Efficiency = efficiency;
125 CurrentValue = TargetValue = Vector3.Zero;
126 }
127 public void SetCurrent(Vector3 current)
128 {
129 CurrentValue = current;
130 }
131 public void SetTarget(Vector3 target)
132 {
133 TargetValue = target;
134 }
135 public override void Zero()
136 {
137 base.Zero();
138 CurrentValue = TargetValue = Vector3.Zero;
139 }
140
141 // Compute the next step and return the new current value
142 public virtual Vector3 Step(float timeStep)
143 {
144 if (!Enabled) return TargetValue;
145
146 Vector3 origTarget = TargetValue; // DEBUG
147 Vector3 origCurrVal = CurrentValue; // DEBUG
148
149 Vector3 correction = Vector3.Zero;
150 Vector3 error = TargetValue - CurrentValue;
151 if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
152 {
153 correction = Step(timeStep, error);
154
155 CurrentValue += correction;
156
157 // The desired value reduces to zero which also reduces the difference with current.
158 // If the decay time is infinite, don't decay at all.
159 float decayFactor = 0f;
160 if (TargetValueDecayTimeScale != BSMotor.Infinite)
161 {
162 decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep;
163 TargetValue *= (1f - decayFactor);
164 }
165
166 // The amount we can correct the error is reduced by the friction
167 Vector3 frictionFactor = Vector3.Zero;
168 if (FrictionTimescale != BSMotor.InfiniteVector)
169 {
170 // frictionFactor = (Vector3.One / FrictionTimescale) * timeStep;
171 // Individual friction components can be 'infinite' so compute each separately.
172 frictionFactor.X = (FrictionTimescale.X == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.X);
173 frictionFactor.Y = (FrictionTimescale.Y == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Y);
174 frictionFactor.Z = (FrictionTimescale.Z == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Z);
175 frictionFactor *= timeStep;
176 CurrentValue *= (Vector3.One - frictionFactor);
177 }
178
179 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},err={5},corr={6}",
180 BSScene.DetailLogZero, UseName, origCurrVal, origTarget,
181 timeStep, error, correction);
182 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},tgtDecayTS={2},decayFact={3},frictTS={4},frictFact={5},tgt={6},curr={7}",
183 BSScene.DetailLogZero, UseName,
184 TargetValueDecayTimeScale, decayFactor, FrictionTimescale, frictionFactor,
185 TargetValue, CurrentValue);
186 }
187 else
188 {
189 // Difference between what we have and target is small. Motor is done.
190 CurrentValue = TargetValue;
191 MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}",
192 BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue);
193 }
194
195 return CurrentValue;
196 }
197 public virtual Vector3 Step(float timeStep, Vector3 error)
198 {
199 if (!Enabled) return Vector3.Zero;
200
201 LastError = error;
202 Vector3 returnCorrection = Vector3.Zero;
203 if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
204 {
205 // correction = error / secondsItShouldTakeToCorrect
206 Vector3 correctionAmount;
207 if (TimeScale == 0f || TimeScale == BSMotor.Infinite)
208 correctionAmount = error * timeStep;
209 else
210 correctionAmount = error / TimeScale * timeStep;
211
212 returnCorrection = correctionAmount;
213 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},timeStep={2},timeScale={3},err={4},corr={5}",
214 BSScene.DetailLogZero, UseName, timeStep, TimeScale, error, correctionAmount);
215 }
216 return returnCorrection;
217 }
218
219 // The user sets all the parameters and calls this which outputs values until error is zero.
220 public override void GenerateTestOutput(float timeStep)
221 {
222 // maximum number of outputs to generate.
223 int maxOutput = 50;
224 MDetailLog("{0},BSVMotor.Test,{1},===================================== BEGIN Test Output", BSScene.DetailLogZero, UseName);
225 MDetailLog("{0},BSVMotor.Test,{1},timeScale={2},targDlyTS={3},frictTS={4},eff={5},curr={6},tgt={7}",
226 BSScene.DetailLogZero, UseName,
227 TimeScale, TargetValueDecayTimeScale, FrictionTimescale, Efficiency,
228 CurrentValue, TargetValue);
229
230 LastError = BSMotor.InfiniteVector;
231 while (maxOutput-- > 0 && !LastError.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
232 {
233 Vector3 lastStep = Step(timeStep);
234 MDetailLog("{0},BSVMotor.Test,{1},cur={2},tgt={3},lastError={4},lastStep={5}",
235 BSScene.DetailLogZero, UseName, CurrentValue, TargetValue, LastError, lastStep);
236 }
237 MDetailLog("{0},BSVMotor.Test,{1},===================================== END Test Output", BSScene.DetailLogZero, UseName);
238
239
240 }
241
242 public override string ToString()
243 {
244 return String.Format("<{0},curr={1},targ={2},decayTS={3},frictTS={4}>",
245 UseName, CurrentValue, TargetValue, TargetValueDecayTimeScale, FrictionTimescale);
246 }
247}
248
249public class BSFMotor : BSMotor
250{
251 public float TimeScale { get; set; }
252 public float DecayTimeScale { get; set; }
253 public float Friction { get; set; }
254 public float Efficiency { get; set; }
255
256 public float Target { get; private set; }
257 public float CurrentValue { get; private set; }
258
259 public BSFMotor(string useName, float timeScale, float decayTimescale, float friction, float efficiency)
260 : base(useName)
261 {
262 }
263 public void SetCurrent(float target)
264 {
265 }
266 public void SetTarget(float target)
267 {
268 }
269 public virtual float Step(float timeStep)
270 {
271 return 0f;
272 }
273}
274
275// Proportional, Integral, Derivitive Motor
276// Good description at http://www.answers.com/topic/pid-controller . Includes processes for choosing p, i and d factors.
277public class BSPIDVMotor : BSVMotor
278{
279 // Larger makes more overshoot, smaller means converge quicker. Range of 0.1 to 10.
280 public Vector3 proportionFactor { get; set; }
281 public Vector3 integralFactor { get; set; }
282 public Vector3 derivFactor { get; set; }
283
284 // Arbritrary factor range.
285 // EfficiencyHigh means move quickly to the correct number. EfficiencyLow means might over correct.
286 public float EfficiencyHigh = 0.4f;
287 public float EfficiencyLow = 4.0f;
288
289 // Running integration of the error
290 Vector3 RunningIntegration { get; set; }
291
292 public BSPIDVMotor(string useName)
293 : base(useName)
294 {
295 proportionFactor = new Vector3(1.00f, 1.00f, 1.00f);
296 integralFactor = new Vector3(1.00f, 1.00f, 1.00f);
297 derivFactor = new Vector3(1.00f, 1.00f, 1.00f);
298 RunningIntegration = Vector3.Zero;
299 LastError = Vector3.Zero;
300 }
301
302 public override void Zero()
303 {
304 base.Zero();
305 }
306
307 public override float Efficiency
308 {
309 get { return base.Efficiency; }
310 set
311 {
312 base.Efficiency = Util.Clamp(value, 0f, 1f);
313 // Compute factors based on efficiency.
314 // If efficiency is high (1f), use a factor value that moves the error value to zero with little overshoot.
315 // If efficiency is low (0f), use a factor value that overcorrects.
316 // TODO: might want to vary contribution of different factor depending on efficiency.
317 float factor = ((1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow) / 3f;
318 // float factor = (1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow;
319 proportionFactor = new Vector3(factor, factor, factor);
320 integralFactor = new Vector3(factor, factor, factor);
321 derivFactor = new Vector3(factor, factor, factor);
322 }
323 }
324
325 // Ignore Current and Target Values and just advance the PID computation on this error.
326 public override Vector3 Step(float timeStep, Vector3 error)
327 {
328 if (!Enabled) return Vector3.Zero;
329
330 // Add up the error so we can integrate over the accumulated errors
331 RunningIntegration += error * timeStep;
332
333 // A simple derivitive is the rate of change from the last error.
334 Vector3 derivFactor = (error - LastError) * timeStep;
335 LastError = error;
336
337 // Correction = -(proportionOfPresentError + accumulationOfPastError + rateOfChangeOfError)
338 Vector3 ret = -(
339 error * proportionFactor
340 + RunningIntegration * integralFactor
341 + derivFactor * derivFactor
342 );
343
344 return ret;
345 }
346}
347}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSParam.cs
new file mode 100644
index 0000000..0bb1674
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSParam.cs
@@ -0,0 +1,559 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OpenSim.Region.Physics.Manager;
32
33using OpenMetaverse;
34using Nini.Config;
35
36namespace OpenSim.Region.Physics.BulletSNPlugin
37{
38public static class BSParam
39{
40 // Level of Detail values kept as float because that's what the Meshmerizer wants
41 public static float MeshLOD { get; private set; }
42 public static float MeshMegaPrimLOD { get; private set; }
43 public static float MeshMegaPrimThreshold { get; private set; }
44 public static float SculptLOD { get; private set; }
45
46 public static float MinimumObjectMass { get; private set; }
47 public static float MaximumObjectMass { get; private set; }
48
49 public static float LinearDamping { get; private set; }
50 public static float AngularDamping { get; private set; }
51 public static float DeactivationTime { get; private set; }
52 public static float LinearSleepingThreshold { get; private set; }
53 public static float AngularSleepingThreshold { get; private set; }
54 public static float CcdMotionThreshold { get; private set; }
55 public static float CcdSweptSphereRadius { get; private set; }
56 public static float ContactProcessingThreshold { get; private set; }
57
58 public static bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed
59 public static bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes
60 public static bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects
61
62 public static float TerrainImplementation { get; private set; }
63 public static float TerrainFriction { get; private set; }
64 public static float TerrainHitFraction { get; private set; }
65 public static float TerrainRestitution { get; private set; }
66 public static float TerrainCollisionMargin { get; private set; }
67
68 // Avatar parameters
69 public static float AvatarFriction { get; private set; }
70 public static float AvatarStandingFriction { get; private set; }
71 public static float AvatarDensity { get; private set; }
72 public static float AvatarRestitution { get; private set; }
73 public static float AvatarCapsuleWidth { get; private set; }
74 public static float AvatarCapsuleDepth { get; private set; }
75 public static float AvatarCapsuleHeight { get; private set; }
76 public static float AvatarContactProcessingThreshold { get; private set; }
77
78 public static float VehicleAngularDamping { get; private set; }
79
80 public static float LinksetImplementation { get; private set; }
81 public static float LinkConstraintUseFrameOffset { get; private set; }
82 public static float LinkConstraintEnableTransMotor { get; private set; }
83 public static float LinkConstraintTransMotorMaxVel { get; private set; }
84 public static float LinkConstraintTransMotorMaxForce { get; private set; }
85 public static float LinkConstraintERP { get; private set; }
86 public static float LinkConstraintCFM { get; private set; }
87 public static float LinkConstraintSolverIterations { get; private set; }
88
89 public static float PID_D { get; private set; } // derivative
90 public static float PID_P { get; private set; } // proportional
91
92 public delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
93 public delegate float ParamGet(BSScene scene);
94 public delegate void ParamSet(BSScene scene, string paramName, uint localID, float val);
95 public delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val);
96
97 public struct ParameterDefn
98 {
99 public string name; // string name of the parameter
100 public string desc; // a short description of what the parameter means
101 public float defaultValue; // default value if not specified anywhere else
102 public ParamUser userParam; // get the value from the configuration file
103 public ParamGet getter; // return the current value stored for this parameter
104 public ParamSet setter; // set the current value for this parameter
105 public SetOnObject onObject; // set the value on an object in the physical domain
106 public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s)
107 {
108 name = n;
109 desc = d;
110 defaultValue = v;
111 userParam = u;
112 getter = g;
113 setter = s;
114 onObject = null;
115 }
116 public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o)
117 {
118 name = n;
119 desc = d;
120 defaultValue = v;
121 userParam = u;
122 getter = g;
123 setter = s;
124 onObject = o;
125 }
126 }
127
128 // List of all of the externally visible parameters.
129 // For each parameter, this table maps a text name to getter and setters.
130 // To add a new externally referencable/settable parameter, add the paramter storage
131 // location somewhere in the program and make an entry in this table with the
132 // getters and setters.
133 // It is easiest to find an existing definition and copy it.
134 // Parameter values are floats. Booleans are converted to a floating value.
135 //
136 // A ParameterDefn() takes the following parameters:
137 // -- the text name of the parameter. This is used for console input and ini file.
138 // -- a short text description of the parameter. This shows up in the console listing.
139 // -- a default value (float)
140 // -- a delegate for fetching the parameter from the ini file.
141 // Should handle fetching the right type from the ini file and converting it.
142 // -- a delegate for getting the value as a float
143 // -- a delegate for setting the value from a float
144 // -- an optional delegate to update the value in the world. Most often used to
145 // push the new value to an in-world object.
146 //
147 // The single letter parameters for the delegates are:
148 // s = BSScene
149 // o = BSPhysObject
150 // p = string parameter name
151 // l = localID of referenced object
152 // v = value (float)
153 // cf = parameter configuration class (for fetching values from ini file)
154 private static ParameterDefn[] ParameterDefinitions =
155 {
156 new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties",
157 ConfigurationParameters.numericTrue,
158 (s,cf,p,v) => { ShouldMeshSculptedPrim = cf.GetBoolean(p, BSParam.BoolNumeric(v)); },
159 (s) => { return BSParam.NumericBool(ShouldMeshSculptedPrim); },
160 (s,p,l,v) => { ShouldMeshSculptedPrim = BSParam.BoolNumeric(v); } ),
161 new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects",
162 ConfigurationParameters.numericFalse,
163 (s,cf,p,v) => { ShouldForceSimplePrimMeshing = cf.GetBoolean(p, BSParam.BoolNumeric(v)); },
164 (s) => { return BSParam.NumericBool(ShouldForceSimplePrimMeshing); },
165 (s,p,l,v) => { ShouldForceSimplePrimMeshing = BSParam.BoolNumeric(v); } ),
166 new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects",
167 ConfigurationParameters.numericTrue,
168 (s,cf,p,v) => { ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, BSParam.BoolNumeric(v)); },
169 (s) => { return BSParam.NumericBool(ShouldUseHullsForPhysicalObjects); },
170 (s,p,l,v) => { ShouldUseHullsForPhysicalObjects = BSParam.BoolNumeric(v); } ),
171
172 new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
173 8f,
174 (s,cf,p,v) => { MeshLOD = (float)cf.GetInt(p, (int)v); },
175 (s) => { return MeshLOD; },
176 (s,p,l,v) => { MeshLOD = v; } ),
177 new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters",
178 16f,
179 (s,cf,p,v) => { MeshMegaPrimLOD = (float)cf.GetInt(p, (int)v); },
180 (s) => { return MeshMegaPrimLOD; },
181 (s,p,l,v) => { MeshMegaPrimLOD = v; } ),
182 new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD",
183 10f,
184 (s,cf,p,v) => { MeshMegaPrimThreshold = (float)cf.GetInt(p, (int)v); },
185 (s) => { return MeshMegaPrimThreshold; },
186 (s,p,l,v) => { MeshMegaPrimThreshold = v; } ),
187 new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)",
188 32f,
189 (s,cf,p,v) => { SculptLOD = (float)cf.GetInt(p, (int)v); },
190 (s) => { return SculptLOD; },
191 (s,p,l,v) => { SculptLOD = v; } ),
192
193 new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps",
194 10f,
195 (s,cf,p,v) => { s.m_maxSubSteps = cf.GetInt(p, (int)v); },
196 (s) => { return (float)s.m_maxSubSteps; },
197 (s,p,l,v) => { s.m_maxSubSteps = (int)v; } ),
198 new ParameterDefn("FixedTimeStep", "In simulation step, seconds of one substep (1/60)",
199 1f / 60f,
200 (s,cf,p,v) => { s.m_fixedTimeStep = cf.GetFloat(p, v); },
201 (s) => { return (float)s.m_fixedTimeStep; },
202 (s,p,l,v) => { s.m_fixedTimeStep = v; } ),
203 new ParameterDefn("MaxCollisionsPerFrame", "Max collisions returned at end of each frame",
204 2048f,
205 (s,cf,p,v) => { s.m_maxCollisionsPerFrame = cf.GetInt(p, (int)v); },
206 (s) => { return (float)s.m_maxCollisionsPerFrame; },
207 (s,p,l,v) => { s.m_maxCollisionsPerFrame = (int)v; } ),
208 new ParameterDefn("MaxUpdatesPerFrame", "Max updates returned at end of each frame",
209 8000f,
210 (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); },
211 (s) => { return (float)s.m_maxUpdatesPerFrame; },
212 (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
213 new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step",
214 500f,
215 (s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); },
216 (s) => { return (float)s.m_taintsToProcessPerStep; },
217 (s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ),
218 new ParameterDefn("MinObjectMass", "Minimum object mass (0.0001)",
219 0.0001f,
220 (s,cf,p,v) => { MinimumObjectMass = cf.GetFloat(p, v); },
221 (s) => { return (float)MinimumObjectMass; },
222 (s,p,l,v) => { MinimumObjectMass = v; } ),
223 new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)",
224 10000.01f,
225 (s,cf,p,v) => { MaximumObjectMass = cf.GetFloat(p, v); },
226 (s) => { return (float)MaximumObjectMass; },
227 (s,p,l,v) => { MaximumObjectMass = v; } ),
228
229 new ParameterDefn("PID_D", "Derivitive factor for motion smoothing",
230 2200f,
231 (s,cf,p,v) => { PID_D = cf.GetFloat(p, v); },
232 (s) => { return (float)PID_D; },
233 (s,p,l,v) => { PID_D = v; } ),
234 new ParameterDefn("PID_P", "Parameteric factor for motion smoothing",
235 900f,
236 (s,cf,p,v) => { PID_P = cf.GetFloat(p, v); },
237 (s) => { return (float)PID_P; },
238 (s,p,l,v) => { PID_P = v; } ),
239
240 new ParameterDefn("DefaultFriction", "Friction factor used on new objects",
241 0.2f,
242 (s,cf,p,v) => { s.UnmanagedParams[0].defaultFriction = cf.GetFloat(p, v); },
243 (s) => { return s.UnmanagedParams[0].defaultFriction; },
244 (s,p,l,v) => { s.UnmanagedParams[0].defaultFriction = v; } ),
245 new ParameterDefn("DefaultDensity", "Density for new objects" ,
246 10.000006836f, // Aluminum g/cm3
247 (s,cf,p,v) => { s.UnmanagedParams[0].defaultDensity = cf.GetFloat(p, v); },
248 (s) => { return s.UnmanagedParams[0].defaultDensity; },
249 (s,p,l,v) => { s.UnmanagedParams[0].defaultDensity = v; } ),
250 new ParameterDefn("DefaultRestitution", "Bouncyness of an object" ,
251 0f,
252 (s,cf,p,v) => { s.UnmanagedParams[0].defaultRestitution = cf.GetFloat(p, v); },
253 (s) => { return s.UnmanagedParams[0].defaultRestitution; },
254 (s,p,l,v) => { s.UnmanagedParams[0].defaultRestitution = v; } ),
255 new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)",
256 0.04f,
257 (s,cf,p,v) => { s.UnmanagedParams[0].collisionMargin = cf.GetFloat(p, v); },
258 (s) => { return s.UnmanagedParams[0].collisionMargin; },
259 (s,p,l,v) => { s.UnmanagedParams[0].collisionMargin = v; } ),
260 new ParameterDefn("Gravity", "Vertical force of gravity (negative means down)",
261 -9.80665f,
262 (s,cf,p,v) => { s.UnmanagedParams[0].gravity = cf.GetFloat(p, v); },
263 (s) => { return s.UnmanagedParams[0].gravity; },
264 (s,p,l,v) => { s.UpdateParameterObject((x)=>{s.UnmanagedParams[0].gravity=x;}, p, PhysParameterEntry.APPLY_TO_NONE, v); },
265 (s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ),
266
267
268 new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)",
269 0f,
270 (s,cf,p,v) => { LinearDamping = cf.GetFloat(p, v); },
271 (s) => { return LinearDamping; },
272 (s,p,l,v) => { s.UpdateParameterObject((x)=>{LinearDamping=x;}, p, l, v); },
273 (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, v, AngularDamping); } ),
274 new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)",
275 0f,
276 (s,cf,p,v) => { AngularDamping = cf.GetFloat(p, v); },
277 (s) => { return AngularDamping; },
278 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AngularDamping=x;}, p, l, v); },
279 (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, LinearDamping, v); } ),
280 new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static",
281 0.2f,
282 (s,cf,p,v) => { DeactivationTime = cf.GetFloat(p, v); },
283 (s) => { return DeactivationTime; },
284 (s,p,l,v) => { s.UpdateParameterObject((x)=>{DeactivationTime=x;}, p, l, v); },
285 (s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.PhysBody.ptr, v); } ),
286 new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static",
287 0.8f,
288 (s,cf,p,v) => { LinearSleepingThreshold = cf.GetFloat(p, v); },
289 (s) => { return LinearSleepingThreshold; },
290 (s,p,l,v) => { s.UpdateParameterObject((x)=>{LinearSleepingThreshold=x;}, p, l, v); },
291 (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
292 new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static",
293 1.0f,
294 (s,cf,p,v) => { AngularSleepingThreshold = cf.GetFloat(p, v); },
295 (s) => { return AngularSleepingThreshold; },
296 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AngularSleepingThreshold=x;}, p, l, v); },
297 (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
298 new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" ,
299 0f, // set to zero to disable
300 (s,cf,p,v) => { CcdMotionThreshold = cf.GetFloat(p, v); },
301 (s) => { return CcdMotionThreshold; },
302 (s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdMotionThreshold=x;}, p, l, v); },
303 (s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.PhysBody.ptr, v); } ),
304 new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" ,
305 0f,
306 (s,cf,p,v) => { CcdSweptSphereRadius = cf.GetFloat(p, v); },
307 (s) => { return CcdSweptSphereRadius; },
308 (s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdSweptSphereRadius=x;}, p, l, v); },
309 (s,o,v) => { BulletSimAPI.SetCcdSweptSphereRadius2(o.PhysBody.ptr, v); } ),
310 new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" ,
311 0.1f,
312 (s,cf,p,v) => { ContactProcessingThreshold = cf.GetFloat(p, v); },
313 (s) => { return ContactProcessingThreshold; },
314 (s,p,l,v) => { s.UpdateParameterObject((x)=>{ContactProcessingThreshold=x;}, p, l, v); },
315 (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ),
316
317 new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)",
318 (float)BSTerrainPhys.TerrainImplementation.Mesh,
319 (s,cf,p,v) => { TerrainImplementation = cf.GetFloat(p,v); },
320 (s) => { return TerrainImplementation; },
321 (s,p,l,v) => { TerrainImplementation = v; } ),
322 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
323 0.3f,
324 (s,cf,p,v) => { TerrainFriction = cf.GetFloat(p, v); },
325 (s) => { return TerrainFriction; },
326 (s,p,l,v) => { TerrainFriction = v; /* TODO: set on real terrain */} ),
327 new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" ,
328 0.8f,
329 (s,cf,p,v) => { TerrainHitFraction = cf.GetFloat(p, v); },
330 (s) => { return TerrainHitFraction; },
331 (s,p,l,v) => { TerrainHitFraction = v; /* TODO: set on real terrain */ } ),
332 new ParameterDefn("TerrainRestitution", "Bouncyness" ,
333 0f,
334 (s,cf,p,v) => { TerrainRestitution = cf.GetFloat(p, v); },
335 (s) => { return TerrainRestitution; },
336 (s,p,l,v) => { TerrainRestitution = v; /* TODO: set on real terrain */ } ),
337 new ParameterDefn("TerrainCollisionMargin", "Margin where collision checking starts" ,
338 0.04f,
339 (s,cf,p,v) => { TerrainCollisionMargin = cf.GetFloat(p, v); },
340 (s) => { return TerrainCollisionMargin; },
341 (s,p,l,v) => { TerrainCollisionMargin = v; /* TODO: set on real terrain */ } ),
342
343 new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
344 0.2f,
345 (s,cf,p,v) => { AvatarFriction = cf.GetFloat(p, v); },
346 (s) => { return AvatarFriction; },
347 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarFriction=x;}, p, l, v); } ),
348 new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
349 10.0f,
350 (s,cf,p,v) => { AvatarStandingFriction = cf.GetFloat(p, v); },
351 (s) => { return AvatarStandingFriction; },
352 (s,p,l,v) => { AvatarStandingFriction = v; } ),
353 new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.",
354 60f,
355 (s,cf,p,v) => { AvatarDensity = cf.GetFloat(p, v); },
356 (s) => { return AvatarDensity; },
357 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarDensity=x;}, p, l, v); } ),
358 new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.",
359 0f,
360 (s,cf,p,v) => { AvatarRestitution = cf.GetFloat(p, v); },
361 (s) => { return AvatarRestitution; },
362 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarRestitution=x;}, p, l, v); } ),
363 new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule",
364 0.6f,
365 (s,cf,p,v) => { AvatarCapsuleWidth = cf.GetFloat(p, v); },
366 (s) => { return AvatarCapsuleWidth; },
367 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleWidth=x;}, p, l, v); } ),
368 new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule",
369 0.45f,
370 (s,cf,p,v) => { AvatarCapsuleDepth = cf.GetFloat(p, v); },
371 (s) => { return AvatarCapsuleDepth; },
372 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleDepth=x;}, p, l, v); } ),
373 new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar",
374 1.5f,
375 (s,cf,p,v) => { AvatarCapsuleHeight = cf.GetFloat(p, v); },
376 (s) => { return AvatarCapsuleHeight; },
377 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleHeight=x;}, p, l, v); } ),
378 new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions",
379 0.1f,
380 (s,cf,p,v) => { AvatarContactProcessingThreshold = cf.GetFloat(p, v); },
381 (s) => { return AvatarContactProcessingThreshold; },
382 (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarContactProcessingThreshold=x;}, p, l, v); } ),
383
384 new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)",
385 0.95f,
386 (s,cf,p,v) => { VehicleAngularDamping = cf.GetFloat(p, v); },
387 (s) => { return VehicleAngularDamping; },
388 (s,p,l,v) => { VehicleAngularDamping = v; } ),
389
390 new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)",
391 0f,
392 (s,cf,p,v) => { s.UnmanagedParams[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); },
393 (s) => { return s.UnmanagedParams[0].maxPersistantManifoldPoolSize; },
394 (s,p,l,v) => { s.UnmanagedParams[0].maxPersistantManifoldPoolSize = v; } ),
395 new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)",
396 0f,
397 (s,cf,p,v) => { s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); },
398 (s) => { return s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize; },
399 (s,p,l,v) => { s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize = v; } ),
400 new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count",
401 ConfigurationParameters.numericFalse,
402 (s,cf,p,v) => { s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
403 (s) => { return s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation; },
404 (s,p,l,v) => { s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = v; } ),
405 new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step",
406 ConfigurationParameters.numericFalse,
407 (s,cf,p,v) => { s.UnmanagedParams[0].shouldForceUpdateAllAabbs = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
408 (s) => { return s.UnmanagedParams[0].shouldForceUpdateAllAabbs; },
409 (s,p,l,v) => { s.UnmanagedParams[0].shouldForceUpdateAllAabbs = v; } ),
410 new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction",
411 ConfigurationParameters.numericTrue,
412 (s,cf,p,v) => { s.UnmanagedParams[0].shouldRandomizeSolverOrder = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
413 (s) => { return s.UnmanagedParams[0].shouldRandomizeSolverOrder; },
414 (s,p,l,v) => { s.UnmanagedParams[0].shouldRandomizeSolverOrder = v; } ),
415 new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands",
416 ConfigurationParameters.numericTrue,
417 (s,cf,p,v) => { s.UnmanagedParams[0].shouldSplitSimulationIslands = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
418 (s) => { return s.UnmanagedParams[0].shouldSplitSimulationIslands; },
419 (s,p,l,v) => { s.UnmanagedParams[0].shouldSplitSimulationIslands = v; } ),
420 new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching",
421 ConfigurationParameters.numericFalse,
422 (s,cf,p,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
423 (s) => { return s.UnmanagedParams[0].shouldEnableFrictionCaching; },
424 (s,p,l,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = v; } ),
425 new ParameterDefn("NumberOfSolverIterations", "Number of internal iterations (0 means default)",
426 0f, // zero says use Bullet default
427 (s,cf,p,v) => { s.UnmanagedParams[0].numberOfSolverIterations = cf.GetFloat(p, v); },
428 (s) => { return s.UnmanagedParams[0].numberOfSolverIterations; },
429 (s,p,l,v) => { s.UnmanagedParams[0].numberOfSolverIterations = v; } ),
430
431 new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)",
432 (float)BSLinkset.LinksetImplementation.Compound,
433 (s,cf,p,v) => { LinksetImplementation = cf.GetFloat(p,v); },
434 (s) => { return LinksetImplementation; },
435 (s,p,l,v) => { LinksetImplementation = v; } ),
436 new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.",
437 ConfigurationParameters.numericFalse,
438 (s,cf,p,v) => { LinkConstraintUseFrameOffset = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
439 (s) => { return LinkConstraintUseFrameOffset; },
440 (s,p,l,v) => { LinkConstraintUseFrameOffset = v; } ),
441 new ParameterDefn("LinkConstraintEnableTransMotor", "Whether to enable translational motor on linkset constraints",
442 ConfigurationParameters.numericTrue,
443 (s,cf,p,v) => { LinkConstraintEnableTransMotor = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
444 (s) => { return LinkConstraintEnableTransMotor; },
445 (s,p,l,v) => { LinkConstraintEnableTransMotor = v; } ),
446 new ParameterDefn("LinkConstraintTransMotorMaxVel", "Maximum velocity to be applied by translational motor in linkset constraints",
447 5.0f,
448 (s,cf,p,v) => { LinkConstraintTransMotorMaxVel = cf.GetFloat(p, v); },
449 (s) => { return LinkConstraintTransMotorMaxVel; },
450 (s,p,l,v) => { LinkConstraintTransMotorMaxVel = v; } ),
451 new ParameterDefn("LinkConstraintTransMotorMaxForce", "Maximum force to be applied by translational motor in linkset constraints",
452 0.1f,
453 (s,cf,p,v) => { LinkConstraintTransMotorMaxForce = cf.GetFloat(p, v); },
454 (s) => { return LinkConstraintTransMotorMaxForce; },
455 (s,p,l,v) => { LinkConstraintTransMotorMaxForce = v; } ),
456 new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1",
457 0.1f,
458 (s,cf,p,v) => { LinkConstraintCFM = cf.GetFloat(p, v); },
459 (s) => { return LinkConstraintCFM; },
460 (s,p,l,v) => { LinkConstraintCFM = v; } ),
461 new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2",
462 0.1f,
463 (s,cf,p,v) => { LinkConstraintERP = cf.GetFloat(p, v); },
464 (s) => { return LinkConstraintERP; },
465 (s,p,l,v) => { LinkConstraintERP = v; } ),
466 new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)",
467 40,
468 (s,cf,p,v) => { LinkConstraintSolverIterations = cf.GetFloat(p, v); },
469 (s) => { return LinkConstraintSolverIterations; },
470 (s,p,l,v) => { LinkConstraintSolverIterations = v; } ),
471
472 new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)",
473 0f,
474 (s,cf,p,v) => { s.UnmanagedParams[0].physicsLoggingFrames = cf.GetInt(p, (int)v); },
475 (s) => { return (float)s.UnmanagedParams[0].physicsLoggingFrames; },
476 (s,p,l,v) => { s.UnmanagedParams[0].physicsLoggingFrames = (int)v; } ),
477 };
478
479 // Convert a boolean to our numeric true and false values
480 public static float NumericBool(bool b)
481 {
482 return (b ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse);
483 }
484
485 // Convert numeric true and false values to a boolean
486 public static bool BoolNumeric(float b)
487 {
488 return (b == ConfigurationParameters.numericTrue ? true : false);
489 }
490
491 // Search through the parameter definitions and return the matching
492 // ParameterDefn structure.
493 // Case does not matter as names are compared after converting to lower case.
494 // Returns 'false' if the parameter is not found.
495 internal static bool TryGetParameter(string paramName, out ParameterDefn defn)
496 {
497 bool ret = false;
498 ParameterDefn foundDefn = new ParameterDefn();
499 string pName = paramName.ToLower();
500
501 foreach (ParameterDefn parm in ParameterDefinitions)
502 {
503 if (pName == parm.name.ToLower())
504 {
505 foundDefn = parm;
506 ret = true;
507 break;
508 }
509 }
510 defn = foundDefn;
511 return ret;
512 }
513
514 // Pass through the settable parameters and set the default values
515 internal static void SetParameterDefaultValues(BSScene physicsScene)
516 {
517 foreach (ParameterDefn parm in ParameterDefinitions)
518 {
519 parm.setter(physicsScene, parm.name, PhysParameterEntry.APPLY_TO_NONE, parm.defaultValue);
520 }
521 }
522
523 // Get user set values out of the ini file.
524 internal static void SetParameterConfigurationValues(BSScene physicsScene, IConfig cfg)
525 {
526 foreach (ParameterDefn parm in ParameterDefinitions)
527 {
528 parm.userParam(physicsScene, cfg, parm.name, parm.defaultValue);
529 }
530 }
531
532 internal static PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1];
533
534 // This creates an array in the correct format for returning the list of
535 // parameters. This is used by the 'list' option of the 'physics' command.
536 internal static void BuildParameterTable()
537 {
538 if (SettableParameters.Length < ParameterDefinitions.Length)
539 {
540 List<PhysParameterEntry> entries = new List<PhysParameterEntry>();
541 for (int ii = 0; ii < ParameterDefinitions.Length; ii++)
542 {
543 ParameterDefn pd = ParameterDefinitions[ii];
544 entries.Add(new PhysParameterEntry(pd.name, pd.desc));
545 }
546
547 // make the list in alphabetical order for estetic reasons
548 entries.Sort(delegate(PhysParameterEntry ppe1, PhysParameterEntry ppe2)
549 {
550 return ppe1.name.CompareTo(ppe2.name);
551 });
552
553 SettableParameters = entries.ToArray();
554 }
555 }
556
557
558}
559}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSPhysObject.cs
new file mode 100644
index 0000000..4096ef8
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSPhysObject.cs
@@ -0,0 +1,345 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OMV = OpenMetaverse;
32using OpenSim.Framework;
33using OpenSim.Region.Physics.Manager;
34
35namespace OpenSim.Region.Physics.BulletSNPlugin
36{
37/*
38 * Class to wrap all objects.
39 * The rest of BulletSim doesn't need to keep checking for avatars or prims
40 * unless the difference is significant.
41 *
42 * Variables in the physicsl objects are in three forms:
43 * VariableName: used by the simulator and performs taint operations, etc
44 * RawVariableName: direct reference to the BulletSim storage for the variable value
45 * ForceVariableName: direct reference (store and fetch) to the value in the physics engine.
46 * The last two (and certainly the last one) should be referenced only in taint-time.
47 */
48
49/*
50 * As of 20121221, the following are the call sequences (going down) for different script physical functions:
51 * llApplyImpulse llApplyRotImpulse llSetTorque llSetForce
52 * SOP.ApplyImpulse SOP.ApplyAngularImpulse SOP.SetAngularImpulse SOP.SetForce
53 * SOG.ApplyImpulse SOG.ApplyAngularImpulse SOG.SetAngularImpulse
54 * PA.AddForce PA.AddAngularForce PA.Torque = v PA.Force = v
55 * BS.ApplyCentralForce BS.ApplyTorque
56 */
57
58public abstract class BSPhysObject : PhysicsActor
59{
60 protected BSPhysObject()
61 {
62 }
63 protected BSPhysObject(BSScene parentScene, uint localID, string name, string typeName)
64 {
65 PhysicsScene = parentScene;
66 LocalID = localID;
67 PhysObjectName = name;
68 TypeName = typeName;
69
70 Linkset = BSLinkset.Factory(PhysicsScene, this);
71 LastAssetBuildFailed = false;
72
73 // Default material type
74 Material = MaterialAttributes.Material.Wood;
75
76 CollisionCollection = new CollisionEventUpdate();
77 SubscribedEventsMs = 0;
78 CollidingStep = 0;
79 CollidingGroundStep = 0;
80 }
81
82 // Tell the object to clean up.
83 public virtual void Destroy()
84 {
85 UnRegisterAllPreStepActions();
86 }
87
88 public BSScene PhysicsScene { get; protected set; }
89 // public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor
90 public string PhysObjectName { get; protected set; }
91 public string TypeName { get; protected set; }
92
93 public BSLinkset Linkset { get; set; }
94 public BSLinksetInfo LinksetInfo { get; set; }
95
96 // Return the object mass without calculating it or having side effects
97 public abstract float RawMass { get; }
98 // Set the raw mass but also update physical mass properties (inertia, ...)
99 public abstract void UpdatePhysicalMassProperties(float mass);
100
101 // The last value calculated for the prim's inertia
102 public OMV.Vector3 Inertia { get; set; }
103
104 // Reference to the physical body (btCollisionObject) of this object
105 public BulletBody PhysBody;
106 // Reference to the physical shape (btCollisionShape) of this object
107 public BulletShape PhysShape;
108
109 // 'true' if the mesh's underlying asset failed to build.
110 // This will keep us from looping after the first time the build failed.
111 public bool LastAssetBuildFailed { get; set; }
112
113 // The objects base shape information. Null if not a prim type shape.
114 public PrimitiveBaseShape BaseShape { get; protected set; }
115 // Some types of objects have preferred physical representations.
116 // Returns SHAPE_UNKNOWN if there is no preference.
117 public virtual BSPhysicsShapeType PreferredPhysicalShape
118 {
119 get { return BSPhysicsShapeType.SHAPE_UNKNOWN; }
120 }
121
122 // When the physical properties are updated, an EntityProperty holds the update values.
123 // Keep the current and last EntityProperties to enable computation of differences
124 // between the current update and the previous values.
125 public EntityProperties CurrentEntityProperties { get; set; }
126 public EntityProperties LastEntityProperties { get; set; }
127
128 public virtual OMV.Vector3 Scale { get; set; }
129 public abstract bool IsSolid { get; }
130 public abstract bool IsStatic { get; }
131
132 // Materialness
133 public MaterialAttributes.Material Material { get; private set; }
134 public override void SetMaterial(int material)
135 {
136 Material = (MaterialAttributes.Material)material;
137 }
138
139 // Stop all physical motion.
140 public abstract void ZeroMotion(bool inTaintTime);
141 public abstract void ZeroAngularMotion(bool inTaintTime);
142
143 // Step the vehicle simulation for this object. A NOOP if the vehicle was not configured.
144 public virtual void StepVehicle(float timeStep) { }
145
146 // Update the physical location and motion of the object. Called with data from Bullet.
147 public abstract void UpdateProperties(EntityProperties entprop);
148
149 public abstract OMV.Vector3 RawPosition { get; set; }
150 public abstract OMV.Vector3 ForcePosition { get; set; }
151
152 public abstract OMV.Quaternion RawOrientation { get; set; }
153 public abstract OMV.Quaternion ForceOrientation { get; set; }
154
155 // The system is telling us the velocity it wants to move at.
156 // protected OMV.Vector3 m_targetVelocity; // use the definition in PhysicsActor
157 public override OMV.Vector3 TargetVelocity
158 {
159 get { return m_targetVelocity; }
160 set
161 {
162 m_targetVelocity = value;
163 Velocity = value;
164 }
165 }
166 public abstract OMV.Vector3 ForceVelocity { get; set; }
167
168 public abstract OMV.Vector3 ForceRotationalVelocity { get; set; }
169
170 public abstract float ForceBuoyancy { get; set; }
171
172 public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
173
174 #region Collisions
175
176 // Requested number of milliseconds between collision events. Zero means disabled.
177 protected int SubscribedEventsMs { get; set; }
178 // Given subscription, the time that a collision may be passed up
179 protected int NextCollisionOkTime { get; set; }
180 // The simulation step that last had a collision
181 protected long CollidingStep { get; set; }
182 // The simulation step that last had a collision with the ground
183 protected long CollidingGroundStep { get; set; }
184 // The collision flags we think are set in Bullet
185 protected CollisionFlags CurrentCollisionFlags { get; set; }
186
187 // The collisions that have been collected this tick
188 protected CollisionEventUpdate CollisionCollection;
189
190 // The simulation step is telling this object about a collision.
191 // Return 'true' if a collision was processed and should be sent up.
192 // Called at taint time from within the Step() function
193 public virtual bool Collide(uint collidingWith, BSPhysObject collidee,
194 OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
195 {
196 bool ret = false;
197
198 // The following lines make IsColliding() and IsCollidingGround() work
199 CollidingStep = PhysicsScene.SimulationStep;
200 if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID)
201 {
202 CollidingGroundStep = PhysicsScene.SimulationStep;
203 }
204
205 // prims in the same linkset cannot collide with each other
206 if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID))
207 {
208 return ret;
209 }
210
211 // if someone has subscribed for collision events....
212 if (SubscribedEvents()) {
213 CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
214 DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}",
215 LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth);
216
217 ret = true;
218 }
219 return ret;
220 }
221
222 // Send the collected collisions into the simulator.
223 // Called at taint time from within the Step() function thus no locking problems
224 // with CollisionCollection and ObjectsWithNoMoreCollisions.
225 // Return 'true' if there were some actual collisions passed up
226 public virtual bool SendCollisions()
227 {
228 bool ret = true;
229 // If the 'no collision' call, force it to happen right now so quick collision_end
230 bool force = (CollisionCollection.Count == 0);
231
232 // throttle the collisions to the number of milliseconds specified in the subscription
233 if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime))
234 {
235 NextCollisionOkTime = PhysicsScene.SimulationNowTime + SubscribedEventsMs;
236
237 // We are called if we previously had collisions. If there are no collisions
238 // this time, send up one last empty event so OpenSim can sense collision end.
239 if (CollisionCollection.Count == 0)
240 {
241 // If I have no collisions this time, remove me from the list of objects with collisions.
242 ret = false;
243 }
244
245 // DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
246 base.SendCollisionUpdate(CollisionCollection);
247
248 // The CollisionCollection instance is passed around in the simulator.
249 // Make sure we don't have a handle to that one and that a new one is used for next time.
250 // This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here,
251 // a race condition is created for the other users of this instance.
252 CollisionCollection = new CollisionEventUpdate();
253 }
254 return ret;
255 }
256
257 // Subscribe for collision events.
258 // Parameter is the millisecond rate the caller wishes collision events to occur.
259 public override void SubscribeEvents(int ms) {
260 // DetailLog("{0},{1}.SubscribeEvents,subscribing,ms={2}", LocalID, TypeName, ms);
261 SubscribedEventsMs = ms;
262 if (ms > 0)
263 {
264 // make sure first collision happens
265 NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs);
266
267 PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate()
268 {
269 if (PhysBody.HasPhysicalBody)
270 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
271 });
272 }
273 else
274 {
275 // Subscribing for zero or less is the same as unsubscribing
276 UnSubscribeEvents();
277 }
278 }
279 public override void UnSubscribeEvents() {
280 // DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName);
281 SubscribedEventsMs = 0;
282 PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate()
283 {
284 // Make sure there is a body there because sometimes destruction happens in an un-ideal order.
285 if (PhysBody.HasPhysicalBody)
286 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
287 });
288 }
289 // Return 'true' if the simulator wants collision events
290 public override bool SubscribedEvents() {
291 return (SubscribedEventsMs > 0);
292 }
293
294 #endregion // Collisions
295
296 #region Per Simulation Step actions
297 // There are some actions that must be performed for a physical object before each simulation step.
298 // These actions are optional so, rather than scanning all the physical objects and asking them
299 // if they have anything to do, a physical object registers for an event call before the step is performed.
300 // This bookkeeping makes it easy to add, remove and clean up after all these registrations.
301 private Dictionary<string, BSScene.PreStepAction> RegisteredActions = new Dictionary<string, BSScene.PreStepAction>();
302 protected void RegisterPreStepAction(string op, uint id, BSScene.PreStepAction actn)
303 {
304 string identifier = op + "-" + id.ToString();
305 RegisteredActions[identifier] = actn;
306 PhysicsScene.BeforeStep += actn;
307 DetailLog("{0},BSPhysObject.RegisterPreStepAction,id={1}", LocalID, identifier);
308 }
309
310 // Unregister a pre step action. Safe to call if the action has not been registered.
311 protected void UnRegisterPreStepAction(string op, uint id)
312 {
313 string identifier = op + "-" + id.ToString();
314 bool removed = false;
315 if (RegisteredActions.ContainsKey(identifier))
316 {
317 PhysicsScene.BeforeStep -= RegisteredActions[identifier];
318 RegisteredActions.Remove(identifier);
319 removed = true;
320 }
321 DetailLog("{0},BSPhysObject.UnRegisterPreStepAction,id={1},removed={2}", LocalID, identifier, removed);
322 }
323
324 protected void UnRegisterAllPreStepActions()
325 {
326 foreach (KeyValuePair<string, BSScene.PreStepAction> kvp in RegisteredActions)
327 {
328 PhysicsScene.BeforeStep -= kvp.Value;
329 }
330 RegisteredActions.Clear();
331 DetailLog("{0},BSPhysObject.UnRegisterAllPreStepActions,", LocalID);
332 }
333
334
335 #endregion // Per Simulation Step actions
336
337 // High performance detailed logging routine used by the physical objects.
338 protected void DetailLog(string msg, params Object[] args)
339 {
340 if (PhysicsScene.PhysicsLogging.Enabled)
341 PhysicsScene.DetailLog(msg, args);
342 }
343
344}
345}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSPlugin.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSPlugin.cs
new file mode 100644
index 0000000..75963ee
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSPlugin.cs
@@ -0,0 +1,81 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using OpenSim.Framework;
30using OpenSim.Region.Physics.Manager;
31using OpenMetaverse;
32
33namespace OpenSim.Region.Physics.BulletSNPlugin
34{
35 /// <summary>
36 /// Entry for a port of Bullet (http://bulletphysics.org/) to OpenSim.
37 /// This module interfaces to an unmanaged C++ library which makes the
38 /// actual calls into the Bullet physics engine.
39 /// The unmanaged library is found in opensim-libs::trunk/unmanaged/BulletSim/.
40 /// The unmanaged library is compiled and linked statically with Bullet
41 /// to create BulletSim.dll and libBulletSim.so (for both 32 and 64 bit).
42 /// </summary>
43public class BSPlugin : IPhysicsPlugin
44{
45 //private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
46
47 private BSScene _mScene;
48
49 public BSPlugin()
50 {
51 }
52
53 public bool Init()
54 {
55 return true;
56 }
57
58 public PhysicsScene GetScene(String sceneIdentifier)
59 {
60 if (_mScene == null)
61 {
62
63 // If not Windows, loading is performed by the
64 // Mono loader as specified in
65 // "bin/Physics/OpenSim.Region.Physics.BulletSNPlugin.dll.config".
66
67 _mScene = new BSScene(sceneIdentifier);
68 }
69 return (_mScene);
70 }
71
72 public string GetName()
73 {
74 return ("BulletSimN");
75 }
76
77 public void Dispose()
78 {
79 }
80}
81}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs
new file mode 100644
index 0000000..a889c24
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSPrim.cs
@@ -0,0 +1,1467 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Reflection;
30using System.Collections.Generic;
31using System.Xml;
32using log4net;
33using OMV = OpenMetaverse;
34using OpenSim.Framework;
35using OpenSim.Region.Physics.Manager;
36using OpenSim.Region.Physics.ConvexDecompositionDotNet;
37
38namespace OpenSim.Region.Physics.BulletSNPlugin
39{
40
41 [Serializable]
42public sealed class BSPrim : BSPhysObject
43{
44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
45 private static readonly string LogHeader = "[BULLETS PRIM]";
46
47 // _size is what the user passed. Scale is what we pass to the physics engine with the mesh.
48 private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user
49
50 private bool _grabbed;
51 private bool _isSelected;
52 private bool _isVolumeDetect;
53 private OMV.Vector3 _position;
54 private float _mass; // the mass of this object
55 private float _density;
56 private OMV.Vector3 _force;
57 private OMV.Vector3 _velocity;
58 private OMV.Vector3 _torque;
59 private float _collisionScore;
60 private OMV.Vector3 _acceleration;
61 private OMV.Quaternion _orientation;
62 private int _physicsActorType;
63 private bool _isPhysical;
64 private bool _flying;
65 private float _friction;
66 private float _restitution;
67 private bool _setAlwaysRun;
68 private bool _throttleUpdates;
69 private bool _isColliding;
70 private bool _collidingGround;
71 private bool _collidingObj;
72 private bool _floatOnWater;
73 private OMV.Vector3 _rotationalVelocity;
74 private bool _kinematic;
75 private float _buoyancy;
76
77 private BSDynamics _vehicle;
78
79 private OMV.Vector3 _PIDTarget;
80 private bool _usePID;
81 private float _PIDTau;
82 private bool _useHoverPID;
83 private float _PIDHoverHeight;
84 private PIDHoverType _PIDHoverType;
85 private float _PIDHoverTao;
86
87 public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size,
88 OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical)
89 : base(parent_scene, localID, primName, "BSPrim")
90 {
91 // m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID);
92 _physicsActorType = (int)ActorTypes.Prim;
93 _position = pos;
94 _size = size;
95 Scale = size; // prims are the size the user wants them to be (different for BSCharactes).
96 _orientation = rotation;
97 _buoyancy = 1f;
98 _velocity = OMV.Vector3.Zero;
99 _rotationalVelocity = OMV.Vector3.Zero;
100 BaseShape = pbs;
101 _isPhysical = pisPhysical;
102 _isVolumeDetect = false;
103
104 // Someday set default attributes based on the material but, for now, we don't know the prim material yet.
105 // MaterialAttributes primMat = BSMaterials.GetAttributes(Material, pisPhysical);
106 _density = PhysicsScene.Params.defaultDensity;
107 _friction = PhysicsScene.Params.defaultFriction;
108 _restitution = PhysicsScene.Params.defaultRestitution;
109
110 _vehicle = new BSDynamics(PhysicsScene, this); // add vehicleness
111
112 _mass = CalculateMass();
113
114 // No body or shape yet
115 PhysBody = new BulletBody(LocalID);
116 PhysShape = new BulletShape();
117
118 DetailLog("{0},BSPrim.constructor,call", LocalID);
119 // do the actual object creation at taint time
120 PhysicsScene.TaintedObject("BSPrim.create", delegate()
121 {
122 CreateGeomAndObject(true);
123
124 CurrentCollisionFlags = BulletSimAPI.GetCollisionFlags2(PhysBody.ptr);
125 });
126 }
127
128 // called when this prim is being destroyed and we should free all the resources
129 public override void Destroy()
130 {
131 // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID);
132 base.Destroy();
133
134 // Undo any links between me and any other object
135 BSPhysObject parentBefore = Linkset.LinksetRoot;
136 int childrenBefore = Linkset.NumberOfChildren;
137
138 Linkset = Linkset.RemoveMeFromLinkset(this);
139
140 DetailLog("{0},BSPrim.Destroy,call,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}",
141 LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
142
143 // Undo any vehicle properties
144 this.VehicleType = (int)Vehicle.TYPE_NONE;
145
146 PhysicsScene.TaintedObject("BSPrim.destroy", delegate()
147 {
148 DetailLog("{0},BSPrim.Destroy,taint,", LocalID);
149 // If there are physical body and shape, release my use of same.
150 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
151 PhysBody.Clear();
152 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
153 PhysShape.Clear();
154 });
155 }
156
157 // No one uses this property.
158 public override bool Stopped {
159 get { return false; }
160 }
161 public override OMV.Vector3 Size {
162 get { return _size; }
163 set {
164 // We presume the scale and size are the same. If scale must be changed for
165 // the physical shape, that is done when the geometry is built.
166 _size = value;
167 Scale = _size;
168 ForceBodyShapeRebuild(false);
169 }
170 }
171
172 public override PrimitiveBaseShape Shape {
173 set {
174 BaseShape = value;
175 ForceBodyShapeRebuild(false);
176 }
177 }
178 // Whatever the linkset wants is what I want.
179 public override BSPhysicsShapeType PreferredPhysicalShape
180 { get { return Linkset.PreferredPhysicalShape(this); } }
181
182 public override bool ForceBodyShapeRebuild(bool inTaintTime)
183 {
184 LastAssetBuildFailed = false;
185 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ForceBodyShapeRebuild", delegate()
186 {
187 _mass = CalculateMass(); // changing the shape changes the mass
188 CreateGeomAndObject(true);
189 });
190 return true;
191 }
192 public override bool Grabbed {
193 set { _grabbed = value;
194 }
195 }
196 public override bool Selected {
197 set
198 {
199 if (value != _isSelected)
200 {
201 _isSelected = value;
202 PhysicsScene.TaintedObject("BSPrim.setSelected", delegate()
203 {
204 DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected);
205 SetObjectDynamic(false);
206 });
207 }
208 }
209 }
210 public override void CrossingFailure() { return; }
211
212 // link me to the specified parent
213 public override void link(PhysicsActor obj) {
214 BSPrim parent = obj as BSPrim;
215 if (parent != null)
216 {
217 BSPhysObject parentBefore = Linkset.LinksetRoot;
218 int childrenBefore = Linkset.NumberOfChildren;
219
220 Linkset = parent.Linkset.AddMeToLinkset(this);
221
222 DetailLog("{0},BSPrim.link,call,parentBefore={1}, childrenBefore=={2}, parentAfter={3}, childrenAfter={4}",
223 LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
224 }
225 return;
226 }
227
228 // delink me from my linkset
229 public override void delink() {
230 // TODO: decide if this parent checking needs to happen at taint time
231 // Race condition here: if link() and delink() in same simulation tick, the delink will not happen
232
233 BSPhysObject parentBefore = Linkset.LinksetRoot;
234 int childrenBefore = Linkset.NumberOfChildren;
235
236 Linkset = Linkset.RemoveMeFromLinkset(this);
237
238 DetailLog("{0},BSPrim.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ",
239 LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
240 return;
241 }
242
243 // Set motion values to zero.
244 // Do it to the properties so the values get set in the physics engine.
245 // Push the setting of the values to the viewer.
246 // Called at taint time!
247 public override void ZeroMotion(bool inTaintTime)
248 {
249 _velocity = OMV.Vector3.Zero;
250 _acceleration = OMV.Vector3.Zero;
251 _rotationalVelocity = OMV.Vector3.Zero;
252
253 // Zero some other properties in the physics engine
254 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
255 {
256 if (PhysBody.HasPhysicalBody)
257 BulletSimAPI.ClearAllForces2(PhysBody.ptr);
258 });
259 }
260 public override void ZeroAngularMotion(bool inTaintTime)
261 {
262 _rotationalVelocity = OMV.Vector3.Zero;
263 // Zero some other properties in the physics engine
264 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
265 {
266 // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity);
267 if (PhysBody.HasPhysicalBody)
268 {
269 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
270 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
271 }
272 });
273 }
274
275 public override void LockAngularMotion(OMV.Vector3 axis)
276 {
277 DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis);
278 return;
279 }
280
281 public override OMV.Vector3 RawPosition
282 {
283 get { return _position; }
284 set { _position = value; }
285 }
286 public override OMV.Vector3 Position {
287 get {
288 /* NOTE: this refetch is not necessary. The simulator knows about linkset children
289 * and does not fetch this position info for children. Thus this is commented out.
290 // child prims move around based on their parent. Need to get the latest location
291 if (!Linkset.IsRoot(this))
292 _position = Linkset.PositionGet(this);
293 */
294
295 // don't do the GetObjectPosition for root elements because this function is called a zillion times.
296 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
297 return _position;
298 }
299 set {
300 // If the position must be forced into the physics engine, use ForcePosition.
301 // All positions are given in world positions.
302 if (_position == value)
303 {
304 DetailLog("{0},BSPrim.setPosition,taint,positionNotChanging,pos={1},orient={2}", LocalID, _position, _orientation);
305 return;
306 }
307 _position = value;
308 PositionSanityCheck(false);
309
310 // A linkset might need to know if a component information changed.
311 Linkset.UpdateProperties(this, false);
312
313 PhysicsScene.TaintedObject("BSPrim.setPosition", delegate()
314 {
315 DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
316 ForcePosition = _position;
317 });
318 }
319 }
320 public override OMV.Vector3 ForcePosition {
321 get {
322 _position = BulletSimAPI.GetPosition2(PhysBody.ptr);
323 return _position;
324 }
325 set {
326 _position = value;
327 if (PhysBody.HasPhysicalBody)
328 {
329 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
330 ActivateIfPhysical(false);
331 }
332 }
333 }
334
335 // Check that the current position is sane and, if not, modify the position to make it so.
336 // Check for being below terrain and being out of bounds.
337 // Returns 'true' of the position was made sane by some action.
338 private bool PositionSanityCheck(bool inTaintTime)
339 {
340 bool ret = false;
341
342 if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position))
343 {
344 // The physical object is out of the known/simulated area.
345 // Upper levels of code will handle the transition to other areas so, for
346 // the time, we just ignore the position.
347 return ret;
348 }
349
350 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
351 OMV.Vector3 upForce = OMV.Vector3.Zero;
352 if (RawPosition.Z < terrainHeight)
353 {
354 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
355 float targetHeight = terrainHeight + (Size.Z / 2f);
356 // Upforce proportional to the distance away from the terrain. Correct the error in 1 sec.
357 upForce.Z = (terrainHeight - RawPosition.Z) * 1f;
358 ret = true;
359 }
360
361 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
362 {
363 float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
364 // TODO: a floating motor so object will bob in the water
365 if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f)
366 {
367 // Upforce proportional to the distance away from the water. Correct the error in 1 sec.
368 upForce.Z = (waterHeight - RawPosition.Z) * 1f;
369 ret = true;
370 }
371 }
372
373 // The above code computes a force to apply to correct any out-of-bounds problems. Apply same.
374 // TODO: This should be intergrated with a geneal physics action mechanism.
375 // TODO: This should be moderated with PID'ness.
376 if (ret)
377 {
378 // Apply upforce and overcome gravity.
379 OMV.Vector3 correctionForce = upForce - PhysicsScene.DefaultGravity;
380 DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, _position, upForce, correctionForce);
381 AddForce(correctionForce, false, inTaintTime);
382 }
383 return ret;
384 }
385
386 // Return the effective mass of the object.
387 // If there are multiple items in the linkset, add them together for the root
388 public override float Mass
389 {
390 get
391 {
392 return Linkset.LinksetMass;
393 // return _mass;
394 }
395 }
396
397 // used when we only want this prim's mass and not the linkset thing
398 public override float RawMass {
399 get { return _mass; }
400 }
401 // Set the physical mass to the passed mass.
402 // Note that this does not change _mass!
403 public override void UpdatePhysicalMassProperties(float physMass)
404 {
405 if (IsStatic)
406 {
407 Inertia = OMV.Vector3.Zero;
408 BulletSimAPI.SetMassProps2(PhysBody.ptr, 0f, Inertia);
409 BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr);
410 }
411 else
412 {
413 Inertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass);
414 BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, Inertia);
415 BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr);
416 // center of mass is at the zero of the object
417 // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(PhysBody.ptr, ForcePosition, ForceOrientation);
418 DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2}", LocalID, physMass, Inertia);
419 }
420 }
421
422 // Is this used?
423 public override OMV.Vector3 CenterOfMass
424 {
425 get { return Linkset.CenterOfMass; }
426 }
427
428 // Is this used?
429 public override OMV.Vector3 GeometricCenter
430 {
431 get { return Linkset.GeometricCenter; }
432 }
433
434 public override OMV.Vector3 Force {
435 get { return _force; }
436 set {
437 _force = value;
438 if (_force != OMV.Vector3.Zero)
439 {
440 // If the force is non-zero, it must be reapplied each tick because
441 // Bullet clears the forces applied last frame.
442 RegisterPreStepAction("BSPrim.setForce", LocalID,
443 delegate(float timeStep)
444 {
445 DetailLog("{0},BSPrim.setForce,preStep,force={1}", LocalID, _force);
446 if (PhysBody.HasPhysicalBody)
447 {
448 BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, _force);
449 ActivateIfPhysical(false);
450 }
451 }
452 );
453 }
454 else
455 {
456 UnRegisterPreStepAction("BSPrim.setForce", LocalID);
457 }
458 }
459 }
460
461 public override int VehicleType {
462 get {
463 return (int)_vehicle.Type; // if we are a vehicle, return that type
464 }
465 set {
466 Vehicle type = (Vehicle)value;
467
468 PhysicsScene.TaintedObject("setVehicleType", delegate()
469 {
470 // Done at taint time so we're sure the physics engine is not using the variables
471 // Vehicle code changes the parameters for this vehicle type.
472 _vehicle.ProcessTypeChange(type);
473 ActivateIfPhysical(false);
474
475 // If an active vehicle, register the vehicle code to be called before each step
476 if (_vehicle.Type == Vehicle.TYPE_NONE)
477 UnRegisterPreStepAction("BSPrim.Vehicle", LocalID);
478 else
479 RegisterPreStepAction("BSPrim.Vehicle", LocalID, _vehicle.Step);
480 });
481 }
482 }
483 public override void VehicleFloatParam(int param, float value)
484 {
485 PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate()
486 {
487 _vehicle.ProcessFloatVehicleParam((Vehicle)param, value);
488 ActivateIfPhysical(false);
489 });
490 }
491 public override void VehicleVectorParam(int param, OMV.Vector3 value)
492 {
493 PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate()
494 {
495 _vehicle.ProcessVectorVehicleParam((Vehicle)param, value);
496 ActivateIfPhysical(false);
497 });
498 }
499 public override void VehicleRotationParam(int param, OMV.Quaternion rotation)
500 {
501 PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate()
502 {
503 _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation);
504 ActivateIfPhysical(false);
505 });
506 }
507 public override void VehicleFlags(int param, bool remove)
508 {
509 PhysicsScene.TaintedObject("BSPrim.VehicleFlags", delegate()
510 {
511 _vehicle.ProcessVehicleFlags(param, remove);
512 });
513 }
514
515 // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more
516 public override void SetVolumeDetect(int param) {
517 bool newValue = (param != 0);
518 if (_isVolumeDetect != newValue)
519 {
520 _isVolumeDetect = newValue;
521 PhysicsScene.TaintedObject("BSPrim.SetVolumeDetect", delegate()
522 {
523 // DetailLog("{0},setVolumeDetect,taint,volDetect={1}", LocalID, _isVolumeDetect);
524 SetObjectDynamic(true);
525 });
526 }
527 return;
528 }
529 public override OMV.Vector3 Velocity {
530 get { return _velocity; }
531 set {
532 _velocity = value;
533 PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate()
534 {
535 // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity);
536 ForceVelocity = _velocity;
537 });
538 }
539 }
540 public override OMV.Vector3 ForceVelocity {
541 get { return _velocity; }
542 set {
543 PhysicsScene.AssertInTaintTime("BSPrim.ForceVelocity");
544
545 _velocity = value;
546 if (PhysBody.HasPhysicalBody)
547 {
548 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
549 ActivateIfPhysical(false);
550 }
551 }
552 }
553 public override OMV.Vector3 Torque {
554 get { return _torque; }
555 set {
556 _torque = value;
557 if (_torque != OMV.Vector3.Zero)
558 {
559 // If the torque is non-zero, it must be reapplied each tick because
560 // Bullet clears the forces applied last frame.
561 RegisterPreStepAction("BSPrim.setTorque", LocalID,
562 delegate(float timeStep)
563 {
564 if (PhysBody.HasPhysicalBody)
565 AddAngularForce(_torque, false, true);
566 }
567 );
568 }
569 else
570 {
571 UnRegisterPreStepAction("BSPrim.setTorque", LocalID);
572 }
573 // DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque);
574 }
575 }
576 public override float CollisionScore {
577 get { return _collisionScore; }
578 set { _collisionScore = value;
579 }
580 }
581 public override OMV.Vector3 Acceleration {
582 get { return _acceleration; }
583 set { _acceleration = value; }
584 }
585 public override OMV.Quaternion RawOrientation
586 {
587 get { return _orientation; }
588 set { _orientation = value; }
589 }
590 public override OMV.Quaternion Orientation {
591 get {
592 /* NOTE: this refetch is not necessary. The simulator knows about linkset children
593 * and does not fetch this position info for children. Thus this is commented out.
594 // Children move around because tied to parent. Get a fresh value.
595 if (!Linkset.IsRoot(this))
596 {
597 _orientation = Linkset.OrientationGet(this);
598 }
599 */
600 return _orientation;
601 }
602 set {
603 if (_orientation == value)
604 return;
605 _orientation = value;
606
607 // A linkset might need to know if a component information changed.
608 Linkset.UpdateProperties(this, false);
609
610 PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate()
611 {
612 if (PhysBody.HasPhysicalBody)
613 {
614 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
615 // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation);
616 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
617 }
618 });
619 }
620 }
621 // Go directly to Bullet to get/set the value.
622 public override OMV.Quaternion ForceOrientation
623 {
624 get
625 {
626 _orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr);
627 return _orientation;
628 }
629 set
630 {
631 _orientation = value;
632 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
633 }
634 }
635 public override int PhysicsActorType {
636 get { return _physicsActorType; }
637 set { _physicsActorType = value; }
638 }
639 public override bool IsPhysical {
640 get { return _isPhysical; }
641 set {
642 if (_isPhysical != value)
643 {
644 _isPhysical = value;
645 PhysicsScene.TaintedObject("BSPrim.setIsPhysical", delegate()
646 {
647 // DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical);
648 SetObjectDynamic(true);
649 // whether phys-to-static or static-to-phys, the object is not moving.
650 ZeroMotion(true);
651 });
652 }
653 }
654 }
655
656 // An object is static (does not move) if selected or not physical
657 public override bool IsStatic
658 {
659 get { return _isSelected || !IsPhysical; }
660 }
661
662 // An object is solid if it's not phantom and if it's not doing VolumeDetect
663 public override bool IsSolid
664 {
665 get { return !IsPhantom && !_isVolumeDetect; }
666 }
667
668 // Make gravity work if the object is physical and not selected
669 // Called at taint-time!!
670 private void SetObjectDynamic(bool forceRebuild)
671 {
672 // Recreate the physical object if necessary
673 CreateGeomAndObject(forceRebuild);
674 }
675
676 // Convert the simulator's physical properties into settings on BulletSim objects.
677 // There are four flags we're interested in:
678 // IsStatic: Object does not move, otherwise the object has mass and moves
679 // isSolid: other objects bounce off of this object
680 // isVolumeDetect: other objects pass through but can generate collisions
681 // collisionEvents: whether this object returns collision events
682 private void UpdatePhysicalParameters()
683 {
684 // DetailLog("{0},BSPrim.UpdatePhysicalParameters,entry,body={1},shape={2}", LocalID, BSBody, BSShape);
685
686 // Mangling all the physical properties requires the object not be in the physical world.
687 // This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found).
688 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
689
690 // Set up the object physicalness (does gravity and collisions move this object)
691 MakeDynamic(IsStatic);
692
693 // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters)
694 _vehicle.Refresh();
695
696 // Arrange for collision events if the simulator wants them
697 EnableCollisions(SubscribedEvents());
698
699 // Make solid or not (do things bounce off or pass through this object).
700 MakeSolid(IsSolid);
701
702 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr, _position, _orientation);
703
704 // Rebuild its shape
705 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
706
707 // Collision filter can be set only when the object is in the world
708 PhysBody.ApplyCollisionMask();
709
710 // Recompute any linkset parameters.
711 // When going from non-physical to physical, this re-enables the constraints that
712 // had been automatically disabled when the mass was set to zero.
713 // For compound based linksets, this enables and disables interactions of the children.
714 Linkset.Refresh(this);
715
716 DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}",
717 LocalID, IsStatic, IsSolid, _mass, SubscribedEvents(), CurrentCollisionFlags, PhysBody, PhysShape);
718 }
719
720 // "Making dynamic" means changing to and from static.
721 // When static, gravity does not effect the object and it is fixed in space.
722 // When dynamic, the object can fall and be pushed by others.
723 // This is independent of its 'solidness' which controls what passes through
724 // this object and what interacts with it.
725 private void MakeDynamic(bool makeStatic)
726 {
727 if (makeStatic)
728 {
729 // Become a Bullet 'static' object type
730 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
731 // Stop all movement
732 ZeroMotion(true);
733
734 // Set various physical properties so other object interact properly
735 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false);
736 BulletSimAPI.SetFriction2(PhysBody.ptr, matAttrib.friction);
737 BulletSimAPI.SetRestitution2(PhysBody.ptr, matAttrib.restitution);
738
739 // Mass is zero which disables a bunch of physics stuff in Bullet
740 UpdatePhysicalMassProperties(0f);
741 // Set collision detection parameters
742 if (BSParam.CcdMotionThreshold > 0f)
743 {
744 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold);
745 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius);
746 }
747
748 // The activation state is 'disabled' so Bullet will not try to act on it.
749 // BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
750 // Start it out sleeping and physical actions could wake it up.
751 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ISLAND_SLEEPING);
752
753 // This collides like a static object
754 PhysBody.collisionType = CollisionType.Static;
755
756 // There can be special things needed for implementing linksets
757 Linkset.MakeStatic(this);
758 }
759 else
760 {
761 // Not a Bullet static object
762 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
763
764 // Set various physical properties so other object interact properly
765 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, true);
766 BulletSimAPI.SetFriction2(PhysBody.ptr, matAttrib.friction);
767 BulletSimAPI.SetRestitution2(PhysBody.ptr, matAttrib.restitution);
768
769 // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382
770 // Since this can be called multiple times, only zero forces when becoming physical
771 // BulletSimAPI.ClearAllForces2(BSBody.ptr);
772
773 // For good measure, make sure the transform is set through to the motion state
774 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
775
776 // Center of mass is at the center of the object
777 // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation);
778
779 // A dynamic object has mass
780 UpdatePhysicalMassProperties(RawMass);
781
782 // Set collision detection parameters
783 if (BSParam.CcdMotionThreshold > 0f)
784 {
785 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold);
786 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius);
787 }
788
789 // Various values for simulation limits
790 BulletSimAPI.SetDamping2(PhysBody.ptr, BSParam.LinearDamping, BSParam.AngularDamping);
791 BulletSimAPI.SetDeactivationTime2(PhysBody.ptr, BSParam.DeactivationTime);
792 BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold);
793 BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, BSParam.ContactProcessingThreshold);
794
795 // This collides like an object.
796 PhysBody.collisionType = CollisionType.Dynamic;
797
798 // Force activation of the object so Bullet will act on it.
799 // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects.
800 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ACTIVE_TAG);
801
802 // There might be special things needed for implementing linksets.
803 Linkset.MakeDynamic(this);
804 }
805 }
806
807 // "Making solid" means that other object will not pass through this object.
808 // To make transparent, we create a Bullet ghost object.
809 // Note: This expects to be called from the UpdatePhysicalParameters() routine as
810 // the functions after this one set up the state of a possibly newly created collision body.
811 private void MakeSolid(bool makeSolid)
812 {
813 CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(PhysBody.ptr);
814 if (makeSolid)
815 {
816 // Verify the previous code created the correct shape for this type of thing.
817 if ((bodyType & CollisionObjectTypes.CO_RIGID_BODY) == 0)
818 {
819 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType);
820 }
821 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
822 }
823 else
824 {
825 if ((bodyType & CollisionObjectTypes.CO_GHOST_OBJECT) == 0)
826 {
827 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType);
828 }
829 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
830
831 // Change collision info from a static object to a ghosty collision object
832 PhysBody.collisionType = CollisionType.VolumeDetect;
833 }
834 }
835
836 // Enable physical actions. Bullet will keep sleeping non-moving physical objects so
837 // they need waking up when parameters are changed.
838 // Called in taint-time!!
839 private void ActivateIfPhysical(bool forceIt)
840 {
841 if (IsPhysical && PhysBody.HasPhysicalBody)
842 BulletSimAPI.Activate2(PhysBody.ptr, forceIt);
843 }
844
845 // Turn on or off the flag controlling whether collision events are returned to the simulator.
846 private void EnableCollisions(bool wantsCollisionEvents)
847 {
848 if (wantsCollisionEvents)
849 {
850 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
851 }
852 else
853 {
854 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
855 }
856 }
857
858 // prims don't fly
859 public override bool Flying {
860 get { return _flying; }
861 set {
862 _flying = value;
863 }
864 }
865 public override bool SetAlwaysRun {
866 get { return _setAlwaysRun; }
867 set { _setAlwaysRun = value; }
868 }
869 public override bool ThrottleUpdates {
870 get { return _throttleUpdates; }
871 set { _throttleUpdates = value; }
872 }
873 public override bool IsColliding {
874 get { return (CollidingStep == PhysicsScene.SimulationStep); }
875 set { _isColliding = value; }
876 }
877 public override bool CollidingGround {
878 get { return (CollidingGroundStep == PhysicsScene.SimulationStep); }
879 set { _collidingGround = value; }
880 }
881 public override bool CollidingObj {
882 get { return _collidingObj; }
883 set { _collidingObj = value; }
884 }
885 public bool IsPhantom {
886 get {
887 // SceneObjectPart removes phantom objects from the physics scene
888 // so, although we could implement touching and such, we never
889 // are invoked as a phantom object
890 return false;
891 }
892 }
893 public override bool FloatOnWater {
894 set {
895 _floatOnWater = value;
896 PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate()
897 {
898 if (_floatOnWater)
899 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
900 else
901 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
902 });
903 }
904 }
905 public override OMV.Vector3 RotationalVelocity {
906 get {
907 return _rotationalVelocity;
908 }
909 set {
910 _rotationalVelocity = value;
911 // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity);
912 PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
913 {
914 DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
915 ForceRotationalVelocity = _rotationalVelocity;
916 });
917 }
918 }
919 public override OMV.Vector3 ForceRotationalVelocity {
920 get {
921 return _rotationalVelocity;
922 }
923 set {
924 _rotationalVelocity = value;
925 if (PhysBody.HasPhysicalBody)
926 {
927 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
928 ActivateIfPhysical(false);
929 }
930 }
931 }
932 public override bool Kinematic {
933 get { return _kinematic; }
934 set { _kinematic = value;
935 // m_log.DebugFormat("{0}: Kinematic={1}", LogHeader, _kinematic);
936 }
937 }
938 public override float Buoyancy {
939 get { return _buoyancy; }
940 set {
941 _buoyancy = value;
942 PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate()
943 {
944 ForceBuoyancy = _buoyancy;
945 });
946 }
947 }
948 public override float ForceBuoyancy {
949 get { return _buoyancy; }
950 set {
951 _buoyancy = value;
952 // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
953 // Buoyancy is faked by changing the gravity applied to the object
954 if (PhysBody.HasPhysicalBody)
955 {
956 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
957 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
958 ActivateIfPhysical(false);
959 }
960 }
961 }
962
963 // Used for MoveTo
964 public override OMV.Vector3 PIDTarget {
965 set { _PIDTarget = value; }
966 }
967 public override bool PIDActive {
968 set { _usePID = value; }
969 }
970 public override float PIDTau {
971 set { _PIDTau = value; }
972 }
973
974 // Used for llSetHoverHeight and maybe vehicle height
975 // Hover Height will override MoveTo target's Z
976 public override bool PIDHoverActive {
977 set { _useHoverPID = value; }
978 }
979 public override float PIDHoverHeight {
980 set { _PIDHoverHeight = value; }
981 }
982 public override PIDHoverType PIDHoverType {
983 set { _PIDHoverType = value; }
984 }
985 public override float PIDHoverTau {
986 set { _PIDHoverTao = value; }
987 }
988
989 // For RotLookAt
990 public override OMV.Quaternion APIDTarget { set { return; } }
991 public override bool APIDActive { set { return; } }
992 public override float APIDStrength { set { return; } }
993 public override float APIDDamping { set { return; } }
994
995 public override void AddForce(OMV.Vector3 force, bool pushforce) {
996 AddForce(force, pushforce, false);
997 }
998 // Applying a force just adds this to the total force on the object.
999 // This added force will only last the next simulation tick.
1000 public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
1001 // for an object, doesn't matter if force is a pushforce or not
1002 if (force.IsFinite())
1003 {
1004 OMV.Vector3 addForce = force;
1005 DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce);
1006 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate()
1007 {
1008 // Bullet adds this central force to the total force for this tick
1009 DetailLog("{0},BSPrim.addForce,taint,force={1}", LocalID, addForce);
1010 if (PhysBody.HasPhysicalBody)
1011 {
1012 BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, addForce);
1013 ActivateIfPhysical(false);
1014 }
1015 });
1016 }
1017 else
1018 {
1019 m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID);
1020 return;
1021 }
1022 }
1023
1024 public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
1025 AddAngularForce(force, pushforce, false);
1026 }
1027 public void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime)
1028 {
1029 if (force.IsFinite())
1030 {
1031 OMV.Vector3 angForce = force;
1032 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate()
1033 {
1034 if (PhysBody.HasPhysicalBody)
1035 {
1036 BulletSimAPI.ApplyTorque2(PhysBody.ptr, angForce);
1037 ActivateIfPhysical(false);
1038 }
1039 });
1040 }
1041 else
1042 {
1043 m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID);
1044 return;
1045 }
1046 }
1047
1048 // A torque impulse.
1049 // ApplyTorqueImpulse adds torque directly to the angularVelocity.
1050 // AddAngularForce accumulates the force and applied it to the angular velocity all at once.
1051 // Computed as: angularVelocity += impulse * inertia;
1052 public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime)
1053 {
1054 OMV.Vector3 applyImpulse = impulse;
1055 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate()
1056 {
1057 if (PhysBody.HasPhysicalBody)
1058 {
1059 BulletSimAPI.ApplyTorqueImpulse2(PhysBody.ptr, applyImpulse);
1060 ActivateIfPhysical(false);
1061 }
1062 });
1063 }
1064
1065 public override void SetMomentum(OMV.Vector3 momentum) {
1066 // DetailLog("{0},BSPrim.SetMomentum,call,mom={1}", LocalID, momentum);
1067 }
1068 #region Mass Calculation
1069
1070 private float CalculateMass()
1071 {
1072 float volume = _size.X * _size.Y * _size.Z; // default
1073 float tmp;
1074
1075 float returnMass = 0;
1076 float hollowAmount = (float)BaseShape.ProfileHollow * 2.0e-5f;
1077 float hollowVolume = hollowAmount * hollowAmount;
1078
1079 switch (BaseShape.ProfileShape)
1080 {
1081 case ProfileShape.Square:
1082 // default box
1083
1084 if (BaseShape.PathCurve == (byte)Extrusion.Straight)
1085 {
1086 if (hollowAmount > 0.0)
1087 {
1088 switch (BaseShape.HollowShape)
1089 {
1090 case HollowShape.Square:
1091 case HollowShape.Same:
1092 break;
1093
1094 case HollowShape.Circle:
1095
1096 hollowVolume *= 0.78539816339f;
1097 break;
1098
1099 case HollowShape.Triangle:
1100
1101 hollowVolume *= (0.5f * .5f);
1102 break;
1103
1104 default:
1105 hollowVolume = 0;
1106 break;
1107 }
1108 volume *= (1.0f - hollowVolume);
1109 }
1110 }
1111
1112 else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
1113 {
1114 //a tube
1115
1116 volume *= 0.78539816339e-2f * (float)(200 - BaseShape.PathScaleX);
1117 tmp= 1.0f -2.0e-2f * (float)(200 - BaseShape.PathScaleY);
1118 volume -= volume*tmp*tmp;
1119
1120 if (hollowAmount > 0.0)
1121 {
1122 hollowVolume *= hollowAmount;
1123
1124 switch (BaseShape.HollowShape)
1125 {
1126 case HollowShape.Square:
1127 case HollowShape.Same:
1128 break;
1129
1130 case HollowShape.Circle:
1131 hollowVolume *= 0.78539816339f;;
1132 break;
1133
1134 case HollowShape.Triangle:
1135 hollowVolume *= 0.5f * 0.5f;
1136 break;
1137 default:
1138 hollowVolume = 0;
1139 break;
1140 }
1141 volume *= (1.0f - hollowVolume);
1142 }
1143 }
1144
1145 break;
1146
1147 case ProfileShape.Circle:
1148
1149 if (BaseShape.PathCurve == (byte)Extrusion.Straight)
1150 {
1151 volume *= 0.78539816339f; // elipse base
1152
1153 if (hollowAmount > 0.0)
1154 {
1155 switch (BaseShape.HollowShape)
1156 {
1157 case HollowShape.Same:
1158 case HollowShape.Circle:
1159 break;
1160
1161 case HollowShape.Square:
1162 hollowVolume *= 0.5f * 2.5984480504799f;
1163 break;
1164
1165 case HollowShape.Triangle:
1166 hollowVolume *= .5f * 1.27323954473516f;
1167 break;
1168
1169 default:
1170 hollowVolume = 0;
1171 break;
1172 }
1173 volume *= (1.0f - hollowVolume);
1174 }
1175 }
1176
1177 else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
1178 {
1179 volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - BaseShape.PathScaleX);
1180 tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
1181 volume *= (1.0f - tmp * tmp);
1182
1183 if (hollowAmount > 0.0)
1184 {
1185
1186 // calculate the hollow volume by it's shape compared to the prim shape
1187 hollowVolume *= hollowAmount;
1188
1189 switch (BaseShape.HollowShape)
1190 {
1191 case HollowShape.Same:
1192 case HollowShape.Circle:
1193 break;
1194
1195 case HollowShape.Square:
1196 hollowVolume *= 0.5f * 2.5984480504799f;
1197 break;
1198
1199 case HollowShape.Triangle:
1200 hollowVolume *= .5f * 1.27323954473516f;
1201 break;
1202
1203 default:
1204 hollowVolume = 0;
1205 break;
1206 }
1207 volume *= (1.0f - hollowVolume);
1208 }
1209 }
1210 break;
1211
1212 case ProfileShape.HalfCircle:
1213 if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
1214 {
1215 volume *= 0.52359877559829887307710723054658f;
1216 }
1217 break;
1218
1219 case ProfileShape.EquilateralTriangle:
1220
1221 if (BaseShape.PathCurve == (byte)Extrusion.Straight)
1222 {
1223 volume *= 0.32475953f;
1224
1225 if (hollowAmount > 0.0)
1226 {
1227
1228 // calculate the hollow volume by it's shape compared to the prim shape
1229 switch (BaseShape.HollowShape)
1230 {
1231 case HollowShape.Same:
1232 case HollowShape.Triangle:
1233 hollowVolume *= .25f;
1234 break;
1235
1236 case HollowShape.Square:
1237 hollowVolume *= 0.499849f * 3.07920140172638f;
1238 break;
1239
1240 case HollowShape.Circle:
1241 // Hollow shape is a perfect cyllinder in respect to the cube's scale
1242 // Cyllinder hollow volume calculation
1243
1244 hollowVolume *= 0.1963495f * 3.07920140172638f;
1245 break;
1246
1247 default:
1248 hollowVolume = 0;
1249 break;
1250 }
1251 volume *= (1.0f - hollowVolume);
1252 }
1253 }
1254 else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
1255 {
1256 volume *= 0.32475953f;
1257 volume *= 0.01f * (float)(200 - BaseShape.PathScaleX);
1258 tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
1259 volume *= (1.0f - tmp * tmp);
1260
1261 if (hollowAmount > 0.0)
1262 {
1263
1264 hollowVolume *= hollowAmount;
1265
1266 switch (BaseShape.HollowShape)
1267 {
1268 case HollowShape.Same:
1269 case HollowShape.Triangle:
1270 hollowVolume *= .25f;
1271 break;
1272
1273 case HollowShape.Square:
1274 hollowVolume *= 0.499849f * 3.07920140172638f;
1275 break;
1276
1277 case HollowShape.Circle:
1278
1279 hollowVolume *= 0.1963495f * 3.07920140172638f;
1280 break;
1281
1282 default:
1283 hollowVolume = 0;
1284 break;
1285 }
1286 volume *= (1.0f - hollowVolume);
1287 }
1288 }
1289 break;
1290
1291 default:
1292 break;
1293 }
1294
1295
1296
1297 float taperX1;
1298 float taperY1;
1299 float taperX;
1300 float taperY;
1301 float pathBegin;
1302 float pathEnd;
1303 float profileBegin;
1304 float profileEnd;
1305
1306 if (BaseShape.PathCurve == (byte)Extrusion.Straight || BaseShape.PathCurve == (byte)Extrusion.Flexible)
1307 {
1308 taperX1 = BaseShape.PathScaleX * 0.01f;
1309 if (taperX1 > 1.0f)
1310 taperX1 = 2.0f - taperX1;
1311 taperX = 1.0f - taperX1;
1312
1313 taperY1 = BaseShape.PathScaleY * 0.01f;
1314 if (taperY1 > 1.0f)
1315 taperY1 = 2.0f - taperY1;
1316 taperY = 1.0f - taperY1;
1317 }
1318 else
1319 {
1320 taperX = BaseShape.PathTaperX * 0.01f;
1321 if (taperX < 0.0f)
1322 taperX = -taperX;
1323 taperX1 = 1.0f - taperX;
1324
1325 taperY = BaseShape.PathTaperY * 0.01f;
1326 if (taperY < 0.0f)
1327 taperY = -taperY;
1328 taperY1 = 1.0f - taperY;
1329
1330 }
1331
1332
1333 volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY);
1334
1335 pathBegin = (float)BaseShape.PathBegin * 2.0e-5f;
1336 pathEnd = 1.0f - (float)BaseShape.PathEnd * 2.0e-5f;
1337 volume *= (pathEnd - pathBegin);
1338
1339 // this is crude aproximation
1340 profileBegin = (float)BaseShape.ProfileBegin * 2.0e-5f;
1341 profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f;
1342 volume *= (profileEnd - profileBegin);
1343
1344 returnMass = _density * volume;
1345
1346 /* Comment out code that computes the mass of the linkset. That is done in the Linkset class.
1347 if (IsRootOfLinkset)
1348 {
1349 foreach (BSPrim prim in _childrenPrims)
1350 {
1351 returnMass += prim.CalculateMass();
1352 }
1353 }
1354 */
1355
1356 returnMass = Util.Clamp(returnMass, BSParam.MinimumObjectMass, BSParam.MaximumObjectMass);
1357
1358 return returnMass;
1359 }// end CalculateMass
1360 #endregion Mass Calculation
1361
1362 // Rebuild the geometry and object.
1363 // This is called when the shape changes so we need to recreate the mesh/hull.
1364 // Called at taint-time!!!
1365 public void CreateGeomAndObject(bool forceRebuild)
1366 {
1367 // If this prim is part of a linkset, we must remove and restore the physical
1368 // links if the body is rebuilt.
1369 bool needToRestoreLinkset = false;
1370 bool needToRestoreVehicle = false;
1371
1372 // Create the correct physical representation for this type of object.
1373 // Updates PhysBody and PhysShape with the new information.
1374 // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary.
1375 PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, null, delegate(BulletBody dBody)
1376 {
1377 // Called if the current prim body is about to be destroyed.
1378 // Remove all the physical dependencies on the old body.
1379 // (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...)
1380 needToRestoreLinkset = Linkset.RemoveBodyDependencies(this);
1381 needToRestoreVehicle = _vehicle.RemoveBodyDependencies(this);
1382 });
1383
1384 if (needToRestoreLinkset)
1385 {
1386 // If physical body dependencies were removed, restore them
1387 Linkset.RestoreBodyDependencies(this);
1388 }
1389 if (needToRestoreVehicle)
1390 {
1391 // If physical body dependencies were removed, restore them
1392 _vehicle.RestoreBodyDependencies(this);
1393 }
1394
1395 // Make sure the properties are set on the new object
1396 UpdatePhysicalParameters();
1397 return;
1398 }
1399
1400 // The physics engine says that properties have updated. Update same and inform
1401 // the world that things have changed.
1402 // TODO: do we really need to check for changed? Maybe just copy values and call RequestPhysicsterseUpdate()
1403 enum UpdatedProperties {
1404 Position = 1 << 0,
1405 Rotation = 1 << 1,
1406 Velocity = 1 << 2,
1407 Acceleration = 1 << 3,
1408 RotationalVel = 1 << 4
1409 }
1410
1411 const float ROTATION_TOLERANCE = 0.01f;
1412 const float VELOCITY_TOLERANCE = 0.001f;
1413 const float POSITION_TOLERANCE = 0.05f;
1414 const float ACCELERATION_TOLERANCE = 0.01f;
1415 const float ROTATIONAL_VELOCITY_TOLERANCE = 0.01f;
1416
1417 public override void UpdateProperties(EntityProperties entprop)
1418 {
1419 // Updates only for individual prims and for the root object of a linkset.
1420 if (Linkset.IsRoot(this))
1421 {
1422 // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet
1423 // TODO: handle physics introduced by Bullet with computed vehicle physics.
1424 if (_vehicle.IsActive)
1425 {
1426 entprop.RotationalVelocity = OMV.Vector3.Zero;
1427 }
1428
1429 // Assign directly to the local variables so the normal set action does not happen
1430 _position = entprop.Position;
1431 _orientation = entprop.Rotation;
1432 _velocity = entprop.Velocity;
1433 _acceleration = entprop.Acceleration;
1434 _rotationalVelocity = entprop.RotationalVelocity;
1435
1436 // The sanity check can change the velocity and/or position.
1437 if (IsPhysical && PositionSanityCheck(true))
1438 {
1439 entprop.Position = _position;
1440 entprop.Velocity = _velocity;
1441 }
1442
1443 OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; // DEBUG DEBUG DEBUG
1444 DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}",
1445 LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity);
1446
1447 // remember the current and last set values
1448 LastEntityProperties = CurrentEntityProperties;
1449 CurrentEntityProperties = entprop;
1450
1451 base.RequestPhysicsterseUpdate();
1452 }
1453 /*
1454 else
1455 {
1456 // For debugging, report the movement of children
1457 DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
1458 LocalID, entprop.Position, entprop.Rotation, entprop.Velocity,
1459 entprop.Acceleration, entprop.RotationalVelocity);
1460 }
1461 */
1462
1463 // The linkset implimentation might want to know about this.
1464 Linkset.UpdateProperties(this, true);
1465 }
1466}
1467}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSScene.cs
new file mode 100644
index 0000000..6bcea3f
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSScene.cs
@@ -0,0 +1,954 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Runtime.InteropServices;
30using System.Text;
31using System.Threading;
32using OpenSim.Framework;
33using OpenSim.Region.Framework;
34using OpenSim.Region.CoreModules;
35using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging;
36using OpenSim.Region.Physics.Manager;
37using Nini.Config;
38using log4net;
39using OpenMetaverse;
40
41// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
42// Based on material, set density and friction
43// More efficient memory usage when passing hull information from BSPrim to BulletSim
44// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
45// Implement LockAngularMotion
46// Add PID movement operations. What does ScenePresence.MoveToTarget do?
47// Check terrain size. 128 or 127?
48// Raycast
49//
50namespace OpenSim.Region.Physics.BulletSNPlugin
51{
52public sealed class BSScene : PhysicsScene, IPhysicsParameters
53{
54 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
55 private static readonly string LogHeader = "[BULLETS SCENE]";
56
57 // The name of the region we're working for.
58 public string RegionName { get; private set; }
59
60 public string BulletSimVersion = "?";
61
62 public Dictionary<uint, BSPhysObject> PhysObjects;
63 public BSShapeCollection Shapes;
64
65 // Keeping track of the objects with collisions so we can report begin and end of a collision
66 public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>();
67 public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>();
68 // Keep track of all the avatars so we can send them a collision event
69 // every tick so OpenSim will update its animation.
70 private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>();
71
72 // let my minuions use my logger
73 public ILog Logger { get { return m_log; } }
74
75 public IMesher mesher;
76 public uint WorldID { get; private set; }
77 public BulletSim World { get; private set; }
78
79 // All the constraints that have been allocated in this instance.
80 public BSConstraintCollection Constraints { get; private set; }
81
82 // Simulation parameters
83 internal int m_maxSubSteps;
84 internal float m_fixedTimeStep;
85 internal long m_simulationStep = 0;
86 public long SimulationStep { get { return m_simulationStep; } }
87 internal int m_taintsToProcessPerStep;
88 internal float LastTimeStep { get; private set; }
89
90 // Physical objects can register for prestep or poststep events
91 public delegate void PreStepAction(float timeStep);
92 public delegate void PostStepAction(float timeStep);
93 public event PreStepAction BeforeStep;
94 public event PreStepAction AfterStep;
95
96 // A value of the time now so all the collision and update routines do not have to get their own
97 // Set to 'now' just before all the prims and actors are called for collisions and updates
98 public int SimulationNowTime { get; private set; }
99
100 // True if initialized and ready to do simulation steps
101 private bool m_initialized = false;
102
103 // Flag which is true when processing taints.
104 // Not guaranteed to be correct all the time (don't depend on this) but good for debugging.
105 public bool InTaintTime { get; private set; }
106
107 // Pinned memory used to pass step information between managed and unmanaged
108 internal int m_maxCollisionsPerFrame;
109 private List<BulletXNA.CollisionDesc> m_collisionArray;
110 //private GCHandle m_collisionArrayPinnedHandle;
111
112 internal int m_maxUpdatesPerFrame;
113 private List<BulletXNA.EntityProperties> m_updateArray;
114 //private GCHandle m_updateArrayPinnedHandle;
115
116
117 public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero
118 public const uint GROUNDPLANE_ID = 1;
119 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
120
121 public float SimpleWaterLevel { get; set; }
122 public BSTerrainManager TerrainManager { get; private set; }
123
124 public ConfigurationParameters Params
125 {
126 get { return UnmanagedParams[0]; }
127 }
128 public Vector3 DefaultGravity
129 {
130 get { return new Vector3(0f, 0f, Params.gravity); }
131 }
132 // Just the Z value of the gravity
133 public float DefaultGravityZ
134 {
135 get { return Params.gravity; }
136 }
137
138 // When functions in the unmanaged code must be called, it is only
139 // done at a known time just before the simulation step. The taint
140 // system saves all these function calls and executes them in
141 // order before the simulation.
142 public delegate void TaintCallback();
143 private struct TaintCallbackEntry
144 {
145 public String ident;
146 public TaintCallback callback;
147 public TaintCallbackEntry(string i, TaintCallback c)
148 {
149 ident = i;
150 callback = c;
151 }
152 }
153 private Object _taintLock = new Object(); // lock for using the next object
154 private List<TaintCallbackEntry> _taintOperations;
155 private Dictionary<string, TaintCallbackEntry> _postTaintOperations;
156 private List<TaintCallbackEntry> _postStepOperations;
157
158 // A pointer to an instance if this structure is passed to the C++ code
159 // Used to pass basic configuration values to the unmanaged code.
160 internal ConfigurationParameters[] UnmanagedParams;
161 //GCHandle m_paramsHandle;
162
163 // Handle to the callback used by the unmanaged code to call into the managed code.
164 // Used for debug logging.
165 // Need to store the handle in a persistant variable so it won't be freed.
166 private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle;
167
168 // Sometimes you just have to log everything.
169 public Logging.LogWriter PhysicsLogging;
170 private bool m_physicsLoggingEnabled;
171 private string m_physicsLoggingDir;
172 private string m_physicsLoggingPrefix;
173 private int m_physicsLoggingFileMinutes;
174 private bool m_physicsLoggingDoFlush;
175 // 'true' of the vehicle code is to log lots of details
176 public bool VehicleLoggingEnabled { get; private set; }
177 public bool VehiclePhysicalLoggingEnabled { get; private set; }
178
179 #region Construction and Initialization
180 public BSScene(string identifier)
181 {
182 m_initialized = false;
183 // we are passed the name of the region we're working for.
184 RegionName = identifier;
185 }
186
187 public override void Initialise(IMesher meshmerizer, IConfigSource config)
188 {
189 mesher = meshmerizer;
190 _taintOperations = new List<TaintCallbackEntry>();
191 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
192 _postStepOperations = new List<TaintCallbackEntry>();
193 PhysObjects = new Dictionary<uint, BSPhysObject>();
194 Shapes = new BSShapeCollection(this);
195
196 // Allocate pinned memory to pass parameters.
197 UnmanagedParams = new ConfigurationParameters[1];
198 //m_paramsHandle = GCHandle.Alloc(UnmanagedParams, GCHandleType.Pinned);
199
200 // Set default values for physics parameters plus any overrides from the ini file
201 GetInitialParameterValues(config);
202
203 // allocate more pinned memory close to the above in an attempt to get the memory all together
204 m_collisionArray = new List<BulletXNA.CollisionDesc>();
205 //m_collisionArrayPinnedHandle = GCHandle.Alloc(m_collisionArray, GCHandleType.Pinned);
206 m_updateArray = new List<BulletXNA.EntityProperties>();
207 //m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned);
208
209 // Enable very detailed logging.
210 // By creating an empty logger when not logging, the log message invocation code
211 // can be left in and every call doesn't have to check for null.
212 if (m_physicsLoggingEnabled)
213 {
214 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes);
215 PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output error messages.
216 }
217 else
218 {
219 PhysicsLogging = new Logging.LogWriter();
220 }
221
222 // If Debug logging level, enable logging from the unmanaged code
223 m_DebugLogCallbackHandle = null;
224 if (m_log.IsDebugEnabled || PhysicsLogging.Enabled)
225 {
226 m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader);
227 if (PhysicsLogging.Enabled)
228 // The handle is saved in a variable to make sure it doesn't get freed after this call
229 m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLoggerPhysLog);
230 else
231 m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger);
232 }
233
234 // Get the version of the DLL
235 // TODO: this doesn't work yet. Something wrong with marshaling the returned string.
236 // BulletSimVersion = BulletSimAPI.GetVersion();
237 // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion);
238
239 // The bounding box for the simulated world. The origin is 0,0,0 unless we're
240 // a child in a mega-region.
241 // Bullet actually doesn't care about the extents of the simulated
242 // area. It tracks active objects no matter where they are.
243 Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
244
245 // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
246
247 World = new BulletSim(0, this, BulletSimAPI.Initialize2(worldExtent, UnmanagedParams,
248 m_maxCollisionsPerFrame, ref m_collisionArray,
249 m_maxUpdatesPerFrame,ref m_updateArray,
250 m_DebugLogCallbackHandle));
251
252 Constraints = new BSConstraintCollection(World);
253
254 TerrainManager = new BSTerrainManager(this);
255 TerrainManager.CreateInitialGroundPlaneAndTerrain();
256
257 m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation);
258
259 InTaintTime = false;
260 m_initialized = true;
261 }
262
263 // All default parameter values are set here. There should be no values set in the
264 // variable definitions.
265 private void GetInitialParameterValues(IConfigSource config)
266 {
267 ConfigurationParameters parms = new ConfigurationParameters();
268 UnmanagedParams[0] = parms;
269
270 BSParam.SetParameterDefaultValues(this);
271
272 if (config != null)
273 {
274 // If there are specifications in the ini file, use those values
275 IConfig pConfig = config.Configs["BulletSim"];
276 if (pConfig != null)
277 {
278 BSParam.SetParameterConfigurationValues(this, pConfig);
279
280 // Very detailed logging for physics debugging
281 m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false);
282 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
283 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
284 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
285 m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
286 // Very detailed logging for vehicle debugging
287 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
288 VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);
289
290 // Do any replacements in the parameters
291 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
292 }
293
294 // The material characteristics.
295 BSMaterials.InitializeFromDefaults(Params);
296 if (pConfig != null)
297 {
298 // Let the user add new and interesting material property values.
299 BSMaterials.InitializefromParameters(pConfig);
300 }
301 }
302 }
303
304 // A helper function that handles a true/false parameter and returns the proper float number encoding
305 float ParamBoolean(IConfig config, string parmName, float deflt)
306 {
307 float ret = deflt;
308 if (config.Contains(parmName))
309 {
310 ret = ConfigurationParameters.numericFalse;
311 if (config.GetBoolean(parmName, false))
312 {
313 ret = ConfigurationParameters.numericTrue;
314 }
315 }
316 return ret;
317 }
318
319 // Called directly from unmanaged code so don't do much
320 private void BulletLogger(string msg)
321 {
322 m_log.Debug("[BULLETS UNMANAGED]:" + msg);
323 }
324
325 // Called directly from unmanaged code so don't do much
326 private void BulletLoggerPhysLog(string msg)
327 {
328 DetailLog("[BULLETS UNMANAGED]:" + msg);
329 }
330
331 public override void Dispose()
332 {
333 // m_log.DebugFormat("{0}: Dispose()", LogHeader);
334
335 // make sure no stepping happens while we're deleting stuff
336 m_initialized = false;
337
338 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
339 {
340 kvp.Value.Destroy();
341 }
342 PhysObjects.Clear();
343
344 // Now that the prims are all cleaned up, there should be no constraints left
345 if (Constraints != null)
346 {
347 Constraints.Dispose();
348 Constraints = null;
349 }
350
351 if (Shapes != null)
352 {
353 Shapes.Dispose();
354 Shapes = null;
355 }
356
357 if (TerrainManager != null)
358 {
359 TerrainManager.ReleaseGroundPlaneAndTerrain();
360 TerrainManager.Dispose();
361 TerrainManager = null;
362 }
363
364 // Anything left in the unmanaged code should be cleaned out
365 BulletSimAPI.Shutdown2(World.ptr);
366
367 // Not logging any more
368 PhysicsLogging.Close();
369 }
370 #endregion // Construction and Initialization
371
372 #region Prim and Avatar addition and removal
373
374 public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
375 {
376 m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
377 return null;
378 }
379
380 public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying)
381 {
382 // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName);
383
384 if (!m_initialized) return null;
385
386 BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying);
387 lock (PhysObjects) PhysObjects.Add(localID, actor);
388
389 // TODO: Remove kludge someday.
390 // We must generate a collision for avatars whether they collide or not.
391 // This is required by OpenSim to update avatar animations, etc.
392 lock (m_avatars) m_avatars.Add(actor);
393
394 return actor;
395 }
396
397 public override void RemoveAvatar(PhysicsActor actor)
398 {
399 // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader);
400
401 if (!m_initialized) return;
402
403 BSCharacter bsactor = actor as BSCharacter;
404 if (bsactor != null)
405 {
406 try
407 {
408 lock (PhysObjects) PhysObjects.Remove(actor.LocalID);
409 // Remove kludge someday
410 lock (m_avatars) m_avatars.Remove(bsactor);
411 }
412 catch (Exception e)
413 {
414 m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e);
415 }
416 bsactor.Destroy();
417 // bsactor.dispose();
418 }
419 }
420
421 public override void RemovePrim(PhysicsActor prim)
422 {
423 if (!m_initialized) return;
424
425 BSPrim bsprim = prim as BSPrim;
426 if (bsprim != null)
427 {
428 DetailLog("{0},RemovePrim,call", bsprim.LocalID);
429 // m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID);
430 try
431 {
432 lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID);
433 }
434 catch (Exception e)
435 {
436 m_log.ErrorFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e);
437 }
438 bsprim.Destroy();
439 // bsprim.dispose();
440 }
441 else
442 {
443 m_log.ErrorFormat("{0}: Attempt to remove prim that is not a BSPrim type.", LogHeader);
444 }
445 }
446
447 public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
448 Vector3 size, Quaternion rotation, bool isPhysical, uint localID)
449 {
450 // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName);
451
452 if (!m_initialized) return null;
453
454 DetailLog("{0},AddPrimShape,call", localID);
455
456 BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical);
457 lock (PhysObjects) PhysObjects.Add(localID, prim);
458 return prim;
459 }
460
461 // This is a call from the simulator saying that some physical property has been updated.
462 // The BulletSim driver senses the changing of relevant properties so this taint
463 // information call is not needed.
464 public override void AddPhysicsActorTaint(PhysicsActor prim) { }
465
466 #endregion // Prim and Avatar addition and removal
467
468 #region Simulation
469 // Simulate one timestep
470 public override float Simulate(float timeStep)
471 {
472 // prevent simulation until we've been initialized
473 if (!m_initialized) return 5.0f;
474
475 LastTimeStep = timeStep;
476
477 int updatedEntityCount = 0;
478 //Object updatedEntitiesPtr;
479 int collidersCount = 0;
480 //Object collidersPtr;
481
482 int beforeTime = 0;
483 int simTime = 0;
484
485 // update the prim states while we know the physics engine is not busy
486 int numTaints = _taintOperations.Count;
487
488 InTaintTime = true; // Only used for debugging so locking is not necessary.
489
490 ProcessTaints();
491
492 // Some of the physical objects requre individual, pre-step calls
493 TriggerPreStepEvent(timeStep);
494
495 // the prestep actions might have added taints
496 ProcessTaints();
497
498 InTaintTime = false; // Only used for debugging so locking is not necessary.
499
500 // step the physical world one interval
501 m_simulationStep++;
502 int numSubSteps = 0;
503
504 try
505 {
506 //if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG
507 if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
508
509 numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
510 out updatedEntityCount, out m_updateArray, out collidersCount, out m_collisionArray);
511
512 if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
513 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
514 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
515 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
516 }
517 catch (Exception e)
518 {
519 m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}",
520 LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e);
521 DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}",
522 DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
523 updatedEntityCount = 0;
524 collidersCount = 0;
525 }
526
527 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in.
528
529 // Get a value for 'now' so all the collision and update routines don't have to get their own.
530 SimulationNowTime = Util.EnvironmentTickCount();
531
532 // If there were collisions, process them by sending the event to the prim.
533 // Collisions must be processed before updates.
534 if (collidersCount > 0)
535 {
536 for (int ii = 0; ii < collidersCount; ii++)
537 {
538 uint cA = m_collisionArray[ii].aID;
539 uint cB = m_collisionArray[ii].bID;
540 Vector3 point = new Vector3(m_collisionArray[ii].point.X, m_collisionArray[ii].point.Y,
541 m_collisionArray[ii].point.Z);
542 Vector3 normal = new Vector3(m_collisionArray[ii].normal.X, m_collisionArray[ii].normal.Y,
543 m_collisionArray[ii].normal.Z);
544 SendCollision(cA, cB, point, normal, 0.01f);
545 SendCollision(cB, cA, point, -normal, 0.01f);
546 }
547 }
548
549 // The above SendCollision's batch up the collisions on the objects.
550 // Now push the collisions into the simulator.
551 if (ObjectsWithCollisions.Count > 0)
552 {
553 foreach (BSPhysObject bsp in ObjectsWithCollisions)
554 if (!bsp.SendCollisions())
555 {
556 // If the object is done colliding, see that it's removed from the colliding list
557 ObjectsWithNoMoreCollisions.Add(bsp);
558 }
559 }
560
561 // This is a kludge to get avatar movement updates.
562 // The simulator expects collisions for avatars even if there are have been no collisions.
563 // The event updates avatar animations and stuff.
564 // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
565 foreach (BSPhysObject bsp in m_avatars)
566 if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
567 bsp.SendCollisions();
568
569 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
570 // Not done above because it is inside an iteration of ObjectWithCollisions.
571 // This complex collision processing is required to create an empty collision
572 // event call after all collisions have happened on an object. This enables
573 // the simulator to generate the 'collision end' event.
574 if (ObjectsWithNoMoreCollisions.Count > 0)
575 {
576 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
577 ObjectsWithCollisions.Remove(po);
578 ObjectsWithNoMoreCollisions.Clear();
579 }
580 // Done with collisions.
581
582 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
583 if (updatedEntityCount > 0)
584 {
585 for (int ii = 0; ii < updatedEntityCount; ii++)
586 {
587
588 BulletXNA.EntityProperties entprop = m_updateArray[ii];
589 BSPhysObject pobj;
590 if (PhysObjects.TryGetValue(entprop.ID, out pobj))
591 {
592 EntityProperties prop = new EntityProperties()
593 {
594 Acceleration = new Vector3(entprop.Acceleration.X, entprop.Acceleration.Y, entprop.Acceleration.Z),
595 ID = entprop.ID,
596 Position = new Vector3(entprop.Position.X,entprop.Position.Y,entprop.Position.Z),
597 Rotation = new Quaternion(entprop.Rotation.X,entprop.Rotation.Y,entprop.Rotation.Z,entprop.Rotation.W),
598 RotationalVelocity = new Vector3(entprop.AngularVelocity.X,entprop.AngularVelocity.Y,entprop.AngularVelocity.Z),
599 Velocity = new Vector3(entprop.Velocity.X,entprop.Velocity.Y,entprop.Velocity.Z)
600 };
601 //m_log.Debug(pobj.Name + ":" + prop.ToString() + "\n");
602 pobj.UpdateProperties(prop);
603 }
604 }
605 }
606
607 TriggerPostStepEvent(timeStep);
608
609 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
610 // Only enable this in a limited test world with few objects.
611 // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
612
613 // The physics engine returns the number of milliseconds it simulated this call.
614 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
615 // Multiply by 55 to give a nominal frame rate of 55.
616 return (float)numSubSteps * m_fixedTimeStep * 1000f * 55f;
617 }
618
619 // Something has collided
620 private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration)
621 {
622 if (localID <= TerrainManager.HighestTerrainID)
623 {
624 return; // don't send collisions to the terrain
625 }
626
627 BSPhysObject collider;
628 if (!PhysObjects.TryGetValue(localID, out collider))
629 {
630 // If the object that is colliding cannot be found, just ignore the collision.
631 DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith);
632 return;
633 }
634
635 // The terrain is not in the physical object list so 'collidee' can be null when Collide() is called.
636 BSPhysObject collidee = null;
637 PhysObjects.TryGetValue(collidingWith, out collidee);
638
639 // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
640
641 if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
642 {
643 // If a collision was posted, remember to send it to the simulator
644 ObjectsWithCollisions.Add(collider);
645 }
646
647 return;
648 }
649
650 #endregion // Simulation
651
652 public override void GetResults() { }
653
654 #region Terrain
655
656 public override void SetTerrain(float[] heightMap) {
657 TerrainManager.SetTerrain(heightMap);
658 }
659
660 public override void SetWaterLevel(float baseheight)
661 {
662 SimpleWaterLevel = baseheight;
663 }
664
665 public override void DeleteTerrain()
666 {
667 // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
668 }
669
670 // Although no one seems to check this, I do support combining.
671 public override bool SupportsCombining()
672 {
673 return TerrainManager.SupportsCombining();
674 }
675 // This call says I am a child to region zero in a mega-region. 'pScene' is that
676 // of region zero, 'offset' is my offset from regions zero's origin, and
677 // 'extents' is the largest XY that is handled in my region.
678 public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
679 {
680 TerrainManager.Combine(pScene, offset, extents);
681 }
682
683 // Unhook all the combining that I know about.
684 public override void UnCombine(PhysicsScene pScene)
685 {
686 TerrainManager.UnCombine(pScene);
687 }
688
689 #endregion // Terrain
690
691 public override Dictionary<uint, float> GetTopColliders()
692 {
693 return new Dictionary<uint, float>();
694 }
695
696 public override bool IsThreaded { get { return false; } }
697
698 #region Taints
699 // The simulation execution order is:
700 // Simulate()
701 // DoOneTimeTaints
702 // TriggerPreStepEvent
703 // DoOneTimeTaints
704 // Step()
705 // ProcessAndForwardCollisions
706 // ProcessAndForwardPropertyUpdates
707 // TriggerPostStepEvent
708
709 // Calls to the PhysicsActors can't directly call into the physics engine
710 // because it might be busy. We delay changes to a known time.
711 // We rely on C#'s closure to save and restore the context for the delegate.
712 public void TaintedObject(String ident, TaintCallback callback)
713 {
714 if (!m_initialized) return;
715
716 lock (_taintLock)
717 {
718 _taintOperations.Add(new TaintCallbackEntry(ident, callback));
719 }
720
721 return;
722 }
723
724 // Sometimes a potentially tainted operation can be used in and out of taint time.
725 // This routine executes the command immediately if in taint-time otherwise it is queued.
726 public void TaintedObject(bool inTaintTime, string ident, TaintCallback callback)
727 {
728 if (inTaintTime)
729 callback();
730 else
731 TaintedObject(ident, callback);
732 }
733
734 private void TriggerPreStepEvent(float timeStep)
735 {
736 PreStepAction actions = BeforeStep;
737 if (actions != null)
738 actions(timeStep);
739
740 }
741
742 private void TriggerPostStepEvent(float timeStep)
743 {
744 PreStepAction actions = AfterStep;
745 if (actions != null)
746 actions(timeStep);
747
748 }
749
750 // When someone tries to change a property on a BSPrim or BSCharacter, the object queues
751 // a callback into itself to do the actual property change. That callback is called
752 // here just before the physics engine is called to step the simulation.
753 public void ProcessTaints()
754 {
755 ProcessRegularTaints();
756 ProcessPostTaintTaints();
757 }
758
759 private void ProcessRegularTaints()
760 {
761 if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process
762 {
763 // swizzle a new list into the list location so we can process what's there
764 List<TaintCallbackEntry> oldList;
765 lock (_taintLock)
766 {
767 oldList = _taintOperations;
768 _taintOperations = new List<TaintCallbackEntry>();
769 }
770
771 foreach (TaintCallbackEntry tcbe in oldList)
772 {
773 try
774 {
775 DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG
776 tcbe.callback();
777 }
778 catch (Exception e)
779 {
780 m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e);
781 }
782 }
783 oldList.Clear();
784 }
785 }
786
787 // Schedule an update to happen after all the regular taints are processed.
788 // Note that new requests for the same operation ("ident") for the same object ("ID")
789 // will replace any previous operation by the same object.
790 public void PostTaintObject(String ident, uint ID, TaintCallback callback)
791 {
792 string uniqueIdent = ident + "-" + ID.ToString();
793 lock (_taintLock)
794 {
795 _postTaintOperations[uniqueIdent] = new TaintCallbackEntry(uniqueIdent, callback);
796 }
797
798 return;
799 }
800
801 // Taints that happen after the normal taint processing but before the simulation step.
802 private void ProcessPostTaintTaints()
803 {
804 if (_postTaintOperations.Count > 0)
805 {
806 Dictionary<string, TaintCallbackEntry> oldList;
807 lock (_taintLock)
808 {
809 oldList = _postTaintOperations;
810 _postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
811 }
812
813 foreach (KeyValuePair<string,TaintCallbackEntry> kvp in oldList)
814 {
815 try
816 {
817 DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG
818 kvp.Value.callback();
819 }
820 catch (Exception e)
821 {
822 m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e);
823 }
824 }
825 oldList.Clear();
826 }
827 }
828
829 // Only used for debugging. Does not change state of anything so locking is not necessary.
830 public bool AssertInTaintTime(string whereFrom)
831 {
832 if (!InTaintTime)
833 {
834 DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
835 m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
836 Util.PrintCallStack(); // Prints the stack into the DEBUG log file.
837 }
838 return InTaintTime;
839 }
840
841 #endregion // Taints
842
843 #region INI and command line parameter processing
844
845 #region IPhysicsParameters
846 // Get the list of parameters this physics engine supports
847 public PhysParameterEntry[] GetParameterList()
848 {
849 BSParam.BuildParameterTable();
850 return BSParam.SettableParameters;
851 }
852
853 // Set parameter on a specific or all instances.
854 // Return 'false' if not able to set the parameter.
855 // Setting the value in the m_params block will change the value the physics engine
856 // will use the next time since it's pinned and shared memory.
857 // Some of the values require calling into the physics engine to get the new
858 // value activated ('terrainFriction' for instance).
859 public bool SetPhysicsParameter(string parm, float val, uint localID)
860 {
861 bool ret = false;
862 BSParam.ParameterDefn theParam;
863 if (BSParam.TryGetParameter(parm, out theParam))
864 {
865 theParam.setter(this, parm, localID, val);
866 ret = true;
867 }
868 return ret;
869 }
870
871 // update all the localIDs specified
872 // If the local ID is APPLY_TO_NONE, just change the default value
873 // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
874 // If the localID is a specific object, apply the parameter change to only that object
875 internal delegate void AssignVal(float x);
876 internal void UpdateParameterObject(AssignVal setDefault, string parm, uint localID, float val)
877 {
878 List<uint> objectIDs = new List<uint>();
879 switch (localID)
880 {
881 case PhysParameterEntry.APPLY_TO_NONE:
882 setDefault(val); // setting only the default value
883 // This will cause a call into the physical world if some operation is specified (SetOnObject).
884 objectIDs.Add(TERRAIN_ID);
885 TaintedUpdateParameter(parm, objectIDs, val);
886 break;
887 case PhysParameterEntry.APPLY_TO_ALL:
888 setDefault(val); // setting ALL also sets the default value
889 lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
890 TaintedUpdateParameter(parm, objectIDs, val);
891 break;
892 default:
893 // setting only one localID
894 objectIDs.Add(localID);
895 TaintedUpdateParameter(parm, objectIDs, val);
896 break;
897 }
898 }
899
900 // schedule the actual updating of the paramter to when the phys engine is not busy
901 private void TaintedUpdateParameter(string parm, List<uint> lIDs, float val)
902 {
903 float xval = val;
904 List<uint> xlIDs = lIDs;
905 string xparm = parm;
906 TaintedObject("BSScene.UpdateParameterSet", delegate() {
907 BSParam.ParameterDefn thisParam;
908 if (BSParam.TryGetParameter(xparm, out thisParam))
909 {
910 if (thisParam.onObject != null)
911 {
912 foreach (uint lID in xlIDs)
913 {
914 BSPhysObject theObject = null;
915 PhysObjects.TryGetValue(lID, out theObject);
916 thisParam.onObject(this, theObject, xval);
917 }
918 }
919 }
920 });
921 }
922
923 // Get parameter.
924 // Return 'false' if not able to get the parameter.
925 public bool GetPhysicsParameter(string parm, out float value)
926 {
927 float val = 0f;
928 bool ret = false;
929 BSParam.ParameterDefn theParam;
930 if (BSParam.TryGetParameter(parm, out theParam))
931 {
932 val = theParam.getter(this);
933 ret = true;
934 }
935 value = val;
936 return ret;
937 }
938
939 #endregion IPhysicsParameters
940
941 #endregion Runtime settable parameters
942
943 // Invoke the detailed logger and output something if it's enabled.
944 public void DetailLog(string msg, params Object[] args)
945 {
946 PhysicsLogging.Write(msg, args);
947 // Add the Flush() if debugging crashes. Gets all the messages written out.
948 if (m_physicsLoggingDoFlush) PhysicsLogging.Flush();
949 }
950 // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
951 public const string DetailLogZero = "0000000000";
952
953}
954}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSShapeCollection.cs
new file mode 100644
index 0000000..398ece0
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSShapeCollection.cs
@@ -0,0 +1,1015 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using OMV = OpenMetaverse;
31using OpenSim.Framework;
32using OpenSim.Region.Physics.Manager;
33using OpenSim.Region.Physics.ConvexDecompositionDotNet;
34
35namespace OpenSim.Region.Physics.BulletSNPlugin
36{
37public sealed class BSShapeCollection : IDisposable
38{
39 private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]";
40
41 private BSScene PhysicsScene { get; set; }
42
43 private Object m_collectionActivityLock = new Object();
44
45 // Description of a Mesh
46 private struct MeshDesc
47 {
48 public Object ptr;
49 public int referenceCount;
50 public DateTime lastReferenced;
51 public UInt64 shapeKey;
52 }
53
54 // Description of a hull.
55 // Meshes and hulls have the same shape hash key but we only need hulls for efficient collision calculations.
56 private struct HullDesc
57 {
58 public Object ptr;
59 public int referenceCount;
60 public DateTime lastReferenced;
61 public UInt64 shapeKey;
62 }
63
64 // The sharable set of meshes and hulls. Indexed by their shape hash.
65 private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>();
66 private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>();
67
68 private bool DDetail = false;
69
70 public BSShapeCollection(BSScene physScene)
71 {
72 PhysicsScene = physScene;
73 // Set the next to 'true' for very detailed shape update detailed logging (detailed details?)
74 // While detailed debugging is still active, this is better than commenting out all the
75 // DetailLog statements. When debugging slows down, this and the protected logging
76 // statements can be commented/removed.
77 DDetail = true;
78 }
79
80 public void Dispose()
81 {
82 // TODO!!!!!!!!!
83 }
84
85 // Callbacks called just before either the body or shape is destroyed.
86 // Mostly used for changing bodies out from under Linksets.
87 // Useful for other cases where parameters need saving.
88 // Passing 'null' says no callback.
89 public delegate void ShapeDestructionCallback(BulletShape shape);
90 public delegate void BodyDestructionCallback(BulletBody body);
91
92 // Called to update/change the body and shape for an object.
93 // First checks the shape and updates that if necessary then makes
94 // sure the body is of the right type.
95 // Return 'true' if either the body or the shape changed.
96 // 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before
97 // the current shape or body is destroyed. This allows the caller to remove any
98 // higher level dependencies on the shape or body. Mostly used for LinkSets to
99 // remove the physical constraints before the body is destroyed.
100 // Called at taint-time!!
101 public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim,
102 ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
103 {
104 PhysicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape");
105
106 bool ret = false;
107
108 // This lock could probably be pushed down lower but building shouldn't take long
109 lock (m_collectionActivityLock)
110 {
111 // Do we have the correct geometry for this type of object?
112 // Updates prim.BSShape with information/pointers to shape.
113 // Returns 'true' of BSShape is changed to a new shape.
114 bool newGeom = CreateGeom(forceRebuild, prim, shapeCallback);
115 // If we had to select a new shape geometry for the object,
116 // rebuild the body around it.
117 // Updates prim.BSBody with information/pointers to requested body
118 // Returns 'true' if BSBody was changed.
119 bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World,
120 prim.PhysShape, bodyCallback);
121 ret = newGeom || newBody;
122 }
123 DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}",
124 prim.LocalID, forceRebuild, ret, prim.PhysBody, prim.PhysShape);
125
126 return ret;
127 }
128
129 public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim)
130 {
131 return GetBodyAndShape(forceRebuild, sim, prim, null, null);
132 }
133
134 // Track another user of a body.
135 // We presume the caller has allocated the body.
136 // Bodies only have one user so the body is just put into the world if not already there.
137 public void ReferenceBody(BulletBody body, bool inTaintTime)
138 {
139 lock (m_collectionActivityLock)
140 {
141 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body);
142 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.ReferenceBody", delegate()
143 {
144 if (!BulletSimAPI.IsInWorld2(PhysicsScene.World.ptr, body.ptr))
145 {
146 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr);
147 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
148 }
149 });
150 }
151 }
152
153 // Release the usage of a body.
154 // Called when releasing use of a BSBody. BSShape is handled separately.
155 public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback )
156 {
157 if (!body.HasPhysicalBody)
158 return;
159
160 lock (m_collectionActivityLock)
161 {
162 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceBody", delegate()
163 {
164 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1},inTaintTime={2}",
165 body.ID, body, inTaintTime);
166 // If the caller needs to know the old body is going away, pass the event up.
167 if (bodyCallback != null) bodyCallback(body);
168
169 if (BulletSimAPI.IsInWorld2(PhysicsScene.World.ptr, body.ptr))
170 {
171 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
172 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body);
173 }
174
175 // Zero any reference to the shape so it is not freed when the body is deleted.
176 BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, null);
177 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
178 });
179 }
180 }
181
182 // Track the datastructures and use count for a shape.
183 // When creating a hull, this is called first to reference the mesh
184 // and then again to reference the hull.
185 // Meshes and hulls for the same shape have the same hash key.
186 // NOTE that native shapes are not added to the mesh list or removed.
187 // Returns 'true' if this is the initial reference to the shape. Otherwise reused.
188 public bool ReferenceShape(BulletShape shape)
189 {
190 bool ret = false;
191 switch (shape.type)
192 {
193 case BSPhysicsShapeType.SHAPE_MESH:
194 MeshDesc meshDesc;
195 if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
196 {
197 // There is an existing instance of this mesh.
198 meshDesc.referenceCount++;
199 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}",
200 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
201 }
202 else
203 {
204 // This is a new reference to a mesh
205 meshDesc.ptr = shape.ptr;
206 meshDesc.shapeKey = shape.shapeKey;
207 // We keep a reference to the underlying IMesh data so a hull can be built
208 meshDesc.referenceCount = 1;
209 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}",
210 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
211 ret = true;
212 }
213 meshDesc.lastReferenced = System.DateTime.Now;
214 Meshes[shape.shapeKey] = meshDesc;
215 break;
216 case BSPhysicsShapeType.SHAPE_HULL:
217 HullDesc hullDesc;
218 if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
219 {
220 // There is an existing instance of this hull.
221 hullDesc.referenceCount++;
222 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}",
223 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
224 }
225 else
226 {
227 // This is a new reference to a hull
228 hullDesc.ptr = shape.ptr;
229 hullDesc.shapeKey = shape.shapeKey;
230 hullDesc.referenceCount = 1;
231 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}",
232 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
233 ret = true;
234
235 }
236 hullDesc.lastReferenced = System.DateTime.Now;
237 Hulls[shape.shapeKey] = hullDesc;
238 break;
239 case BSPhysicsShapeType.SHAPE_UNKNOWN:
240 break;
241 default:
242 // Native shapes are not tracked and they don't go into any list
243 break;
244 }
245 return ret;
246 }
247
248 // Release the usage of a shape.
249 public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback)
250 {
251 if (!shape.HasPhysicalShape)
252 return;
253
254 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceShape", delegate()
255 {
256 if (shape.HasPhysicalShape)
257 {
258 if (shape.isNativeShape)
259 {
260 // Native shapes are not tracked and are released immediately
261 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
262 BSScene.DetailLogZero, shape.ptr.ToString(), inTaintTime);
263 if (shapeCallback != null) shapeCallback(shape);
264 BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
265 }
266 else
267 {
268 switch (shape.type)
269 {
270 case BSPhysicsShapeType.SHAPE_HULL:
271 DereferenceHull(shape, shapeCallback);
272 break;
273 case BSPhysicsShapeType.SHAPE_MESH:
274 DereferenceMesh(shape, shapeCallback);
275 break;
276 case BSPhysicsShapeType.SHAPE_COMPOUND:
277 DereferenceCompound(shape, shapeCallback);
278 break;
279 case BSPhysicsShapeType.SHAPE_UNKNOWN:
280 break;
281 default:
282 break;
283 }
284 }
285 }
286 });
287 }
288
289 // Count down the reference count for a mesh shape
290 // Called at taint-time.
291 private void DereferenceMesh(BulletShape shape, ShapeDestructionCallback shapeCallback)
292 {
293 MeshDesc meshDesc;
294 if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
295 {
296 meshDesc.referenceCount--;
297 // TODO: release the Bullet storage
298 if (shapeCallback != null) shapeCallback(shape);
299 meshDesc.lastReferenced = System.DateTime.Now;
300 Meshes[shape.shapeKey] = meshDesc;
301 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}",
302 BSScene.DetailLogZero, shape, meshDesc.referenceCount);
303
304 }
305 }
306
307 // Count down the reference count for a hull shape
308 // Called at taint-time.
309 private void DereferenceHull(BulletShape shape, ShapeDestructionCallback shapeCallback)
310 {
311 HullDesc hullDesc;
312 if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
313 {
314 hullDesc.referenceCount--;
315 // TODO: release the Bullet storage (aging old entries?)
316
317 // Tell upper layers that, if they have dependencies on this shape, this link is going away
318 if (shapeCallback != null) shapeCallback(shape);
319
320 hullDesc.lastReferenced = System.DateTime.Now;
321 Hulls[shape.shapeKey] = hullDesc;
322 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}",
323 BSScene.DetailLogZero, shape, hullDesc.referenceCount);
324 }
325 }
326
327 // Remove a reference to a compound shape.
328 // Taking a compound shape apart is a little tricky because if you just delete the
329 // physical shape, it will free all the underlying children. We can't do that because
330 // they could be shared. So, this removes each of the children from the compound and
331 // dereferences them separately before destroying the compound collision object itself.
332 // Called at taint-time.
333 private void DereferenceCompound(BulletShape shape, ShapeDestructionCallback shapeCallback)
334 {
335 if (!BulletSimAPI.IsCompound2(shape.ptr))
336 {
337 // Failed the sanity check!!
338 PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}",
339 LogHeader, shape.type, shape.ptr.ToString());
340 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}",
341 BSScene.DetailLogZero, shape.type, shape.ptr.ToString());
342 return;
343 }
344
345 int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr);
346 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren);
347
348 for (int ii = numChildren - 1; ii >= 0; ii--)
349 {
350 Object childShape = BulletSimAPI.RemoveChildShapeFromCompoundShapeIndex2(shape.ptr, ii);
351 DereferenceAnonCollisionShape(childShape);
352 }
353 BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
354 }
355
356 // Sometimes we have a pointer to a collision shape but don't know what type it is.
357 // Figure out type and call the correct dereference routine.
358 // Called at taint-time.
359 private void DereferenceAnonCollisionShape(Object cShape)
360 {
361 MeshDesc meshDesc;
362 HullDesc hullDesc;
363
364 BulletShape shapeInfo = new BulletShape(cShape);
365 if (TryGetMeshByPtr(cShape, out meshDesc))
366 {
367 shapeInfo.type = BSPhysicsShapeType.SHAPE_MESH;
368 shapeInfo.shapeKey = meshDesc.shapeKey;
369 }
370 else
371 {
372 if (TryGetHullByPtr(cShape, out hullDesc))
373 {
374 shapeInfo.type = BSPhysicsShapeType.SHAPE_HULL;
375 shapeInfo.shapeKey = hullDesc.shapeKey;
376 }
377 else
378 {
379 if (BulletSimAPI.IsCompound2(cShape))
380 {
381 shapeInfo.type = BSPhysicsShapeType.SHAPE_COMPOUND;
382 }
383 else
384 {
385 if (BulletSimAPI.IsNativeShape2(cShape))
386 {
387 shapeInfo.isNativeShape = true;
388 shapeInfo.type = BSPhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter)
389 }
390 }
391 }
392 }
393
394 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo);
395
396 if (shapeInfo.type != BSPhysicsShapeType.SHAPE_UNKNOWN)
397 {
398 DereferenceShape(shapeInfo, true, null);
399 }
400 else
401 {
402 PhysicsScene.Logger.ErrorFormat("{0} Could not decypher shape type. Region={1}, addr={2}",
403 LogHeader, PhysicsScene.RegionName, cShape.ToString());
404 }
405 }
406
407 // Create the geometry information in Bullet for later use.
408 // The objects needs a hull if it's physical otherwise a mesh is enough.
409 // if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls,
410 // shared geometries will be used. If the parameters of the existing shape are the same
411 // as this request, the shape is not rebuilt.
412 // Info in prim.BSShape is updated to the new shape.
413 // Returns 'true' if the geometry was rebuilt.
414 // Called at taint-time!
415 private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
416 {
417 bool ret = false;
418 bool haveShape = false;
419
420 if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE)
421 {
422 // an avatar capsule is close to a native shape (it is not shared)
423 GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_CAPSULE,
424 FixedShapeKey.KEY_CAPSULE, shapeCallback);
425 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape);
426 ret = true;
427 haveShape = true;
428 }
429
430 // Compound shapes are handled special as they are rebuilt from scratch.
431 // This isn't too great a hardship since most of the child shapes will have already been created.
432 if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND)
433 {
434 ret = GetReferenceToCompoundShape(prim, shapeCallback);
435 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape);
436 haveShape = true;
437 }
438
439 if (!haveShape)
440 {
441 ret = CreateGeomNonSpecial(forceRebuild, prim, shapeCallback);
442 }
443
444 return ret;
445 }
446
447 // Create a mesh/hull shape or a native shape if 'nativeShapePossible' is 'true'.
448 public bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
449 {
450 bool ret = false;
451 bool haveShape = false;
452 bool nativeShapePossible = true;
453 PrimitiveBaseShape pbs = prim.BaseShape;
454
455 // If the prim attributes are simple, this could be a simple Bullet native shape
456 if (!haveShape
457 && pbs != null
458 && nativeShapePossible
459 && ((pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim)
460 || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
461 && pbs.ProfileHollow == 0
462 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
463 && pbs.PathBegin == 0 && pbs.PathEnd == 0
464 && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
465 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
466 && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) )
467 {
468 // Get the scale of any existing shape so we can see if the new shape is same native type and same size.
469 OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero;
470 if (prim.PhysShape.HasPhysicalShape)
471 scaleOfExistingShape = BulletSimAPI.GetLocalScaling2(prim.PhysShape.ptr);
472
473 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}",
474 prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.type);
475
476 // It doesn't look like Bullet scales spheres so make sure the scales are all equal
477 if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
478 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)
479 {
480 haveShape = true;
481 if (forceRebuild
482 || prim.Scale != scaleOfExistingShape
483 || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_SPHERE
484 )
485 {
486 ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE,
487 FixedShapeKey.KEY_SPHERE, shapeCallback);
488 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
489 prim.LocalID, forceRebuild, prim.PhysShape);
490 }
491 }
492 if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
493 {
494 haveShape = true;
495 if (forceRebuild
496 || prim.Scale != scaleOfExistingShape
497 || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_BOX
498 )
499 {
500 ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX,
501 FixedShapeKey.KEY_BOX, shapeCallback);
502 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
503 prim.LocalID, forceRebuild, prim.PhysShape);
504 }
505 }
506 }
507
508 // If a simple shape is not happening, create a mesh and possibly a hull.
509 if (!haveShape && pbs != null)
510 {
511 ret = CreateGeomMeshOrHull(prim, shapeCallback);
512 }
513
514 return ret;
515 }
516
517 public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
518 {
519
520 bool ret = false;
521 // Note that if it's a native shape, the check for physical/non-physical is not
522 // made. Native shapes work in either case.
523 if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects)
524 {
525 // Update prim.BSShape to reference a hull of this shape.
526 ret = GetReferenceToHull(prim,shapeCallback);
527 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}",
528 prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
529 }
530 else
531 {
532 ret = GetReferenceToMesh(prim, shapeCallback);
533 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}",
534 prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
535 }
536 return ret;
537 }
538
539 // Creates a native shape and assignes it to prim.BSShape.
540 // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape().
541 private bool GetReferenceToNativeShape(BSPhysObject prim,
542 BSPhysicsShapeType shapeType, FixedShapeKey shapeKey,
543 ShapeDestructionCallback shapeCallback)
544 {
545 // release any previous shape
546 DereferenceShape(prim.PhysShape, true, shapeCallback);
547
548 BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey);
549
550 // Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
551 if (DDetail) DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
552 prim.LocalID, newShape, prim.Scale);
553
554 // native shapes are scaled by Bullet
555 prim.PhysShape = newShape;
556 return true;
557 }
558
559 private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, BSPhysicsShapeType shapeType,
560 FixedShapeKey shapeKey)
561 {
562 BulletShape newShape;
563 // Need to make sure the passed shape information is for the native type.
564 ShapeData nativeShapeData = new ShapeData();
565 nativeShapeData.Type = shapeType;
566 nativeShapeData.ID = prim.LocalID;
567 nativeShapeData.Scale = prim.Scale;
568 nativeShapeData.Size = prim.Scale; // unneeded, I think.
569 nativeShapeData.MeshKey = (ulong)shapeKey;
570 nativeShapeData.HullKey = (ulong)shapeKey;
571
572 if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
573 {
574 // The proper scale has been calculated in the prim.
575 newShape = new BulletShape(
576 BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale)
577 , shapeType);
578 if (DDetail) DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
579 }
580 else
581 {
582 // Native shapes are scaled in Bullet so set the scaling to the size
583 newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType);
584 }
585 if (!newShape.HasPhysicalShape)
586 {
587 PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
588 LogHeader, prim.LocalID, shapeType);
589 }
590 newShape.shapeKey = (System.UInt64)shapeKey;
591 newShape.isNativeShape = true;
592
593 return newShape;
594 }
595
596 // Builds a mesh shape in the physical world and updates prim.BSShape.
597 // Dereferences previous shape in BSShape and adds a reference for this new shape.
598 // Returns 'true' of a mesh was actually built. Otherwise .
599 // Called at taint-time!
600 private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
601 {
602 BulletShape newShape = new BulletShape();
603
604 float lod;
605 System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
606
607 // if this new shape is the same as last time, don't recreate the mesh
608 if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_MESH)
609 return false;
610
611 if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}",
612 prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"));
613
614 // Since we're recreating new, get rid of the reference to the previous shape
615 DereferenceShape(prim.PhysShape, true, shapeCallback);
616
617 newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, prim.BaseShape, prim.Size, lod);
618 // Take evasive action if the mesh was not constructed.
619 newShape = VerifyMeshCreated(newShape, prim);
620
621 ReferenceShape(newShape);
622
623 prim.PhysShape = newShape;
624
625 return true; // 'true' means a new shape has been added to this prim
626 }
627
628 private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
629 {
630 IMesh meshData = null;
631 Object meshPtr = null;
632 MeshDesc meshDesc;
633 if (Meshes.TryGetValue(newMeshKey, out meshDesc))
634 {
635 // If the mesh has already been built just use it.
636 meshPtr = meshDesc.ptr;
637 }
638 else
639 {
640 meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false);
641
642 if (meshData != null)
643 {
644 int[] indices = meshData.getIndexListAsInt();
645 List<OMV.Vector3> vertices = meshData.getVertexList();
646
647 float[] verticesAsFloats = new float[vertices.Count * 3];
648 int vi = 0;
649 foreach (OMV.Vector3 vv in vertices)
650 {
651 verticesAsFloats[vi++] = vv.X;
652 verticesAsFloats[vi++] = vv.Y;
653 verticesAsFloats[vi++] = vv.Z;
654 }
655
656 // m_log.DebugFormat("{0}: BSShapeCollection.CreatePhysicalMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}",
657 // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count);
658
659 meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
660 indices.GetLength(0), indices, vertices.Count, verticesAsFloats);
661 }
662 }
663 BulletShape newShape = new BulletShape(meshPtr, BSPhysicsShapeType.SHAPE_MESH);
664 newShape.shapeKey = newMeshKey;
665
666 return newShape;
667 }
668
669 // See that hull shape exists in the physical world and update prim.BSShape.
670 // We could be creating the hull because scale changed or whatever.
671 private bool GetReferenceToHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
672 {
673 BulletShape newShape;
674
675 float lod;
676 System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
677
678 // if the hull hasn't changed, don't rebuild it
679 if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_HULL)
680 return false;
681
682 if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}",
683 prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X"));
684
685 // Remove usage of the previous shape.
686 DereferenceShape(prim.PhysShape, true, shapeCallback);
687
688 newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, prim.BaseShape, prim.Size, lod);
689 newShape = VerifyMeshCreated(newShape, prim);
690
691 ReferenceShape(newShape);
692
693 prim.PhysShape = newShape;
694 return true; // 'true' means a new shape has been added to this prim
695 }
696
697 List<ConvexResult> m_hulls;
698 private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
699 {
700
701 Object hullPtr = null;
702 HullDesc hullDesc;
703 if (Hulls.TryGetValue(newHullKey, out hullDesc))
704 {
705 // If the hull shape already is created, just use it.
706 hullPtr = hullDesc.ptr;
707 }
708 else
709 {
710 // Build a new hull in the physical world
711 // Pass true for physicalness as this creates some sort of bounding box which we don't need
712 IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false);
713 if (meshData != null)
714 {
715
716 int[] indices = meshData.getIndexListAsInt();
717 List<OMV.Vector3> vertices = meshData.getVertexList();
718
719 //format conversion from IMesh format to DecompDesc format
720 List<int> convIndices = new List<int>();
721 List<float3> convVertices = new List<float3>();
722 for (int ii = 0; ii < indices.GetLength(0); ii++)
723 {
724 convIndices.Add(indices[ii]);
725 }
726 foreach (OMV.Vector3 vv in vertices)
727 {
728 convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
729 }
730
731 // setup and do convex hull conversion
732 m_hulls = new List<ConvexResult>();
733 DecompDesc dcomp = new DecompDesc();
734 dcomp.mIndices = convIndices;
735 dcomp.mVertices = convVertices;
736 ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
737 // create the hull into the _hulls variable
738 convexBuilder.process(dcomp);
739
740 // Convert the vertices and indices for passing to unmanaged.
741 // The hull information is passed as a large floating point array.
742 // The format is:
743 // convHulls[0] = number of hulls
744 // convHulls[1] = number of vertices in first hull
745 // convHulls[2] = hull centroid X coordinate
746 // convHulls[3] = hull centroid Y coordinate
747 // convHulls[4] = hull centroid Z coordinate
748 // convHulls[5] = first hull vertex X
749 // convHulls[6] = first hull vertex Y
750 // convHulls[7] = first hull vertex Z
751 // convHulls[8] = second hull vertex X
752 // ...
753 // convHulls[n] = number of vertices in second hull
754 // convHulls[n+1] = second hull centroid X coordinate
755 // ...
756 //
757 // TODO: is is very inefficient. Someday change the convex hull generator to return
758 // data structures that do not need to be converted in order to pass to Bullet.
759 // And maybe put the values directly into pinned memory rather than marshaling.
760 int hullCount = m_hulls.Count;
761 int totalVertices = 1; // include one for the count of the hulls
762 foreach (ConvexResult cr in m_hulls)
763 {
764 totalVertices += 4; // add four for the vertex count and centroid
765 totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
766 }
767 float[] convHulls = new float[totalVertices];
768
769 convHulls[0] = (float)hullCount;
770 int jj = 1;
771 foreach (ConvexResult cr in m_hulls)
772 {
773 // copy vertices for index access
774 float3[] verts = new float3[cr.HullVertices.Count];
775 int kk = 0;
776 foreach (float3 ff in cr.HullVertices)
777 {
778 verts[kk++] = ff;
779 }
780
781 // add to the array one hull's worth of data
782 convHulls[jj++] = cr.HullIndices.Count;
783 convHulls[jj++] = 0f; // centroid x,y,z
784 convHulls[jj++] = 0f;
785 convHulls[jj++] = 0f;
786 foreach (int ind in cr.HullIndices)
787 {
788 convHulls[jj++] = verts[ind].x;
789 convHulls[jj++] = verts[ind].y;
790 convHulls[jj++] = verts[ind].z;
791 }
792 }
793 // create the hull data structure in Bullet
794 hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
795 }
796 }
797
798 BulletShape newShape = new BulletShape(hullPtr, BSPhysicsShapeType.SHAPE_HULL);
799 newShape.shapeKey = newHullKey;
800
801 return newShape;
802 }
803
804 // Callback from convex hull creater with a newly created hull.
805 // Just add it to our collection of hulls for this shape.
806 private void HullReturn(ConvexResult result)
807 {
808 m_hulls.Add(result);
809 return;
810 }
811
812 // Compound shapes are always built from scratch.
813 // This shouldn't be to bad since most of the parts will be meshes that had been built previously.
814 private bool GetReferenceToCompoundShape(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
815 {
816 // Remove reference to the old shape
817 // Don't need to do this as the shape is freed when the new root shape is created below.
818 // DereferenceShape(prim.PhysShape, true, shapeCallback);
819
820 BulletShape cShape = new BulletShape(
821 BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr, false), BSPhysicsShapeType.SHAPE_COMPOUND);
822
823 // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape.
824 CreateGeomMeshOrHull(prim, shapeCallback);
825 BulletSimAPI.AddChildShapeToCompoundShape2(cShape.ptr, prim.PhysShape.ptr, OMV.Vector3.Zero, OMV.Quaternion.Identity);
826 if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}",
827 prim.LocalID, cShape, prim.PhysShape);
828
829 prim.PhysShape = cShape;
830
831 return true;
832 }
833
834 // Create a hash of all the shape parameters to be used as a key
835 // for this particular shape.
836 private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod)
837 {
838 // level of detail based on size and type of the object
839 float lod = BSParam.MeshLOD;
840 if (pbs.SculptEntry)
841 lod = BSParam.SculptLOD;
842
843 // Mega prims usually get more detail because one can interact with shape approximations at this size.
844 float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z));
845 if (maxAxis > BSParam.MeshMegaPrimThreshold)
846 lod = BSParam.MeshMegaPrimLOD;
847
848 retLod = lod;
849 return pbs.GetMeshKey(size, lod);
850 }
851 // For those who don't want the LOD
852 private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs)
853 {
854 float lod;
855 return ComputeShapeKey(size, pbs, out lod);
856 }
857
858 // The creation of a mesh or hull can fail if an underlying asset is not available.
859 // There are two cases: 1) the asset is not in the cache and it needs to be fetched;
860 // and 2) the asset cannot be converted (like failed decompression of JPEG2000s).
861 // The first case causes the asset to be fetched. The second case requires
862 // us to not loop forever.
863 // Called after creating a physical mesh or hull. If the physical shape was created,
864 // just return.
865 private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim)
866 {
867 // If the shape was successfully created, nothing more to do
868 if (newShape.HasPhysicalShape)
869 return newShape;
870
871 // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
872 if (prim.BaseShape.SculptEntry && !prim.LastAssetBuildFailed && prim.BaseShape.SculptTexture != OMV.UUID.Zero)
873 {
874 prim.LastAssetBuildFailed = true;
875 BSPhysObject xprim = prim;
876 DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lID={1},lastFailed={2}",
877 LogHeader, prim.LocalID, prim.LastAssetBuildFailed);
878 Util.FireAndForget(delegate
879 {
880 RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod;
881 if (assetProvider != null)
882 {
883 BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
884 assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
885 {
886 if (!yprim.BaseShape.SculptEntry)
887 return;
888 if (yprim.BaseShape.SculptTexture.ToString() != asset.ID)
889 return;
890
891 yprim.BaseShape.SculptData = asset.Data;
892 // This will cause the prim to see that the filler shape is not the right
893 // one and try again to build the object.
894 // No race condition with the normal shape setting since the rebuild is at taint time.
895 yprim.ForceBodyShapeRebuild(false);
896
897 });
898 }
899 });
900 }
901 else
902 {
903 if (prim.LastAssetBuildFailed)
904 {
905 PhysicsScene.Logger.ErrorFormat("{0} Mesh failed to fetch asset. lID={1}, texture={2}",
906 LogHeader, prim.LocalID, prim.BaseShape.SculptTexture);
907 }
908 }
909
910 // While we figure out the real problem, stick a simple native shape on the object.
911 BulletShape fillinShape =
912 BuildPhysicalNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
913
914 return fillinShape;
915 }
916
917 // Create a body object in Bullet.
918 // Updates prim.BSBody with the information about the new body if one is created.
919 // Returns 'true' if an object was actually created.
920 // Called at taint-time.
921 private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape,
922 BodyDestructionCallback bodyCallback)
923 {
924 bool ret = false;
925
926 // the mesh, hull or native shape must have already been created in Bullet
927 bool mustRebuild = !prim.PhysBody.HasPhysicalBody;
928
929 // If there is an existing body, verify it's of an acceptable type.
930 // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
931 if (!mustRebuild)
932 {
933 CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.PhysBody.ptr);
934 if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY
935 || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT)
936 {
937 // If the collisionObject is not the correct type for solidness, rebuild what's there
938 mustRebuild = true;
939 }
940 }
941
942 if (mustRebuild || forceRebuild)
943 {
944 // Free any old body
945 DereferenceBody(prim.PhysBody, true, bodyCallback);
946
947 BulletBody aBody;
948 Object bodyPtr = null;
949 if (prim.IsSolid)
950 {
951 bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
952 prim.LocalID, prim.RawPosition, prim.RawOrientation);
953 if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString());
954 }
955 else
956 {
957 bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
958 prim.LocalID, prim.RawPosition, prim.RawOrientation);
959 if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString());
960 }
961 aBody = new BulletBody(prim.LocalID, bodyPtr);
962
963 ReferenceBody(aBody, true);
964
965 prim.PhysBody = aBody;
966
967 ret = true;
968 }
969
970 return ret;
971 }
972
973 private bool TryGetMeshByPtr(Object addr, out MeshDesc outDesc)
974 {
975 bool ret = false;
976 MeshDesc foundDesc = new MeshDesc();
977 foreach (MeshDesc md in Meshes.Values)
978 {
979 if (md.ptr == addr)
980 {
981 foundDesc = md;
982 ret = true;
983 break;
984 }
985
986 }
987 outDesc = foundDesc;
988 return ret;
989 }
990
991 private bool TryGetHullByPtr(Object addr, out HullDesc outDesc)
992 {
993 bool ret = false;
994 HullDesc foundDesc = new HullDesc();
995 foreach (HullDesc hd in Hulls.Values)
996 {
997 if (hd.ptr == addr)
998 {
999 foundDesc = hd;
1000 ret = true;
1001 break;
1002 }
1003
1004 }
1005 outDesc = foundDesc;
1006 return ret;
1007 }
1008
1009 private void DetailLog(string msg, params Object[] args)
1010 {
1011 if (PhysicsScene.PhysicsLogging.Enabled)
1012 PhysicsScene.DetailLog(msg, args);
1013 }
1014}
1015}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSShapes.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSShapes.cs
new file mode 100644
index 0000000..8ff0275
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSShapes.cs
@@ -0,0 +1,208 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Linq;
31using System.Text;
32
33namespace OpenSim.Region.Physics.BulletSNPlugin
34{
35public abstract class BSShape
36{
37 public Object ptr { get; set; }
38 public BSPhysicsShapeType type { get; set; }
39 public System.UInt64 key { get; set; }
40 public int referenceCount { get; set; }
41 public DateTime lastReferenced { get; set; }
42
43 public BSShape()
44 {
45 ptr = null;
46 type = BSPhysicsShapeType.SHAPE_UNKNOWN;
47 key = 0;
48 referenceCount = 0;
49 lastReferenced = DateTime.Now;
50 }
51
52 // Get a reference to a physical shape. Create if it doesn't exist
53 public static BSShape GetShapeReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
54 {
55 BSShape ret = null;
56
57 if (prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE)
58 {
59 // an avatar capsule is close to a native shape (it is not shared)
60 ret = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_CAPSULE,
61 FixedShapeKey.KEY_CAPSULE);
62 physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret);
63 }
64
65 // Compound shapes are handled special as they are rebuilt from scratch.
66 // This isn't too great a hardship since most of the child shapes will already been created.
67 if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND)
68 {
69 // Getting a reference to a compound shape gets you the compound shape with the root prim shape added
70 ret = BSShapeCompound.GetReference(prim);
71 physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, ret);
72 }
73
74 if (ret == null)
75 ret = GetShapeReferenceNonSpecial(physicsScene, forceRebuild, prim);
76
77 return ret;
78 }
79 public static BSShape GetShapeReferenceNonSpecial(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
80 {
81 return null;
82 }
83 public static BSShape GetShapeReferenceNonNative(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
84 {
85 return null;
86 }
87
88 // Release the use of a physical shape.
89 public abstract void Dereference(BSScene physicsScene);
90
91 // All shapes have a static call to get a reference to the physical shape
92 // protected abstract static BSShape GetReference();
93
94 public override string ToString()
95 {
96 StringBuilder buff = new StringBuilder();
97 buff.Append("<p=");
98 buff.Append(ptr.ToString());
99 buff.Append(",s=");
100 buff.Append(type.ToString());
101 buff.Append(",k=");
102 buff.Append(key.ToString("X"));
103 buff.Append(",c=");
104 buff.Append(referenceCount.ToString());
105 buff.Append(">");
106 return buff.ToString();
107 }
108}
109
110public class BSShapeNull : BSShape
111{
112 public BSShapeNull() : base()
113 {
114 }
115 public static BSShape GetReference() { return new BSShapeNull(); }
116 public override void Dereference(BSScene physicsScene) { /* The magic of garbage collection will make this go away */ }
117}
118
119public class BSShapeNative : BSShape
120{
121 private static string LogHeader = "[BULLETSIM SHAPE NATIVE]";
122 public BSShapeNative() : base()
123 {
124 }
125 public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim,
126 BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
127 {
128 // Native shapes are not shared and are always built anew.
129 return new BSShapeNative(physicsScene, prim, shapeType, shapeKey);
130 }
131
132 private BSShapeNative(BSScene physicsScene, BSPhysObject prim,
133 BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
134 {
135 ShapeData nativeShapeData = new ShapeData();
136 nativeShapeData.Type = shapeType;
137 nativeShapeData.ID = prim.LocalID;
138 nativeShapeData.Scale = prim.Scale;
139 nativeShapeData.Size = prim.Scale;
140 nativeShapeData.MeshKey = (ulong)shapeKey;
141 nativeShapeData.HullKey = (ulong)shapeKey;
142
143
144 if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
145 {
146 ptr = BulletSimAPI.BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale);
147 physicsScene.DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
148 }
149 else
150 {
151 ptr = BulletSimAPI.BuildNativeShape2(physicsScene.World.ptr, nativeShapeData);
152 }
153 if (ptr == null)
154 {
155 physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
156 LogHeader, prim.LocalID, shapeType);
157 }
158 type = shapeType;
159 key = (UInt64)shapeKey;
160 }
161 // Make this reference to the physical shape go away since native shapes are not shared.
162 public override void Dereference(BSScene physicsScene)
163 {
164 // Native shapes are not tracked and are released immediately
165 physicsScene.DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this);
166 BulletSimAPI.DeleteCollisionShape2(physicsScene.World.ptr, ptr);
167 ptr = null;
168 // Garbage collection will free up this instance.
169 }
170}
171
172public class BSShapeMesh : BSShape
173{
174 private static string LogHeader = "[BULLETSIM SHAPE MESH]";
175 private static Dictionary<System.UInt64, BSShapeMesh> Meshes = new Dictionary<System.UInt64, BSShapeMesh>();
176
177 public BSShapeMesh() : base()
178 {
179 }
180 public static BSShape GetReference() { return new BSShapeNull(); }
181 public override void Dereference(BSScene physicsScene) { }
182}
183
184public class BSShapeHull : BSShape
185{
186 private static string LogHeader = "[BULLETSIM SHAPE HULL]";
187 private static Dictionary<System.UInt64, BSShapeHull> Hulls = new Dictionary<System.UInt64, BSShapeHull>();
188
189 public BSShapeHull() : base()
190 {
191 }
192 public static BSShape GetReference() { return new BSShapeNull(); }
193 public override void Dereference(BSScene physicsScene) { }
194}
195
196public class BSShapeCompound : BSShape
197{
198 private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]";
199 public BSShapeCompound() : base()
200 {
201 }
202 public static BSShape GetReference(BSPhysObject prim)
203 {
204 return new BSShapeNull();
205 }
206 public override void Dereference(BSScene physicsScene) { }
207}
208}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainHeightmap.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainHeightmap.cs
new file mode 100644
index 0000000..252953b
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainHeightmap.cs
@@ -0,0 +1,175 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OpenSim.Framework;
32using OpenSim.Region.Framework;
33using OpenSim.Region.CoreModules;
34using OpenSim.Region.Physics.Manager;
35
36using Nini.Config;
37using log4net;
38
39using OpenMetaverse;
40
41namespace OpenSim.Region.Physics.BulletSNPlugin
42{
43public sealed class BSTerrainHeightmap : BSTerrainPhys
44{
45 static string LogHeader = "[BULLETSIM TERRAIN HEIGHTMAP]";
46
47 BulletHeightMapInfo m_mapInfo = null;
48
49 // Constructor to build a default, flat heightmap terrain.
50 public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
51 : base(physicsScene, regionBase, id)
52 {
53 Vector3 minTerrainCoords = new Vector3(0f, 0f, BSTerrainManager.HEIGHT_INITIALIZATION - BSTerrainManager.HEIGHT_EQUAL_FUDGE);
54 Vector3 maxTerrainCoords = new Vector3(regionSize.X, regionSize.Y, BSTerrainManager.HEIGHT_INITIALIZATION);
55 int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y;
56 float[] initialMap = new float[totalHeights];
57 for (int ii = 0; ii < totalHeights; ii++)
58 {
59 initialMap[ii] = BSTerrainManager.HEIGHT_INITIALIZATION;
60 }
61 m_mapInfo = new BulletHeightMapInfo(id, initialMap, null);
62 m_mapInfo.minCoords = minTerrainCoords;
63 m_mapInfo.maxCoords = maxTerrainCoords;
64 m_mapInfo.terrainRegionBase = TerrainBase;
65 // Don't have to free any previous since we just got here.
66 BuildHeightmapTerrain();
67 }
68
69 // This minCoords and maxCoords passed in give the size of the terrain (min and max Z
70 // are the high and low points of the heightmap).
71 public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
72 Vector3 minCoords, Vector3 maxCoords)
73 : base(physicsScene, regionBase, id)
74 {
75 m_mapInfo = new BulletHeightMapInfo(id, initialMap, null);
76 m_mapInfo.minCoords = minCoords;
77 m_mapInfo.maxCoords = maxCoords;
78 m_mapInfo.minZ = minCoords.Z;
79 m_mapInfo.maxZ = maxCoords.Z;
80 m_mapInfo.terrainRegionBase = TerrainBase;
81
82 // Don't have to free any previous since we just got here.
83 BuildHeightmapTerrain();
84 }
85
86 public override void Dispose()
87 {
88 ReleaseHeightMapTerrain();
89 }
90
91 // Using the information in m_mapInfo, create the physical representation of the heightmap.
92 private void BuildHeightmapTerrain()
93 {
94 m_mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, m_mapInfo.ID,
95 m_mapInfo.minCoords, m_mapInfo.maxCoords,
96 m_mapInfo.heightMap, BSParam.TerrainCollisionMargin);
97
98 // Create the terrain shape from the mapInfo
99 m_mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(m_mapInfo.Ptr),
100 BSPhysicsShapeType.SHAPE_TERRAIN);
101
102 // The terrain object initial position is at the center of the object
103 Vector3 centerPos;
104 centerPos.X = m_mapInfo.minCoords.X + (m_mapInfo.sizeX / 2f);
105 centerPos.Y = m_mapInfo.minCoords.Y + (m_mapInfo.sizeY / 2f);
106 centerPos.Z = m_mapInfo.minZ + ((m_mapInfo.maxZ - m_mapInfo.minZ) / 2f + 0.5f);
107
108 m_mapInfo.terrainBody = new BulletBody(m_mapInfo.ID,
109 BulletSimAPI.CreateBodyWithDefaultMotionState2(m_mapInfo.terrainShape.ptr,
110 m_mapInfo.ID, centerPos, Quaternion.Identity));
111
112 // Set current terrain attributes
113 BulletSimAPI.SetFriction2(m_mapInfo.terrainBody.ptr, BSParam.TerrainFriction);
114 BulletSimAPI.SetHitFraction2(m_mapInfo.terrainBody.ptr, BSParam.TerrainHitFraction);
115 BulletSimAPI.SetRestitution2(m_mapInfo.terrainBody.ptr, BSParam.TerrainRestitution);
116 BulletSimAPI.SetCollisionFlags2(m_mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
117
118 // Return the new terrain to the world of physical objects
119 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr, centerPos, Quaternion.Identity);
120
121 // redo its bounding box now that it is in the world
122 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
123
124 m_mapInfo.terrainBody.collisionType = CollisionType.Terrain;
125 m_mapInfo.terrainBody.ApplyCollisionMask();
126
127 // Make it so the terrain will not move or be considered for movement.
128 BulletSimAPI.ForceActivationState2(m_mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
129
130 return;
131 }
132
133 // If there is information in m_mapInfo pointing to physical structures, release same.
134 private void ReleaseHeightMapTerrain()
135 {
136 if (m_mapInfo != null)
137 {
138 if (m_mapInfo.terrainBody.HasPhysicalBody)
139 {
140 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
141 // Frees both the body and the shape.
142 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
143 BulletSimAPI.ReleaseHeightMapInfo2(m_mapInfo.Ptr);
144 }
145 }
146 m_mapInfo = null;
147 }
148
149 // The passed position is relative to the base of the region.
150 public override float GetTerrainHeightAtXYZ(Vector3 pos)
151 {
152 float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
153
154 int mapIndex = (int)pos.Y * (int)m_mapInfo.sizeY + (int)pos.X;
155 try
156 {
157 ret = m_mapInfo.heightMap[mapIndex];
158 }
159 catch
160 {
161 // Sometimes they give us wonky values of X and Y. Give a warning and return something.
162 PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
163 LogHeader, m_mapInfo.terrainRegionBase, pos);
164 ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
165 }
166 return ret;
167 }
168
169 // The passed position is relative to the base of the region.
170 public override float GetWaterLevelAtXYZ(Vector3 pos)
171 {
172 return PhysicsScene.SimpleWaterLevel;
173 }
174}
175}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainManager.cs
new file mode 100644
index 0000000..dfad70e
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainManager.cs
@@ -0,0 +1,460 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OpenSim.Framework;
32using OpenSim.Region.Framework;
33using OpenSim.Region.CoreModules;
34using OpenSim.Region.Physics.Manager;
35
36using Nini.Config;
37using log4net;
38
39using OpenMetaverse;
40
41namespace OpenSim.Region.Physics.BulletSNPlugin
42{
43
44// The physical implementation of the terrain is wrapped in this class.
45public abstract class BSTerrainPhys : IDisposable
46{
47 public enum TerrainImplementation
48 {
49 Heightmap = 0,
50 Mesh = 1
51 }
52
53 public BSScene PhysicsScene { get; private set; }
54 // Base of the region in world coordinates. Coordinates inside the region are relative to this.
55 public Vector3 TerrainBase { get; private set; }
56 public uint ID { get; private set; }
57
58 public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id)
59 {
60 PhysicsScene = physicsScene;
61 TerrainBase = regionBase;
62 ID = id;
63 }
64 public abstract void Dispose();
65 public abstract float GetTerrainHeightAtXYZ(Vector3 pos);
66 public abstract float GetWaterLevelAtXYZ(Vector3 pos);
67}
68
69// ==========================================================================================
70public sealed class BSTerrainManager : IDisposable
71{
72 static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
73
74 // These height values are fractional so the odd values will be
75 // noticable when debugging.
76 public const float HEIGHT_INITIALIZATION = 24.987f;
77 public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f;
78 public const float HEIGHT_GETHEIGHT_RET = 24.765f;
79 public const float WATER_HEIGHT_GETHEIGHT_RET = 19.998f;
80
81 // If the min and max height are equal, we reduce the min by this
82 // amount to make sure that a bounding box is built for the terrain.
83 public const float HEIGHT_EQUAL_FUDGE = 0.2f;
84
85 // Until the whole simulator is changed to pass us the region size, we rely on constants.
86 public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
87
88 // The scene that I am part of
89 private BSScene PhysicsScene { get; set; }
90
91 // The ground plane created to keep thing from falling to infinity.
92 private BulletBody m_groundPlane;
93
94 // If doing mega-regions, if we're region zero we will be managing multiple
95 // region terrains since region zero does the physics for the whole mega-region.
96 private Dictionary<Vector3, BSTerrainPhys> m_terrains;
97
98 // Flags used to know when to recalculate the height.
99 private bool m_terrainModified = false;
100
101 // If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount.
102 // This is incremented before assigning to new region so it is the last ID allocated.
103 private uint m_terrainCount = BSScene.CHILDTERRAIN_ID - 1;
104 public uint HighestTerrainID { get {return m_terrainCount; } }
105
106 // If doing mega-regions, this holds our offset from region zero of
107 // the mega-regions. "parentScene" points to the PhysicsScene of region zero.
108 private Vector3 m_worldOffset;
109 // If the parent region (region 0), this is the extent of the combined regions
110 // relative to the origin of region zero
111 private Vector3 m_worldMax;
112 private PhysicsScene MegaRegionParentPhysicsScene { get; set; }
113
114 public BSTerrainManager(BSScene physicsScene)
115 {
116 PhysicsScene = physicsScene;
117 m_terrains = new Dictionary<Vector3,BSTerrainPhys>();
118
119 // Assume one region of default size
120 m_worldOffset = Vector3.Zero;
121 m_worldMax = new Vector3(DefaultRegionSize);
122 MegaRegionParentPhysicsScene = null;
123 }
124
125 public void Dispose()
126 {
127 ReleaseGroundPlaneAndTerrain();
128 }
129
130 // Create the initial instance of terrain and the underlying ground plane.
131 // This is called from the initialization routine so we presume it is
132 // safe to call Bullet in real time. We hope no one is moving prims around yet.
133 public void CreateInitialGroundPlaneAndTerrain()
134 {
135 // The ground plane is here to catch things that are trying to drop to negative infinity
136 BulletShape groundPlaneShape = new BulletShape(
137 BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f,
138 BSParam.TerrainCollisionMargin),
139 BSPhysicsShapeType.SHAPE_GROUNDPLANE);
140 m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
141 BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
142 Vector3.Zero, Quaternion.Identity));
143 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr);
144 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_groundPlane.ptr);
145 // Ground plane does not move
146 BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION);
147 // Everything collides with the ground plane.
148 m_groundPlane.collisionType = CollisionType.Groundplane;
149 m_groundPlane.ApplyCollisionMask();
150
151 // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
152 BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize);
153 m_terrains.Add(Vector3.Zero, initialTerrain);
154 }
155
156 // Release all the terrain structures we might have allocated
157 public void ReleaseGroundPlaneAndTerrain()
158 {
159 if (m_groundPlane.HasPhysicalBody)
160 {
161 if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr))
162 {
163 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_groundPlane.ptr);
164 }
165 m_groundPlane.Clear();
166 }
167
168 ReleaseTerrain();
169 }
170
171 // Release all the terrain we have allocated
172 public void ReleaseTerrain()
173 {
174 lock (m_terrains)
175 {
176 foreach (KeyValuePair<Vector3, BSTerrainPhys> kvp in m_terrains)
177 {
178 kvp.Value.Dispose();
179 }
180 m_terrains.Clear();
181 }
182 }
183
184 // The simulator wants to set a new heightmap for the terrain.
185 public void SetTerrain(float[] heightMap) {
186 float[] localHeightMap = heightMap;
187 // If there are multiple requests for changes to the same terrain between ticks,
188 // only do that last one.
189 PhysicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate()
190 {
191 if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null)
192 {
193 // If a child of a mega-region, we shouldn't have any terrain allocated for us
194 ReleaseGroundPlaneAndTerrain();
195 // If doing the mega-prim stuff and we are the child of the zero region,
196 // the terrain is added to our parent
197 if (MegaRegionParentPhysicsScene is BSScene)
198 {
199 DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}",
200 BSScene.DetailLogZero, m_worldOffset, m_worldMax);
201 ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateTerrain(
202 BSScene.CHILDTERRAIN_ID, localHeightMap,
203 m_worldOffset, m_worldOffset + DefaultRegionSize, true);
204 }
205 }
206 else
207 {
208 // If not doing the mega-prim thing, just change the terrain
209 DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
210
211 UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap,
212 m_worldOffset, m_worldOffset + DefaultRegionSize, true);
213 }
214 });
215 }
216
217 // If called with no mapInfo for the terrain, this will create a new mapInfo and terrain
218 // based on the passed information. The 'id' should be either the terrain id or
219 // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used.
220 // The latter feature is for creating child terrains for mega-regions.
221 // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new
222 // terrain shape is created and added to the body.
223 // This call is most often used to update the heightMap and parameters of the terrain.
224 // (The above does suggest that some simplification/refactoring is in order.)
225 // Called during taint-time.
226 private void UpdateTerrain(uint id, float[] heightMap,
227 Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
228 {
229 DetailLog("{0},BSTerrainManager.UpdateTerrain,call,minC={1},maxC={2},inTaintTime={3}",
230 BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime);
231
232 // Find high and low points of passed heightmap.
233 // The min and max passed in is usually the area objects can be in (maximum
234 // object height, for instance). The terrain wants the bounding box for the
235 // terrain so replace passed min and max Z with the actual terrain min/max Z.
236 float minZ = float.MaxValue;
237 float maxZ = float.MinValue;
238 foreach (float height in heightMap)
239 {
240 if (height < minZ) minZ = height;
241 if (height > maxZ) maxZ = height;
242 }
243 if (minZ == maxZ)
244 {
245 // If min and max are the same, reduce min a little bit so a good bounding box is created.
246 minZ -= BSTerrainManager.HEIGHT_EQUAL_FUDGE;
247 }
248 minCoords.Z = minZ;
249 maxCoords.Z = maxZ;
250
251 Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f);
252
253 lock (m_terrains)
254 {
255 BSTerrainPhys terrainPhys;
256 if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
257 {
258 // There is already a terrain in this spot. Free the old and build the new.
259 DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}",
260 BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
261
262 // Remove old terrain from the collection
263 m_terrains.Remove(terrainRegionBase);
264 // Release any physical memory it may be using.
265 terrainPhys.Dispose();
266
267 if (MegaRegionParentPhysicsScene == null)
268 {
269 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
270 m_terrains.Add(terrainRegionBase, newTerrainPhys);
271
272 m_terrainModified = true;
273 }
274 else
275 {
276 // It's possible that Combine() was called after this code was queued.
277 // If we are a child of combined regions, we don't create any terrain for us.
278 DetailLog("{0},BSTerrainManager.UpdateTerrain:AmACombineChild,taint", BSScene.DetailLogZero);
279
280 // Get rid of any terrain that may have been allocated for us.
281 ReleaseGroundPlaneAndTerrain();
282
283 // I hate doing this, but just bail
284 return;
285 }
286 }
287 else
288 {
289 // We don't know about this terrain so either we are creating a new terrain or
290 // our mega-prim child is giving us a new terrain to add to the phys world
291
292 // if this is a child terrain, calculate a unique terrain id
293 uint newTerrainID = id;
294 if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
295 newTerrainID = ++m_terrainCount;
296
297 DetailLog("{0},UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}",
298 BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
299 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
300 m_terrains.Add(terrainRegionBase, newTerrainPhys);
301
302 m_terrainModified = true;
303 }
304 }
305 }
306
307 // TODO: redo terrain implementation selection to allow other base types than heightMap.
308 private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords)
309 {
310 PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}",
311 LogHeader, PhysicsScene.RegionName, terrainRegionBase,
312 (BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation);
313 BSTerrainPhys newTerrainPhys = null;
314 switch ((int)BSParam.TerrainImplementation)
315 {
316 case (int)BSTerrainPhys.TerrainImplementation.Heightmap:
317 newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id,
318 heightMap, minCoords, maxCoords);
319 break;
320 case (int)BSTerrainPhys.TerrainImplementation.Mesh:
321 newTerrainPhys = new BSTerrainMesh(PhysicsScene, terrainRegionBase, id,
322 heightMap, minCoords, maxCoords);
323 break;
324 default:
325 PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}",
326 LogHeader,
327 (int)BSParam.TerrainImplementation,
328 BSParam.TerrainImplementation,
329 PhysicsScene.RegionName, terrainRegionBase);
330 break;
331 }
332 return newTerrainPhys;
333 }
334
335 // Return 'true' of this position is somewhere in known physical terrain space
336 public bool IsWithinKnownTerrain(Vector3 pos)
337 {
338 Vector3 terrainBaseXYZ;
339 BSTerrainPhys physTerrain;
340 return GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ);
341 }
342
343 // Given an X and Y, find the height of the terrain.
344 // Since we could be handling multiple terrains for a mega-region,
345 // the base of the region is calcuated assuming all regions are
346 // the same size and that is the default.
347 // Once the heightMapInfo is found, we have all the information to
348 // compute the offset into the array.
349 private float lastHeightTX = 999999f;
350 private float lastHeightTY = 999999f;
351 private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT;
352 public float GetTerrainHeightAtXYZ(Vector3 pos)
353 {
354 float tX = pos.X;
355 float tY = pos.Y;
356 // You'd be surprized at the number of times this routine is called
357 // with the same parameters as last time.
358 if (!m_terrainModified && (lastHeightTX == tX) && (lastHeightTY == tY))
359 return lastHeight;
360 m_terrainModified = false;
361
362 lastHeightTX = tX;
363 lastHeightTY = tY;
364 float ret = HEIGHT_GETHEIGHT_RET;
365
366 Vector3 terrainBaseXYZ;
367 BSTerrainPhys physTerrain;
368 if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
369 {
370 ret = physTerrain.GetTerrainHeightAtXYZ(pos - terrainBaseXYZ);
371 }
372 else
373 {
374 PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
375 LogHeader, PhysicsScene.RegionName, tX, tY);
376 DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,terrainNotFound,pos={1},base={2}",
377 BSScene.DetailLogZero, pos, terrainBaseXYZ);
378 }
379 lastHeight = ret;
380 return ret;
381 }
382
383 public float GetWaterLevelAtXYZ(Vector3 pos)
384 {
385 float ret = WATER_HEIGHT_GETHEIGHT_RET;
386
387 Vector3 terrainBaseXYZ;
388 BSTerrainPhys physTerrain;
389 if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
390 {
391 ret = physTerrain.GetWaterLevelAtXYZ(pos);
392 }
393 else
394 {
395 PhysicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}",
396 LogHeader, PhysicsScene.RegionName, pos, terrainBaseXYZ, ret);
397 }
398 return ret;
399 }
400
401 // Given an address, return 'true' of there is a description of that terrain and output
402 // the descriptor class and the 'base' fo the addresses therein.
403 private bool GetTerrainPhysicalAtXYZ(Vector3 pos, out BSTerrainPhys outPhysTerrain, out Vector3 outTerrainBase)
404 {
405 int offsetX = ((int)(pos.X / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
406 int offsetY = ((int)(pos.Y / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
407 Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
408
409 BSTerrainPhys physTerrain = null;
410 lock (m_terrains)
411 {
412 m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain);
413 }
414 outTerrainBase = terrainBaseXYZ;
415 outPhysTerrain = physTerrain;
416 return (physTerrain != null);
417 }
418
419 // Although no one seems to check this, I do support combining.
420 public bool SupportsCombining()
421 {
422 return true;
423 }
424
425 // This routine is called two ways:
426 // One with 'offset' and 'pScene' zero and null but 'extents' giving the maximum
427 // extent of the combined regions. This is to inform the parent of the size
428 // of the combined regions.
429 // and one with 'offset' as the offset of the child region to the base region,
430 // 'pScene' pointing to the parent and 'extents' of zero. This informs the
431 // child of its relative base and new parent.
432 public void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
433 {
434 m_worldOffset = offset;
435 m_worldMax = extents;
436 MegaRegionParentPhysicsScene = pScene;
437 if (pScene != null)
438 {
439 // We are a child.
440 // We want m_worldMax to be the highest coordinate of our piece of terrain.
441 m_worldMax = offset + DefaultRegionSize;
442 }
443 DetailLog("{0},BSTerrainManager.Combine,offset={1},extents={2},wOffset={3},wMax={4}",
444 BSScene.DetailLogZero, offset, extents, m_worldOffset, m_worldMax);
445 }
446
447 // Unhook all the combining that I know about.
448 public void UnCombine(PhysicsScene pScene)
449 {
450 // Just like ODE, we don't do anything yet.
451 DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero);
452 }
453
454
455 private void DetailLog(string msg, params Object[] args)
456 {
457 PhysicsScene.PhysicsLogging.Write(msg, args);
458 }
459}
460}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainMesh.cs
new file mode 100644
index 0000000..6083dd4
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BSTerrainMesh.cs
@@ -0,0 +1,267 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30
31using OpenSim.Framework;
32using OpenSim.Region.Framework;
33using OpenSim.Region.CoreModules;
34using OpenSim.Region.Physics.Manager;
35
36using Nini.Config;
37using log4net;
38
39using OpenMetaverse;
40
41namespace OpenSim.Region.Physics.BulletSNPlugin
42{
43public sealed class BSTerrainMesh : BSTerrainPhys
44{
45 static string LogHeader = "[BULLETSIM TERRAIN MESH]";
46
47 private float[] m_savedHeightMap;
48 int m_sizeX;
49 int m_sizeY;
50
51 BulletShape m_terrainShape;
52 BulletBody m_terrainBody;
53
54 public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
55 : base(physicsScene, regionBase, id)
56 {
57 }
58
59 public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id /* parameters for making mesh */)
60 : base(physicsScene, regionBase, id)
61 {
62 }
63
64 // Create terrain mesh from a heightmap.
65 public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
66 Vector3 minCoords, Vector3 maxCoords)
67 : base(physicsScene, regionBase, id)
68 {
69 int indicesCount;
70 int[] indices;
71 int verticesCount;
72 float[] vertices;
73
74 m_savedHeightMap = initialMap;
75
76 m_sizeX = (int)(maxCoords.X - minCoords.X);
77 m_sizeY = (int)(maxCoords.Y - minCoords.Y);
78
79 if (!BSTerrainMesh.ConvertHeightmapToMesh(PhysicsScene, initialMap,
80 m_sizeX, m_sizeY,
81 (float)m_sizeX, (float)m_sizeY,
82 Vector3.Zero, 1.0f,
83 out indicesCount, out indices, out verticesCount, out vertices))
84 {
85 // DISASTER!!
86 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap", ID);
87 PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase);
88 // Something is very messed up and a crash is in our future.
89 return;
90 }
91 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,indices={1},indSz={2},vertices={3},vertSz={4}",
92 ID, indicesCount, indices.Length, verticesCount, vertices.Length);
93
94 m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
95 indicesCount, indices, verticesCount, vertices),
96 BSPhysicsShapeType.SHAPE_MESH);
97 if (!m_terrainShape.HasPhysicalShape)
98 {
99 // DISASTER!!
100 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID);
101 physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase);
102 // Something is very messed up and a crash is in our future.
103 return;
104 }
105
106 Vector3 pos = regionBase;
107 Quaternion rot = Quaternion.Identity;
108
109 m_terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2( m_terrainShape.ptr, ID, pos, rot));
110 if (!m_terrainBody.HasPhysicalBody)
111 {
112 // DISASTER!!
113 physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase);
114 // Something is very messed up and a crash is in our future.
115 return;
116 }
117
118 // Set current terrain attributes
119 BulletSimAPI.SetFriction2(m_terrainBody.ptr, BSParam.TerrainFriction);
120 BulletSimAPI.SetHitFraction2(m_terrainBody.ptr, BSParam.TerrainHitFraction);
121 BulletSimAPI.SetRestitution2(m_terrainBody.ptr, BSParam.TerrainRestitution);
122 BulletSimAPI.SetCollisionFlags2(m_terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
123
124 // Static objects are not very massive.
125 BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero);
126
127 // Put the new terrain to the world of physical objects
128 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr, pos, rot);
129
130 // Redo its bounding box now that it is in the world
131 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr);
132
133 m_terrainBody.collisionType = CollisionType.Terrain;
134 m_terrainBody.ApplyCollisionMask();
135
136 // Make it so the terrain will not move or be considered for movement.
137 BulletSimAPI.ForceActivationState2(m_terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
138 }
139
140 public override void Dispose()
141 {
142 if (m_terrainBody.HasPhysicalBody)
143 {
144 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
145 // Frees both the body and the shape.
146 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_terrainBody.ptr);
147 }
148 }
149
150 public override float GetTerrainHeightAtXYZ(Vector3 pos)
151 {
152 // For the moment use the saved heightmap to get the terrain height.
153 // TODO: raycast downward to find the true terrain below the position.
154 float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
155
156 int mapIndex = (int)pos.Y * m_sizeY + (int)pos.X;
157 try
158 {
159 ret = m_savedHeightMap[mapIndex];
160 }
161 catch
162 {
163 // Sometimes they give us wonky values of X and Y. Give a warning and return something.
164 PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
165 LogHeader, TerrainBase, pos);
166 ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
167 }
168 return ret;
169 }
170
171 // The passed position is relative to the base of the region.
172 public override float GetWaterLevelAtXYZ(Vector3 pos)
173 {
174 return PhysicsScene.SimpleWaterLevel;
175 }
176
177 // Convert the passed heightmap to mesh information suitable for CreateMeshShape2().
178 // Return 'true' if successfully created.
179 public static bool ConvertHeightmapToMesh(
180 BSScene physicsScene,
181 float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap
182 float extentX, float extentY, // zero based range for output vertices
183 Vector3 extentBase, // base to be added to all vertices
184 float magnification, // number of vertices to create between heightMap coords
185 out int indicesCountO, out int[] indicesO,
186 out int verticesCountO, out float[] verticesO)
187 {
188 bool ret = false;
189
190 int indicesCount = 0;
191 int verticesCount = 0;
192 int[] indices = new int[0];
193 float[] vertices = new float[0];
194
195 // Simple mesh creation which assumes magnification == 1.
196 // TODO: do a more general solution that scales, adds new vertices and smoothes the result.
197
198 // Create an array of vertices that is sizeX+1 by sizeY+1 (note the loop
199 // from zero to <= sizeX). The triangle indices are then generated as two triangles
200 // per heightmap point. There are sizeX by sizeY of these squares. The extra row and
201 // column of vertices are used to complete the triangles of the last row and column
202 // of the heightmap.
203 try
204 {
205 // One vertice per heightmap value plus the vertices off the top and bottom edge.
206 int totalVertices = (sizeX + 1) * (sizeY + 1);
207 vertices = new float[totalVertices * 3];
208 int totalIndices = sizeX * sizeY * 6;
209 indices = new int[totalIndices];
210
211 float magX = (float)sizeX / extentX;
212 float magY = (float)sizeY / extentY;
213 physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}",
214 BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY);
215 float minHeight = float.MaxValue;
216 // Note that sizeX+1 vertices are created since there is land between this and the next region.
217 for (int yy = 0; yy <= sizeY; yy++)
218 {
219 for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we go around sizeX + 1 times
220 {
221 int offset = yy * sizeX + xx;
222 // Extend the height with the height from the last row or column
223 if (yy == sizeY) offset -= sizeX;
224 if (xx == sizeX) offset -= 1;
225 float height = heightMap[offset];
226 minHeight = Math.Min(minHeight, height);
227 vertices[verticesCount + 0] = (float)xx * magX + extentBase.X;
228 vertices[verticesCount + 1] = (float)yy * magY + extentBase.Y;
229 vertices[verticesCount + 2] = height + extentBase.Z;
230 verticesCount += 3;
231 }
232 }
233 verticesCount = verticesCount / 3;
234
235 for (int yy = 0; yy < sizeY; yy++)
236 {
237 for (int xx = 0; xx < sizeX; xx++)
238 {
239 int offset = yy * (sizeX + 1) + xx;
240 // Each vertices is presumed to be the upper left corner of a box of two triangles
241 indices[indicesCount + 0] = offset;
242 indices[indicesCount + 1] = offset + 1;
243 indices[indicesCount + 2] = offset + sizeX + 1; // accounting for the extra column
244 indices[indicesCount + 3] = offset + 1;
245 indices[indicesCount + 4] = offset + sizeX + 2;
246 indices[indicesCount + 5] = offset + sizeX + 1;
247 indicesCount += 6;
248 }
249 }
250
251 ret = true;
252 }
253 catch (Exception e)
254 {
255 physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}",
256 LogHeader, physicsScene.RegionName, extentBase, e);
257 }
258
259 indicesCountO = indicesCount;
260 indicesO = indices;
261 verticesCountO = verticesCount;
262 verticesO = vertices;
263
264 return ret;
265 }
266}
267}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSNPlugin/BulletSimAPI.cs
new file mode 100644
index 0000000..6af59d6
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BulletSimAPI.cs
@@ -0,0 +1,1604 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.IO;
30using System.Runtime.InteropServices;
31using System.Security;
32using System.Text;
33using BulletXNA;
34using OpenMetaverse;
35using BulletXNA.LinearMath;
36using BulletXNA.BulletCollision;
37using BulletXNA.BulletDynamics;
38using BulletXNA.BulletCollision.CollisionDispatch;
39using OpenSim.Framework;
40
41namespace OpenSim.Region.Physics.BulletSNPlugin {
42
43// Classes to allow some type checking for the API
44// These hold pointers to allocated objects in the unmanaged space.
45
46
47
48 // Constraint type values as defined by Bullet
49public enum ConstraintType : int
50{
51 POINT2POINT_CONSTRAINT_TYPE = 3,
52 HINGE_CONSTRAINT_TYPE,
53 CONETWIST_CONSTRAINT_TYPE,
54 D6_CONSTRAINT_TYPE,
55 SLIDER_CONSTRAINT_TYPE,
56 CONTACT_CONSTRAINT_TYPE,
57 D6_SPRING_CONSTRAINT_TYPE,
58 MAX_CONSTRAINT_TYPE
59}
60
61
62// ===============================================================================
63[StructLayout(LayoutKind.Sequential)]
64public struct ConvexHull
65{
66 Vector3 Offset;
67 int VertexCount;
68 Vector3[] Vertices;
69}
70public enum BSPhysicsShapeType
71{
72 SHAPE_UNKNOWN = 0,
73 SHAPE_CAPSULE = 1,
74 SHAPE_BOX = 2,
75 SHAPE_CONE = 3,
76 SHAPE_CYLINDER = 4,
77 SHAPE_SPHERE = 5,
78 SHAPE_MESH = 6,
79 SHAPE_HULL = 7,
80 // following defined by BulletSim
81 SHAPE_GROUNDPLANE = 20,
82 SHAPE_TERRAIN = 21,
83 SHAPE_COMPOUND = 22,
84 SHAPE_HEIGHTMAP = 23,
85};
86
87// The native shapes have predefined shape hash keys
88public enum FixedShapeKey : ulong
89{
90 KEY_NONE = 0,
91 KEY_BOX = 1,
92 KEY_SPHERE = 2,
93 KEY_CONE = 3,
94 KEY_CYLINDER = 4,
95 KEY_CAPSULE = 5,
96}
97
98[StructLayout(LayoutKind.Sequential)]
99public struct ShapeData
100{
101 public uint ID;
102 public BSPhysicsShapeType Type;
103 public Vector3 Position;
104 public Quaternion Rotation;
105 public Vector3 Velocity;
106 public Vector3 Scale;
107 public float Mass;
108 public float Buoyancy;
109 public System.UInt64 HullKey;
110 public System.UInt64 MeshKey;
111 public float Friction;
112 public float Restitution;
113 public float Collidable; // true of things bump into this
114 public float Static; // true if a static object. Otherwise gravity, etc.
115 public float Solid; // true if object cannot be passed through
116 public Vector3 Size;
117
118 // note that bools are passed as floats since bool size changes by language and architecture
119 public const float numericTrue = 1f;
120 public const float numericFalse = 0f;
121}
122[StructLayout(LayoutKind.Sequential)]
123public struct SweepHit
124{
125 public uint ID;
126 public float Fraction;
127 public Vector3 Normal;
128 public Vector3 Point;
129}
130[StructLayout(LayoutKind.Sequential)]
131public struct RaycastHit
132{
133 public uint ID;
134 public float Fraction;
135 public Vector3 Normal;
136}
137[StructLayout(LayoutKind.Sequential)]
138public struct CollisionDesc
139{
140 public uint aID;
141 public uint bID;
142 public Vector3 point;
143 public Vector3 normal;
144}
145[StructLayout(LayoutKind.Sequential)]
146public struct EntityProperties
147{
148 public uint ID;
149 public Vector3 Position;
150 public Quaternion Rotation;
151 public Vector3 Velocity;
152 public Vector3 Acceleration;
153 public Vector3 RotationalVelocity;
154 public override string ToString()
155 {
156 return string.Format("ID:{0}, Pos:<{1:F},{2:F},{3:F}>, Rot:<{4:F},{5:F},{6:F},{7:F}>, LVel:<{8:F},{9:F},{10:F}>, AVel:<{11:F},{12:F},{13:F}>",
157 ID.ToString(),
158 Position.X,Position.Y,Position.Z,
159 Rotation.X,Rotation.Y,Rotation.Z,Rotation.W,
160 Velocity.X,Velocity.Y,Velocity.Z,
161 RotationalVelocity.X,RotationalVelocity.Y,RotationalVelocity.Z
162 );
163 }
164}
165
166// Format of this structure must match the definition in the C++ code
167// NOTE: adding the X causes compile breaks if used. These are unused symbols
168// that can be removed from both here and the unmanaged definition of this structure.
169[StructLayout(LayoutKind.Sequential)]
170public struct ConfigurationParameters
171{
172 public float defaultFriction;
173 public float defaultDensity;
174 public float defaultRestitution;
175 public float collisionMargin;
176 public float gravity;
177
178 public float XlinearDamping;
179 public float XangularDamping;
180 public float XdeactivationTime;
181 public float XlinearSleepingThreshold;
182 public float XangularSleepingThreshold;
183 public float XccdMotionThreshold;
184 public float XccdSweptSphereRadius;
185 public float XcontactProcessingThreshold;
186
187 public float XterrainImplementation;
188 public float XterrainFriction;
189 public float XterrainHitFraction;
190 public float XterrainRestitution;
191 public float XterrainCollisionMargin;
192
193 public float XavatarFriction;
194 public float XavatarStandingFriction;
195 public float XavatarDensity;
196 public float XavatarRestitution;
197 public float XavatarCapsuleWidth;
198 public float XavatarCapsuleDepth;
199 public float XavatarCapsuleHeight;
200 public float XavatarContactProcessingThreshold;
201
202 public float XvehicleAngularDamping;
203
204 public float maxPersistantManifoldPoolSize;
205 public float maxCollisionAlgorithmPoolSize;
206 public float shouldDisableContactPoolDynamicAllocation;
207 public float shouldForceUpdateAllAabbs;
208 public float shouldRandomizeSolverOrder;
209 public float shouldSplitSimulationIslands;
210 public float shouldEnableFrictionCaching;
211 public float numberOfSolverIterations;
212
213 public float XlinksetImplementation;
214 public float XlinkConstraintUseFrameOffset;
215 public float XlinkConstraintEnableTransMotor;
216 public float XlinkConstraintTransMotorMaxVel;
217 public float XlinkConstraintTransMotorMaxForce;
218 public float XlinkConstraintERP;
219 public float XlinkConstraintCFM;
220 public float XlinkConstraintSolverIterations;
221
222 public float physicsLoggingFrames;
223
224 public const float numericTrue = 1f;
225 public const float numericFalse = 0f;
226}
227
228
229// The states a bullet collision object can have
230
231public enum ActivationState : uint
232{
233 UNDEFINED = 0,
234 ACTIVE_TAG = 1,
235 ISLAND_SLEEPING = 2,
236 WANTS_DEACTIVATION = 3,
237 DISABLE_DEACTIVATION = 4,
238 DISABLE_SIMULATION = 5,
239}
240
241public enum CollisionObjectTypes : int
242{
243 CO_COLLISION_OBJECT = 1 << 0,
244 CO_RIGID_BODY = 1 << 1,
245 CO_GHOST_OBJECT = 1 << 2,
246 CO_SOFT_BODY = 1 << 3,
247 CO_HF_FLUID = 1 << 4,
248 CO_USER_TYPE = 1 << 5,
249}
250
251// Values used by Bullet and BulletSim to control object properties.
252// Bullet's "CollisionFlags" has more to do with operations on the
253// object (if collisions happen, if gravity effects it, ...).
254 [Flags]
255public enum CollisionFlags : uint
256{
257 CF_STATIC_OBJECT = 1 << 0,
258 CF_KINEMATIC_OBJECT = 1 << 1,
259 CF_NO_CONTACT_RESPONSE = 1 << 2,
260 CF_CUSTOM_MATERIAL_CALLBACK = 1 << 3,
261 CF_CHARACTER_OBJECT = 1 << 4,
262 CF_DISABLE_VISUALIZE_OBJECT = 1 << 5,
263 CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6,
264 // Following used by BulletSim to control collisions and updates
265 BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10,
266 BS_FLOATS_ON_WATER = 1 << 11,
267 BS_VEHICLE_COLLISIONS = 1 << 12,
268 BS_NONE = 0,
269 BS_ALL = 0xFFFFFFFF,
270
271 // These are the collision flags switched depending on physical state.
272 // The other flags are used for other things and should not be fooled with.
273 BS_ACTIVE = CF_STATIC_OBJECT
274 | CF_KINEMATIC_OBJECT
275 | CF_NO_CONTACT_RESPONSE
276};
277
278// Values for collisions groups and masks
279public enum CollisionFilterGroups : uint
280{
281 // Don't use the bit definitions!! Define the use in a
282 // filter/mask definition below. This way collision interactions
283 // are more easily debugged.
284 BNoneGroup = 0,
285 BDefaultGroup = 1 << 0,
286 BStaticGroup = 1 << 1,
287 BKinematicGroup = 1 << 2,
288 BDebrisGroup = 1 << 3,
289 BSensorTrigger = 1 << 4,
290 BCharacterGroup = 1 << 5,
291 BAllGroup = 0xFFFFFFFF,
292 // Filter groups defined by BulletSim
293 BGroundPlaneGroup = 1 << 10,
294 BTerrainGroup = 1 << 11,
295 BRaycastGroup = 1 << 12,
296 BSolidGroup = 1 << 13,
297 // BLinksetGroup = xx // a linkset proper is either static or dynamic
298 BLinksetChildGroup = 1 << 14,
299 // The collsion filters and masked are defined in one place -- don't want them scattered
300 AvatarGroup = BCharacterGroup,
301 AvatarMask = BAllGroup,
302 ObjectGroup = BSolidGroup,
303 ObjectMask = BAllGroup,
304 StaticObjectGroup = BStaticGroup,
305 StaticObjectMask = AvatarGroup | ObjectGroup, // static things don't interact with much
306 LinksetGroup = BLinksetChildGroup,
307 LinksetMask = BAllGroup & ~BLinksetChildGroup, // linkset objects don't collide with each other
308 VolumeDetectGroup = BSensorTrigger,
309 VolumeDetectMask = ~BSensorTrigger,
310 TerrainGroup = BTerrainGroup,
311 TerrainMask = BAllGroup & ~BStaticGroup, // static objects on the ground don't collide
312 GroundPlaneGroup = BGroundPlaneGroup,
313 GroundPlaneMask = BAllGroup
314
315};
316
317// CFM controls the 'hardness' of the constraint. 0=fixed, 0..1=violatable. Default=0
318// ERP controls amount of correction per tick. Usable range=0.1..0.8. Default=0.2.
319public enum ConstraintParams : int
320{
321 BT_CONSTRAINT_ERP = 1, // this one is not used in Bullet as of 20120730
322 BT_CONSTRAINT_STOP_ERP,
323 BT_CONSTRAINT_CFM,
324 BT_CONSTRAINT_STOP_CFM,
325};
326public enum ConstraintParamAxis : int
327{
328 AXIS_LINEAR_X = 0,
329 AXIS_LINEAR_Y,
330 AXIS_LINEAR_Z,
331 AXIS_ANGULAR_X,
332 AXIS_ANGULAR_Y,
333 AXIS_ANGULAR_Z,
334 AXIS_LINEAR_ALL = 20, // these last three added by BulletSim so we don't have to do zillions of calls
335 AXIS_ANGULAR_ALL,
336 AXIS_ALL
337};
338
339// ===============================================================================
340static class BulletSimAPI {
341 private static int m_collisionsThisFrame;
342 public delegate void DebugLogCallback(string msg);
343 /// <summary>
344 ///
345 /// </summary>
346 /// <param name="p"></param>
347 /// <param name="p_2"></param>
348 internal static bool RemoveObjectFromWorld2(object pWorld, object pBody)
349 {
350 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
351 RigidBody body = pBody as RigidBody;
352 world.RemoveRigidBody(body);
353 return true;
354 }
355
356 internal static void SetRestitution2(object pBody, float pRestitution)
357 {
358 RigidBody body = pBody as RigidBody;
359 body.SetRestitution(pRestitution);
360 }
361
362 internal static void SetMargin2(object pShape, float pMargin)
363 {
364 CollisionShape shape = pShape as CollisionShape;
365 shape.SetMargin(pMargin);
366 }
367
368 internal static void SetLocalScaling2(object pShape, Vector3 pScale)
369 {
370 CollisionShape shape = pShape as CollisionShape;
371 IndexedVector3 vec = new IndexedVector3(pScale.X, pScale.Y, pScale.Z);
372 shape.SetLocalScaling(ref vec);
373
374 }
375
376 internal static void SetContactProcessingThreshold2(object pBody, float contactprocessingthreshold)
377 {
378 RigidBody body = pBody as RigidBody;
379 body.SetContactProcessingThreshold(contactprocessingthreshold);
380 }
381
382 internal static void SetCcdMotionThreshold2(object pBody, float pccdMotionThreashold)
383 {
384 RigidBody body = pBody as RigidBody;
385 body.SetCcdMotionThreshold(pccdMotionThreashold);
386 }
387
388 internal static void SetCcdSweptSphereRadius2(object pBody, float pCcdSweptSphereRadius)
389 {
390 RigidBody body = pBody as RigidBody;
391 body.SetCcdSweptSphereRadius(pCcdSweptSphereRadius);
392 }
393
394 internal static void SetAngularFactorV2(object pBody, Vector3 pAngularFactor)
395 {
396 RigidBody body = pBody as RigidBody;
397 body.SetAngularFactor(new IndexedVector3(pAngularFactor.X, pAngularFactor.Y, pAngularFactor.Z));
398 }
399
400 internal static CollisionFlags AddToCollisionFlags2(object pBody, CollisionFlags pcollisionFlags)
401 {
402 CollisionObject body = pBody as CollisionObject;
403 CollisionFlags existingcollisionFlags = (CollisionFlags)(uint)body.GetCollisionFlags();
404 existingcollisionFlags |= pcollisionFlags;
405 body.SetCollisionFlags((BulletXNA.BulletCollision.CollisionFlags)(uint)existingcollisionFlags);
406 return (CollisionFlags) (uint) existingcollisionFlags;
407 }
408
409 internal static void AddObjectToWorld2(object pWorld, object pBody)
410 {
411 RigidBody body = pBody as RigidBody;
412 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
413 //if (!(body.GetCollisionShape().GetShapeType() == BroadphaseNativeTypes.STATIC_PLANE_PROXYTYPE && body.GetCollisionShape().GetShapeType() == BroadphaseNativeTypes.TERRAIN_SHAPE_PROXYTYPE))
414
415 world.AddRigidBody(body);
416
417 //if (body.GetBroadphaseHandle() != null)
418 // world.UpdateSingleAabb(body);
419 }
420
421 internal static void AddObjectToWorld2(object pWorld, object pBody, Vector3 _position, Quaternion _orientation)
422 {
423 RigidBody body = pBody as RigidBody;
424 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
425 //if (!(body.GetCollisionShape().GetShapeType() == BroadphaseNativeTypes.STATIC_PLANE_PROXYTYPE && body.GetCollisionShape().GetShapeType() == BroadphaseNativeTypes.TERRAIN_SHAPE_PROXYTYPE))
426
427 world.AddRigidBody(body);
428 IndexedVector3 vposition = new IndexedVector3(_position.X, _position.Y, _position.Z);
429 IndexedQuaternion vquaternion = new IndexedQuaternion(_orientation.X, _orientation.Y, _orientation.Z,
430 _orientation.W);
431 IndexedMatrix mat = IndexedMatrix.CreateFromQuaternion(vquaternion);
432 mat._origin = vposition;
433 body.SetWorldTransform(mat);
434 //if (body.GetBroadphaseHandle() != null)
435 // world.UpdateSingleAabb(body);
436 }
437
438 internal static void ForceActivationState2(object pBody, ActivationState pActivationState)
439 {
440 CollisionObject body = pBody as CollisionObject;
441 body.ForceActivationState((BulletXNA.BulletCollision.ActivationState)(uint)pActivationState);
442 }
443
444 internal static void UpdateSingleAabb2(object pWorld, object pBody)
445 {
446 CollisionObject body = pBody as CollisionObject;
447 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
448 world.UpdateSingleAabb(body);
449 }
450
451 internal static bool SetCollisionGroupMask2(object pBody, uint pGroup, uint pMask)
452 {
453 RigidBody body = pBody as RigidBody;
454 body.GetBroadphaseHandle().m_collisionFilterGroup = (BulletXNA.BulletCollision.CollisionFilterGroups) pGroup;
455 body.GetBroadphaseHandle().m_collisionFilterGroup = (BulletXNA.BulletCollision.CollisionFilterGroups) pGroup;
456 if ((uint) body.GetBroadphaseHandle().m_collisionFilterGroup == 0)
457 return false;
458 return true;
459 }
460
461 internal static void ClearAllForces2(object pBody)
462 {
463 CollisionObject body = pBody as CollisionObject;
464 IndexedVector3 zeroVector = new IndexedVector3(0, 0, 0);
465 body.SetInterpolationLinearVelocity(ref zeroVector);
466 body.SetInterpolationAngularVelocity(ref zeroVector);
467 IndexedMatrix bodytransform = body.GetWorldTransform();
468
469 body.SetInterpolationWorldTransform(ref bodytransform);
470
471 if (body is RigidBody)
472 {
473 RigidBody rigidbody = body as RigidBody;
474 rigidbody.SetLinearVelocity(zeroVector);
475 rigidbody.SetAngularVelocity(zeroVector);
476 rigidbody.ClearForces();
477 }
478 }
479
480 internal static void SetInterpolationAngularVelocity2(object pBody, Vector3 pVector3)
481 {
482 RigidBody body = pBody as RigidBody;
483 IndexedVector3 vec = new IndexedVector3(pVector3.X, pVector3.Y, pVector3.Z);
484 body.SetInterpolationAngularVelocity(ref vec);
485 }
486
487 internal static void SetAngularVelocity2(object pBody, Vector3 pVector3)
488 {
489 RigidBody body = pBody as RigidBody;
490 IndexedVector3 vec = new IndexedVector3(pVector3.X, pVector3.Y, pVector3.Z);
491 body.SetAngularVelocity(ref vec);
492 }
493
494 internal static void ClearForces2(object pBody)
495 {
496 RigidBody body = pBody as RigidBody;
497 body.ClearForces();
498 }
499
500 internal static void SetTranslation2(object pBody, Vector3 _position, Quaternion _orientation)
501 {
502 RigidBody body = pBody as RigidBody;
503 IndexedVector3 vposition = new IndexedVector3(_position.X, _position.Y, _position.Z);
504 IndexedQuaternion vquaternion = new IndexedQuaternion(_orientation.X, _orientation.Y, _orientation.Z,
505 _orientation.W);
506 IndexedMatrix mat = IndexedMatrix.CreateFromQuaternion(vquaternion);
507 mat._origin = vposition;
508 body.SetWorldTransform(mat);
509
510 }
511
512 internal static Vector3 GetPosition2(object pBody)
513 {
514 RigidBody body = pBody as RigidBody;
515 IndexedVector3 pos = body.GetInterpolationWorldTransform()._origin;
516 return new Vector3(pos.X, pos.Y, pos.Z);
517 }
518
519 internal static Vector3 CalculateLocalInertia2(object pShape, float pphysMass)
520 {
521 CollisionShape shape = pShape as CollisionShape;
522 IndexedVector3 inertia = IndexedVector3.Zero;
523 shape.CalculateLocalInertia(pphysMass, out inertia);
524 return new Vector3(inertia.X, inertia.Y, inertia.Z);
525 }
526
527 internal static void SetMassProps2(object pBody, float pphysMass, Vector3 plocalInertia)
528 {
529 RigidBody body = pBody as RigidBody;
530 IndexedVector3 inertia = new IndexedVector3(plocalInertia.X, plocalInertia.Y, plocalInertia.Z);
531 body.SetMassProps(pphysMass, inertia);
532 }
533
534
535 internal static void SetObjectForce2(object pBody, Vector3 _force)
536 {
537 RigidBody body = pBody as RigidBody;
538 IndexedVector3 force = new IndexedVector3(_force.X, _force.Y, _force.Z);
539 body.SetTotalForce(ref force);
540 }
541
542 internal static void SetFriction2(object pBody, float _currentFriction)
543 {
544 RigidBody body = pBody as RigidBody;
545 body.SetFriction(_currentFriction);
546 }
547
548 internal static void SetLinearVelocity2(object pBody, Vector3 _velocity)
549 {
550 RigidBody body = pBody as RigidBody;
551 IndexedVector3 velocity = new IndexedVector3(_velocity.X, _velocity.Y, _velocity.Z);
552 body.SetLinearVelocity(velocity);
553 }
554
555 internal static void Activate2(object pBody, bool pforceactivation)
556 {
557 RigidBody body = pBody as RigidBody;
558 body.Activate(pforceactivation);
559
560 }
561
562 internal static Quaternion GetOrientation2(object pBody)
563 {
564 RigidBody body = pBody as RigidBody;
565 IndexedQuaternion mat = body.GetInterpolationWorldTransform().GetRotation();
566 return new Quaternion(mat.X, mat.Y, mat.Z, mat.W);
567 }
568
569 internal static CollisionFlags RemoveFromCollisionFlags2(object pBody, CollisionFlags pcollisionFlags)
570 {
571 RigidBody body = pBody as RigidBody;
572 CollisionFlags existingcollisionFlags = (CollisionFlags)(uint)body.GetCollisionFlags();
573 existingcollisionFlags &= ~pcollisionFlags;
574 body.SetCollisionFlags((BulletXNA.BulletCollision.CollisionFlags)(uint)existingcollisionFlags);
575 return (CollisionFlags)(uint)existingcollisionFlags;
576 }
577
578 internal static void SetGravity2(object pBody, Vector3 pGravity)
579 {
580 RigidBody body = pBody as RigidBody;
581 IndexedVector3 gravity = new IndexedVector3(pGravity.X, pGravity.Y, pGravity.Z);
582 body.SetGravity(gravity);
583 }
584
585 internal static bool DestroyConstraint2(object pBody, object pConstraint)
586 {
587 RigidBody body = pBody as RigidBody;
588 TypedConstraint constraint = pConstraint as TypedConstraint;
589 body.RemoveConstraintRef(constraint);
590 return true;
591 }
592
593 internal static bool SetLinearLimits2(object pConstraint, Vector3 low, Vector3 high)
594 {
595 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
596 IndexedVector3 lowlimit = new IndexedVector3(low.X, low.Y, low.Z);
597 IndexedVector3 highlimit = new IndexedVector3(high.X, high.Y, high.Z);
598 constraint.SetLinearLowerLimit(lowlimit);
599 constraint.SetLinearUpperLimit(highlimit);
600 return true;
601 }
602
603 internal static bool SetAngularLimits2(object pConstraint, Vector3 low, Vector3 high)
604 {
605 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
606 IndexedVector3 lowlimit = new IndexedVector3(low.X, low.Y, low.Z);
607 IndexedVector3 highlimit = new IndexedVector3(high.X, high.Y, high.Z);
608 constraint.SetAngularLowerLimit(lowlimit);
609 constraint.SetAngularUpperLimit(highlimit);
610 return true;
611 }
612
613 internal static void SetConstraintNumSolverIterations2(object pConstraint, float cnt)
614 {
615 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
616 constraint.SetOverrideNumSolverIterations((int)cnt);
617 }
618
619 internal static void CalculateTransforms2(object pConstraint)
620 {
621 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
622 constraint.CalculateTransforms();
623 }
624
625 internal static void SetConstraintEnable2(object pConstraint, float p_2)
626 {
627 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
628 constraint.SetEnabled((p_2 == 0) ? false : true);
629 }
630
631
632 //BulletSimAPI.Create6DofConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,frame1, frame1rot,frame2, frame2rot,useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
633 internal static object Create6DofConstraint2(object pWorld, object pBody1, object pBody2, Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies)
634
635 {
636 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
637 RigidBody body1 = pBody1 as RigidBody;
638 RigidBody body2 = pBody2 as RigidBody;
639 IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z);
640 IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W);
641 IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot);
642 frame1._origin = frame1v;
643
644 IndexedVector3 frame2v = new IndexedVector3(pframe2.X, pframe2.Y, pframe2.Z);
645 IndexedQuaternion frame2rot = new IndexedQuaternion(pframe2rot.X, pframe2rot.Y, pframe2rot.Z, pframe2rot.W);
646 IndexedMatrix frame2 = IndexedMatrix.CreateFromQuaternion(frame2rot);
647 frame2._origin = frame1v;
648
649 Generic6DofConstraint consttr = new Generic6DofConstraint(body1, body2, ref frame1, ref frame2,
650 puseLinearReferenceFrameA);
651 consttr.CalculateTransforms();
652 world.AddConstraint(consttr,pdisableCollisionsBetweenLinkedBodies);
653
654 return consttr;
655 }
656
657
658 /// <summary>
659 ///
660 /// </summary>
661 /// <param name="pWorld"></param>
662 /// <param name="pBody1"></param>
663 /// <param name="pBody2"></param>
664 /// <param name="pjoinPoint"></param>
665 /// <param name="puseLinearReferenceFrameA"></param>
666 /// <param name="pdisableCollisionsBetweenLinkedBodies"></param>
667 /// <returns></returns>
668 internal static object Create6DofConstraintToPoint2(object pWorld, object pBody1, object pBody2, Vector3 pjoinPoint, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies)
669 {
670 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
671 RigidBody body1 = pBody1 as RigidBody;
672 RigidBody body2 = pBody2 as RigidBody;
673 IndexedMatrix frame1 = new IndexedMatrix(IndexedBasisMatrix.Identity, new IndexedVector3(0, 0, 0));
674 IndexedMatrix frame2 = new IndexedMatrix(IndexedBasisMatrix.Identity, new IndexedVector3(0, 0, 0));
675
676 IndexedVector3 joinPoint = new IndexedVector3(pjoinPoint.X, pjoinPoint.Y, pjoinPoint.Z);
677 IndexedMatrix mat = IndexedMatrix.Identity;
678 mat._origin = new IndexedVector3(pjoinPoint.X, pjoinPoint.Y, pjoinPoint.Z);
679 frame1._origin = body1.GetWorldTransform().Inverse()*joinPoint;
680 frame2._origin = body2.GetWorldTransform().Inverse()*joinPoint;
681
682 Generic6DofConstraint consttr = new Generic6DofConstraint(body1, body2, ref frame1, ref frame2, puseLinearReferenceFrameA);
683 consttr.CalculateTransforms();
684 world.AddConstraint(consttr, pdisableCollisionsBetweenLinkedBodies);
685
686 return consttr;
687 }
688 //SetFrames2(m_constraint.ptr, frameA, frameArot, frameB, frameBrot);
689 internal static void SetFrames2(object pConstraint, Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot)
690 {
691 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
692 IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z);
693 IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W);
694 IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot);
695 frame1._origin = frame1v;
696
697 IndexedVector3 frame2v = new IndexedVector3(pframe2.X, pframe2.Y, pframe2.Z);
698 IndexedQuaternion frame2rot = new IndexedQuaternion(pframe2rot.X, pframe2rot.Y, pframe2rot.Z, pframe2rot.W);
699 IndexedMatrix frame2 = IndexedMatrix.CreateFromQuaternion(frame2rot);
700 frame2._origin = frame1v;
701 constraint.SetFrames(ref frame1, ref frame2);
702 }
703
704
705
706
707 internal static bool IsInWorld2(object pWorld, object pShapeObj)
708 {
709 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
710 CollisionObject shape = pShapeObj as CollisionObject;
711 return world.IsInWorld(shape);
712 }
713
714 internal static void SetInterpolationLinearVelocity2(object pBody, Vector3 VehicleVelocity)
715 {
716 RigidBody body = pBody as RigidBody;
717 IndexedVector3 velocity = new IndexedVector3(VehicleVelocity.X, VehicleVelocity.Y, VehicleVelocity.Z);
718 body.SetInterpolationLinearVelocity(ref velocity);
719 }
720
721 internal static bool UseFrameOffset2(object pConstraint, float onOff)
722 {
723 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
724 constraint.SetUseFrameOffset((onOff == 0) ? false : true);
725 return true;
726 }
727 //SetBreakingImpulseThreshold2(m_constraint.ptr, threshold);
728 internal static bool SetBreakingImpulseThreshold2(object pConstraint, float threshold)
729 {
730 Generic6DofConstraint constraint = pConstraint as Generic6DofConstraint;
731 constraint.SetBreakingImpulseThreshold(threshold);
732 return true;
733 }
734 //BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping);
735 internal static void SetAngularDamping2(object pBody, float angularDamping)
736 {
737 RigidBody body = pBody as RigidBody;
738 float lineardamping = body.GetLinearDamping();
739 body.SetDamping(lineardamping, angularDamping);
740
741 }
742
743 internal static void UpdateInertiaTensor2(object pBody)
744 {
745 RigidBody body = pBody as RigidBody;
746 body.UpdateInertiaTensor();
747 }
748
749 internal static void RecalculateCompoundShapeLocalAabb2( object pCompoundShape)
750 {
751
752 CompoundShape shape = pCompoundShape as CompoundShape;
753 shape.RecalculateLocalAabb();
754 }
755
756 //BulletSimAPI.GetCollisionFlags2(PhysBody.ptr)
757 internal static CollisionFlags GetCollisionFlags2(object pBody)
758 {
759 RigidBody body = pBody as RigidBody;
760 uint flags = (uint)body.GetCollisionFlags();
761 return (CollisionFlags) flags;
762 }
763
764 internal static void SetDamping2(object pBody, float pLinear, float pAngular)
765 {
766 RigidBody body = pBody as RigidBody;
767 body.SetDamping(pLinear, pAngular);
768 }
769 //PhysBody.ptr, PhysicsScene.Params.deactivationTime);
770 internal static void SetDeactivationTime2(object pBody, float pDeactivationTime)
771 {
772 RigidBody body = pBody as RigidBody;
773 body.SetDeactivationTime(pDeactivationTime);
774 }
775 //SetSleepingThresholds2(PhysBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold);
776 internal static void SetSleepingThresholds2(object pBody, float plinearSleepingThreshold, float pangularSleepingThreshold)
777 {
778 RigidBody body = pBody as RigidBody;
779 body.SetSleepingThresholds(plinearSleepingThreshold, pangularSleepingThreshold);
780 }
781
782 internal static CollisionObjectTypes GetBodyType2(object pBody)
783 {
784 RigidBody body = pBody as RigidBody;
785 return (CollisionObjectTypes)(int) body.GetInternalType();
786 }
787
788 //BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, fSum);
789 internal static void ApplyCentralForce2(object pBody, Vector3 pfSum)
790 {
791 RigidBody body = pBody as RigidBody;
792 IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z);
793 body.ApplyCentralForce(ref fSum);
794 }
795 internal static void ApplyCentralImpulse2(object pBody, Vector3 pfSum)
796 {
797 RigidBody body = pBody as RigidBody;
798 IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z);
799 body.ApplyCentralImpulse(ref fSum);
800 }
801 internal static void ApplyTorque2(object pBody, Vector3 pfSum)
802 {
803 RigidBody body = pBody as RigidBody;
804 IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z);
805 body.ApplyTorque(ref fSum);
806 }
807 internal static void ApplyTorqueImpulse2(object pBody, Vector3 pfSum)
808 {
809 RigidBody body = pBody as RigidBody;
810 IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z);
811 body.ApplyTorqueImpulse(ref fSum);
812 }
813
814 internal static void DumpRigidBody2(object p, object p_2)
815 {
816 //TODO:
817 }
818
819 internal static void DumpCollisionShape2(object p, object p_2)
820 {
821 //TODO:
822 }
823
824 internal static void DestroyObject2(object p, object p_2)
825 {
826 //TODO:
827 }
828
829 internal static void Shutdown2(object pWorld)
830 {
831 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
832 world.Cleanup();
833 }
834
835 internal static void DeleteCollisionShape2(object p, object p_2)
836 {
837 //TODO:
838 }
839 //(sim.ptr, shape.ptr, prim.LocalID, prim.RawPosition, prim.RawOrientation);
840
841 internal static object CreateBodyFromShape2(object pWorld, object pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation)
842 {
843 CollisionWorld world = pWorld as CollisionWorld;
844 IndexedMatrix mat =
845 IndexedMatrix.CreateFromQuaternion(new IndexedQuaternion(pRawOrientation.X, pRawOrientation.Y,
846 pRawOrientation.Z, pRawOrientation.W));
847 mat._origin = new IndexedVector3(pRawPosition.X, pRawPosition.Y, pRawPosition.Z);
848 CollisionShape shape = pShape as CollisionShape;
849 //UpdateSingleAabb2(world, shape);
850 // TODO: Feed Update array into null
851 RigidBody body = new RigidBody(0,new SimMotionState(world,pLocalID,mat,null),shape,IndexedVector3.Zero);
852
853 body.SetUserPointer(pLocalID);
854 return body;
855 }
856
857
858 internal static object CreateBodyWithDefaultMotionState2( object pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation)
859 {
860
861 IndexedMatrix mat =
862 IndexedMatrix.CreateFromQuaternion(new IndexedQuaternion(pRawOrientation.X, pRawOrientation.Y,
863 pRawOrientation.Z, pRawOrientation.W));
864 mat._origin = new IndexedVector3(pRawPosition.X, pRawPosition.Y, pRawPosition.Z);
865
866 CollisionShape shape = pShape as CollisionShape;
867
868 // TODO: Feed Update array into null
869 RigidBody body = new RigidBody(0, new DefaultMotionState( mat, IndexedMatrix.Identity), shape, IndexedVector3.Zero);
870 body.SetWorldTransform(mat);
871 body.SetUserPointer(pLocalID);
872 return body;
873 }
874 //(m_mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
875 internal static void SetCollisionFlags2(object pBody, CollisionFlags collisionFlags)
876 {
877 RigidBody body = pBody as RigidBody;
878 body.SetCollisionFlags((BulletXNA.BulletCollision.CollisionFlags) (uint) collisionFlags);
879 }
880 //(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
881 internal static void SetHitFraction2(object pBody, float pHitFraction)
882 {
883 RigidBody body = pBody as RigidBody;
884 body.SetHitFraction(pHitFraction);
885 }
886 //BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale);
887 internal static object BuildCapsuleShape2(object pWorld, float pRadius, float pHeight, Vector3 pScale)
888 {
889 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
890 IndexedVector3 scale = new IndexedVector3(pScale.X, pScale.Y, pScale.Z);
891 CapsuleShapeZ capsuleShapeZ = new CapsuleShapeZ(pRadius, pHeight);
892 capsuleShapeZ.SetMargin(world.WorldSettings.Params.collisionMargin);
893 capsuleShapeZ.SetLocalScaling(ref scale);
894
895 return capsuleShapeZ;
896 }
897
898 public static object Initialize2(Vector3 worldExtent, ConfigurationParameters[] o, int mMaxCollisionsPerFrame, ref List<BulletXNA.CollisionDesc> collisionArray, int mMaxUpdatesPerFrame, ref List<BulletXNA.EntityProperties> updateArray, object mDebugLogCallbackHandle)
899 {
900 CollisionWorld.WorldData.ParamData p = new CollisionWorld.WorldData.ParamData();
901
902 p.angularDamping = o[0].XangularDamping;
903 p.defaultFriction = o[0].defaultFriction;
904 p.defaultFriction = o[0].defaultFriction;
905 p.defaultDensity = o[0].defaultDensity;
906 p.defaultRestitution = o[0].defaultRestitution;
907 p.collisionMargin = o[0].collisionMargin;
908 p.gravity = o[0].gravity;
909
910 p.linearDamping = o[0].XlinearDamping;
911 p.angularDamping = o[0].XangularDamping;
912 p.deactivationTime = o[0].XdeactivationTime;
913 p.linearSleepingThreshold = o[0].XlinearSleepingThreshold;
914 p.angularSleepingThreshold = o[0].XangularSleepingThreshold;
915 p.ccdMotionThreshold = o[0].XccdMotionThreshold;
916 p.ccdSweptSphereRadius = o[0].XccdSweptSphereRadius;
917 p.contactProcessingThreshold = o[0].XcontactProcessingThreshold;
918
919 p.terrainImplementation = o[0].XterrainImplementation;
920 p.terrainFriction = o[0].XterrainFriction;
921
922 p.terrainHitFraction = o[0].XterrainHitFraction;
923 p.terrainRestitution = o[0].XterrainRestitution;
924 p.terrainCollisionMargin = o[0].XterrainCollisionMargin;
925
926 p.avatarFriction = o[0].XavatarFriction;
927 p.avatarStandingFriction = o[0].XavatarStandingFriction;
928 p.avatarDensity = o[0].XavatarDensity;
929 p.avatarRestitution = o[0].XavatarRestitution;
930 p.avatarCapsuleWidth = o[0].XavatarCapsuleWidth;
931 p.avatarCapsuleDepth = o[0].XavatarCapsuleDepth;
932 p.avatarCapsuleHeight = o[0].XavatarCapsuleHeight;
933 p.avatarContactProcessingThreshold = o[0].XavatarContactProcessingThreshold;
934
935 p.vehicleAngularDamping = o[0].XvehicleAngularDamping;
936
937 p.maxPersistantManifoldPoolSize = o[0].maxPersistantManifoldPoolSize;
938 p.maxCollisionAlgorithmPoolSize = o[0].maxCollisionAlgorithmPoolSize;
939 p.shouldDisableContactPoolDynamicAllocation = o[0].shouldDisableContactPoolDynamicAllocation;
940 p.shouldForceUpdateAllAabbs = o[0].shouldForceUpdateAllAabbs;
941 p.shouldRandomizeSolverOrder = o[0].shouldRandomizeSolverOrder;
942 p.shouldSplitSimulationIslands = o[0].shouldSplitSimulationIslands;
943 p.shouldEnableFrictionCaching = o[0].shouldEnableFrictionCaching;
944 p.numberOfSolverIterations = o[0].numberOfSolverIterations;
945
946 p.linksetImplementation = o[0].XlinksetImplementation;
947 p.linkConstraintUseFrameOffset = o[0].XlinkConstraintUseFrameOffset;
948 p.linkConstraintEnableTransMotor = o[0].XlinkConstraintEnableTransMotor;
949 p.linkConstraintTransMotorMaxVel = o[0].XlinkConstraintTransMotorMaxVel;
950 p.linkConstraintTransMotorMaxForce = o[0].XlinkConstraintTransMotorMaxForce;
951 p.linkConstraintERP = o[0].XlinkConstraintERP;
952 p.linkConstraintCFM = o[0].XlinkConstraintCFM;
953 p.linkConstraintSolverIterations = o[0].XlinkConstraintSolverIterations;
954 p.physicsLoggingFrames = o[0].physicsLoggingFrames;
955 DefaultCollisionConstructionInfo ccci = new DefaultCollisionConstructionInfo();
956
957 DefaultCollisionConfiguration cci = new DefaultCollisionConfiguration();
958 CollisionDispatcher m_dispatcher = new CollisionDispatcher(cci);
959
960
961 if (p.maxPersistantManifoldPoolSize > 0)
962 cci.m_persistentManifoldPoolSize = (int)p.maxPersistantManifoldPoolSize;
963 if (p.shouldDisableContactPoolDynamicAllocation !=0)
964 m_dispatcher.SetDispatcherFlags(DispatcherFlags.CD_DISABLE_CONTACTPOOL_DYNAMIC_ALLOCATION);
965 //if (p.maxCollisionAlgorithmPoolSize >0 )
966
967 DbvtBroadphase m_broadphase = new DbvtBroadphase();
968 //IndexedVector3 aabbMin = new IndexedVector3(0, 0, 0);
969 //IndexedVector3 aabbMax = new IndexedVector3(256, 256, 256);
970
971 //AxisSweep3Internal m_broadphase2 = new AxisSweep3Internal(ref aabbMin, ref aabbMax, Convert.ToInt32(0xfffe), 0xffff, ushort.MaxValue/2, null, true);
972 m_broadphase.GetOverlappingPairCache().SetInternalGhostPairCallback(new GhostPairCallback());
973
974 SequentialImpulseConstraintSolver m_solver = new SequentialImpulseConstraintSolver();
975
976 DiscreteDynamicsWorld world = new DiscreteDynamicsWorld(m_dispatcher, m_broadphase, m_solver, cci);
977 world.UpdatedObjects = updateArray;
978 world.UpdatedCollisions = collisionArray;
979 world.WorldSettings.Params = p;
980 world.SetForceUpdateAllAabbs(p.shouldForceUpdateAllAabbs != 0);
981 world.GetSolverInfo().m_solverMode = SolverMode.SOLVER_USE_WARMSTARTING | SolverMode.SOLVER_SIMD;
982 if (p.shouldRandomizeSolverOrder != 0)
983 world.GetSolverInfo().m_solverMode |= SolverMode.SOLVER_RANDMIZE_ORDER;
984
985 world.GetSimulationIslandManager().SetSplitIslands(p.shouldSplitSimulationIslands != 0);
986 //world.GetDispatchInfo().m_enableSatConvex Not implemented in C# port
987
988 if (p.shouldEnableFrictionCaching != 0)
989 world.GetSolverInfo().m_solverMode |= SolverMode.SOLVER_ENABLE_FRICTION_DIRECTION_CACHING;
990
991 if (p.numberOfSolverIterations > 0)
992 world.GetSolverInfo().m_numIterations = (int) p.numberOfSolverIterations;
993
994
995 world.GetSolverInfo().m_damping = world.WorldSettings.Params.linearDamping;
996 world.GetSolverInfo().m_restitution = world.WorldSettings.Params.defaultRestitution;
997 world.GetSolverInfo().m_globalCfm = 0.0f;
998 world.GetSolverInfo().m_tau = 0.6f;
999 world.GetSolverInfo().m_friction = 0.3f;
1000 world.GetSolverInfo().m_maxErrorReduction = 20f;
1001 world.GetSolverInfo().m_numIterations = 10;
1002 world.GetSolverInfo().m_erp = 0.2f;
1003 world.GetSolverInfo().m_erp2 = 0.1f;
1004 world.GetSolverInfo().m_sor = 1.0f;
1005 world.GetSolverInfo().m_splitImpulse = false;
1006 world.GetSolverInfo().m_splitImpulsePenetrationThreshold = -0.02f;
1007 world.GetSolverInfo().m_linearSlop = 0.0f;
1008 world.GetSolverInfo().m_warmstartingFactor = 0.85f;
1009 world.GetSolverInfo().m_restingContactRestitutionThreshold = 2;
1010 world.SetForceUpdateAllAabbs(true);
1011
1012
1013 world.SetGravity(new IndexedVector3(0,0,p.gravity));
1014
1015 return world;
1016 }
1017 //m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL
1018 internal static bool SetConstraintParam2(object pConstraint, ConstraintParams paramIndex, float paramvalue, ConstraintParamAxis axis)
1019 {
1020 Generic6DofConstraint constrain = pConstraint as Generic6DofConstraint;
1021 if (axis == ConstraintParamAxis.AXIS_LINEAR_ALL || axis == ConstraintParamAxis.AXIS_ALL)
1022 {
1023 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams) (int) paramIndex, paramvalue, 0);
1024 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams) (int) paramIndex, paramvalue, 1);
1025 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams) (int) paramIndex, paramvalue, 2);
1026 }
1027 if (axis == ConstraintParamAxis.AXIS_ANGULAR_ALL || axis == ConstraintParamAxis.AXIS_ALL)
1028 {
1029 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, 3);
1030 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, 4);
1031 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, 5);
1032 }
1033 if (axis == ConstraintParamAxis.AXIS_LINEAR_ALL)
1034 {
1035 constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, (int)axis);
1036 }
1037 return true;
1038 }
1039
1040 internal static bool PushUpdate2(object pCollisionObject)
1041 {
1042 bool ret = false;
1043 RigidBody rb = pCollisionObject as RigidBody;
1044 if (rb != null)
1045 {
1046 SimMotionState sms = rb.GetMotionState() as SimMotionState;
1047 if (sms != null)
1048 {
1049 IndexedMatrix wt = IndexedMatrix.Identity;
1050 sms.GetWorldTransform(out wt);
1051 sms.SetWorldTransform(ref wt, true);
1052 ret = true;
1053 }
1054 }
1055 return ret;
1056
1057 }
1058
1059 internal static bool IsCompound2(object pShape)
1060 {
1061 CollisionShape shape = pShape as CollisionShape;
1062 return shape.IsCompound();
1063 }
1064 internal static bool IsPloyhedral2(object pShape)
1065 {
1066 CollisionShape shape = pShape as CollisionShape;
1067 return shape.IsPolyhedral();
1068 }
1069 internal static bool IsConvex2d2(object pShape)
1070 {
1071 CollisionShape shape = pShape as CollisionShape;
1072 return shape.IsConvex2d();
1073 }
1074 internal static bool IsConvex2(object pShape)
1075 {
1076 CollisionShape shape = pShape as CollisionShape;
1077 return shape.IsConvex();
1078 }
1079 internal static bool IsNonMoving2(object pShape)
1080 {
1081 CollisionShape shape = pShape as CollisionShape;
1082 return shape.IsNonMoving();
1083 }
1084 internal static bool IsConcave2(object pShape)
1085 {
1086 CollisionShape shape = pShape as CollisionShape;
1087 return shape.IsConcave();
1088 }
1089 internal static bool IsInfinite2(object pShape)
1090 {
1091 CollisionShape shape = pShape as CollisionShape;
1092 return shape.IsInfinite();
1093 }
1094 internal static bool IsNativeShape2(object pShape)
1095 {
1096 CollisionShape shape = pShape as CollisionShape;
1097 bool ret;
1098 switch (shape.GetShapeType())
1099 {
1100 case BroadphaseNativeTypes.BOX_SHAPE_PROXYTYPE:
1101 case BroadphaseNativeTypes.CONE_SHAPE_PROXYTYPE:
1102 case BroadphaseNativeTypes.SPHERE_SHAPE_PROXYTYPE:
1103 case BroadphaseNativeTypes.CYLINDER_SHAPE_PROXYTYPE:
1104 ret = true;
1105 break;
1106 default:
1107 ret = false;
1108 break;
1109 }
1110 return ret;
1111 }
1112 //sim.ptr, shape.ptr,prim.LocalID, prim.RawPosition, prim.RawOrientation
1113 internal static object CreateGhostFromShape2(object pWorld, object pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation)
1114 {
1115 IndexedMatrix bodyTransform = new IndexedMatrix();
1116 bodyTransform._origin = new IndexedVector3(pRawPosition.X, pRawPosition.Y, pRawPosition.Z);
1117 bodyTransform.SetRotation(new IndexedQuaternion(pRawOrientation.X,pRawOrientation.Y,pRawOrientation.Z,pRawOrientation.W));
1118 GhostObject gObj = new PairCachingGhostObject();
1119 gObj.SetWorldTransform(bodyTransform);
1120 CollisionShape shape = pShape as CollisionShape;
1121 gObj.SetCollisionShape(shape);
1122 gObj.SetUserPointer(pLocalID);
1123 // TODO: Add to Special CollisionObjects!
1124 return gObj;
1125 }
1126
1127 public static void SetCollisionShape2(object pWorld, object pObj, object pShape)
1128 {
1129 var world = pWorld as DiscreteDynamicsWorld;
1130 var obj = pObj as CollisionObject;
1131 var shape = pShape as CollisionShape;
1132 obj.SetCollisionShape(shape);
1133
1134 }
1135 //(PhysicsScene.World.ptr, nativeShapeData)
1136 internal static object BuildNativeShape2(object pWorld, ShapeData pShapeData)
1137 {
1138 var world = pWorld as DiscreteDynamicsWorld;
1139 CollisionShape shape = null;
1140 switch (pShapeData.Type)
1141 {
1142 case BSPhysicsShapeType.SHAPE_BOX:
1143 shape = new BoxShape(new IndexedVector3(0.5f,0.5f,0.5f));
1144 break;
1145 case BSPhysicsShapeType.SHAPE_CONE:
1146 shape = new ConeShapeZ(0.5f, 1.0f);
1147 break;
1148 case BSPhysicsShapeType.SHAPE_CYLINDER:
1149 shape = new CylinderShapeZ(new IndexedVector3(0.5f, 0.5f, 0.5f));
1150 break;
1151 case BSPhysicsShapeType.SHAPE_SPHERE:
1152 shape = new SphereShape(0.5f);
1153 break;
1154
1155 }
1156 if (shape != null)
1157 {
1158 IndexedVector3 scaling = new IndexedVector3(pShapeData.Scale.X, pShapeData.Scale.Y, pShapeData.Scale.Z);
1159 shape.SetMargin(world.WorldSettings.Params.collisionMargin);
1160 shape.SetLocalScaling(ref scaling);
1161
1162 }
1163 return shape;
1164 }
1165 //PhysicsScene.World.ptr, false
1166 internal static object CreateCompoundShape2(object pWorld, bool enableDynamicAabbTree)
1167 {
1168 return new CompoundShape(enableDynamicAabbTree);
1169 }
1170
1171 internal static int GetNumberOfCompoundChildren2(object pCompoundShape)
1172 {
1173 var compoundshape = pCompoundShape as CompoundShape;
1174 return compoundshape.GetNumChildShapes();
1175 }
1176 //LinksetRoot.PhysShape.ptr, newShape.ptr, displacementPos, displacementRot
1177 internal static void AddChildShapeToCompoundShape2(object pCShape, object paddShape, Vector3 displacementPos, Quaternion displacementRot)
1178 {
1179 IndexedMatrix relativeTransform = new IndexedMatrix();
1180 var compoundshape = pCShape as CompoundShape;
1181 var addshape = paddShape as CollisionShape;
1182
1183 relativeTransform._origin = new IndexedVector3(displacementPos.X, displacementPos.Y, displacementPos.Z);
1184 relativeTransform.SetRotation(new IndexedQuaternion(displacementRot.X,displacementRot.Y,displacementRot.Z,displacementRot.W));
1185 compoundshape.AddChildShape(ref relativeTransform, addshape);
1186
1187 }
1188
1189 internal static object RemoveChildShapeFromCompoundShapeIndex2(object pCShape, int pii)
1190 {
1191 var compoundshape = pCShape as CompoundShape;
1192 CollisionShape ret = null;
1193 ret = compoundshape.GetChildShape(pii);
1194 compoundshape.RemoveChildShapeByIndex(pii);
1195 return ret;
1196 }
1197
1198 internal static object CreateGroundPlaneShape2(uint pLocalId, float pheight, float pcollisionMargin)
1199 {
1200 StaticPlaneShape m_planeshape = new StaticPlaneShape(new IndexedVector3(0,0,1),(int)pheight );
1201 m_planeshape.SetMargin(pcollisionMargin);
1202 m_planeshape.SetUserPointer(pLocalId);
1203 return m_planeshape;
1204 }
1205
1206 internal static object CreateHingeConstraint2(object pWorld, object pBody1, object ppBody2, Vector3 ppivotInA, Vector3 ppivotInB, Vector3 paxisInA, Vector3 paxisInB, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies)
1207 {
1208 HingeConstraint constrain = null;
1209 var rb1 = pBody1 as RigidBody;
1210 var rb2 = ppBody2 as RigidBody;
1211 if (rb1 != null && rb2 != null)
1212 {
1213 IndexedVector3 pivotInA = new IndexedVector3(ppivotInA.X, ppivotInA.Y, ppivotInA.Z);
1214 IndexedVector3 pivotInB = new IndexedVector3(ppivotInB.X, ppivotInB.Y, ppivotInB.Z);
1215 IndexedVector3 axisInA = new IndexedVector3(paxisInA.X, paxisInA.Y, paxisInA.Z);
1216 IndexedVector3 axisInB = new IndexedVector3(paxisInB.X, paxisInB.Y, paxisInB.Z);
1217 var world = pWorld as DiscreteDynamicsWorld;
1218 world.AddConstraint(constrain, pdisableCollisionsBetweenLinkedBodies);
1219 }
1220 return constrain;
1221 }
1222
1223 internal static bool ReleaseHeightMapInfo2(object pMapInfo)
1224 {
1225 if (pMapInfo != null)
1226 {
1227 BulletHeightMapInfo mapinfo = pMapInfo as BulletHeightMapInfo;
1228 if (mapinfo.heightMap != null)
1229 mapinfo.heightMap = null;
1230
1231
1232 }
1233 return true;
1234 }
1235
1236 internal static object CreateHullShape2(object pWorld, int pHullCount, float[] pConvHulls)
1237 {
1238 CompoundShape compoundshape = new CompoundShape(false);
1239 var world = pWorld as DiscreteDynamicsWorld;
1240
1241
1242 compoundshape.SetMargin(world.WorldSettings.Params.collisionMargin);
1243 int ii = 1;
1244
1245 for (int i = 0; i < pHullCount; i++)
1246 {
1247 int vertexCount = (int) pConvHulls[ii];
1248
1249 IndexedVector3 centroid = new IndexedVector3(pConvHulls[ii + 1], pConvHulls[ii + 2], pConvHulls[ii + 3]);
1250 IndexedMatrix childTrans = IndexedMatrix.Identity;
1251 childTrans._origin = centroid;
1252
1253 List<IndexedVector3> virts = new List<IndexedVector3>();
1254 int ender = ((ii + 4) + (vertexCount*3));
1255 for (int iii = ii + 4; iii < ender; iii+=3)
1256 {
1257
1258 virts.Add(new IndexedVector3(pConvHulls[iii], pConvHulls[iii + 1], pConvHulls[iii +2]));
1259 }
1260 ConvexHullShape convexShape = new ConvexHullShape(virts, vertexCount);
1261 convexShape.SetMargin(world.WorldSettings.Params.collisionMargin);
1262 compoundshape.AddChildShape(ref childTrans, convexShape);
1263 ii += (vertexCount*3 + 4);
1264 }
1265
1266
1267 return compoundshape;
1268 }
1269
1270 internal static object CreateMeshShape2(object pWorld, int pIndicesCount, int[] indices, int pVerticesCount, float[] verticesAsFloats)
1271 {
1272 //DumpRaw(indices,verticesAsFloats,pIndicesCount,pVerticesCount);
1273
1274 for (int iter = 0; iter < pVerticesCount; iter++)
1275 {
1276 if (verticesAsFloats[iter] > 0 && verticesAsFloats[iter] < 0.0001) verticesAsFloats[iter] = 0;
1277 if (verticesAsFloats[iter] < 0 && verticesAsFloats[iter] > -0.0001) verticesAsFloats[iter] = 0;
1278 }
1279
1280 ObjectArray<int> indicesarr = new ObjectArray<int>(indices);
1281 ObjectArray<float> vertices = new ObjectArray<float>(verticesAsFloats);
1282 DumpRaw(indicesarr,vertices,pIndicesCount,pVerticesCount);
1283 var world = pWorld as DiscreteDynamicsWorld;
1284 IndexedMesh mesh = new IndexedMesh();
1285 mesh.m_indexType = PHY_ScalarType.PHY_INTEGER;
1286 mesh.m_numTriangles = pIndicesCount/3;
1287 mesh.m_numVertices = pVerticesCount;
1288 mesh.m_triangleIndexBase = indicesarr;
1289 mesh.m_vertexBase = vertices;
1290 mesh.m_vertexStride = 3;
1291 mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT;
1292 mesh.m_triangleIndexStride = 3;
1293
1294 TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray();
1295 tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER);
1296 BvhTriangleMeshShape meshShape = new BvhTriangleMeshShape(tribuilder, true,true);
1297 meshShape.SetMargin(world.WorldSettings.Params.collisionMargin);
1298 // world.UpdateSingleAabb(meshShape);
1299 return meshShape;
1300
1301 }
1302 public static void DumpRaw(ObjectArray<int>indices, ObjectArray<float> vertices, int pIndicesCount,int pVerticesCount )
1303 {
1304
1305 String fileName = "objTest3.raw";
1306 String completePath = System.IO.Path.Combine(Util.configDir(), fileName);
1307 StreamWriter sw = new StreamWriter(completePath);
1308 IndexedMesh mesh = new IndexedMesh();
1309
1310 mesh.m_indexType = PHY_ScalarType.PHY_INTEGER;
1311 mesh.m_numTriangles = pIndicesCount / 3;
1312 mesh.m_numVertices = pVerticesCount;
1313 mesh.m_triangleIndexBase = indices;
1314 mesh.m_vertexBase = vertices;
1315 mesh.m_vertexStride = 3;
1316 mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT;
1317 mesh.m_triangleIndexStride = 3;
1318
1319 TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray();
1320 tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER);
1321
1322
1323
1324 for (int i = 0; i < pVerticesCount; i++)
1325 {
1326
1327 string s = vertices[indices[i * 3]].ToString("0.0000");
1328 s += " " + vertices[indices[i * 3 + 1]].ToString("0.0000");
1329 s += " " + vertices[indices[i * 3 + 2]].ToString("0.0000");
1330
1331 sw.Write(s + "\n");
1332 }
1333
1334 sw.Close();
1335 }
1336 public static void DumpRaw(int[] indices, float[] vertices, int pIndicesCount, int pVerticesCount)
1337 {
1338
1339 String fileName = "objTest6.raw";
1340 String completePath = System.IO.Path.Combine(Util.configDir(), fileName);
1341 StreamWriter sw = new StreamWriter(completePath);
1342 IndexedMesh mesh = new IndexedMesh();
1343
1344 mesh.m_indexType = PHY_ScalarType.PHY_INTEGER;
1345 mesh.m_numTriangles = pIndicesCount / 3;
1346 mesh.m_numVertices = pVerticesCount;
1347 mesh.m_triangleIndexBase = indices;
1348 mesh.m_vertexBase = vertices;
1349 mesh.m_vertexStride = 3;
1350 mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT;
1351 mesh.m_triangleIndexStride = 3;
1352
1353 TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray();
1354 tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER);
1355
1356
1357 sw.WriteLine("Indices");
1358 sw.WriteLine(string.Format("int[] indices = new int[{0}];",pIndicesCount));
1359 for (int iter = 0; iter < indices.Length; iter++)
1360 {
1361 sw.WriteLine(string.Format("indices[{0}]={1};",iter,indices[iter]));
1362 }
1363 sw.WriteLine("VerticesFloats");
1364 sw.WriteLine(string.Format("float[] vertices = new float[{0}];", pVerticesCount));
1365 for (int iter = 0; iter < vertices.Length; iter++)
1366 {
1367 sw.WriteLine(string.Format("Vertices[{0}]={1};", iter, vertices[iter].ToString("0.0000")));
1368 }
1369
1370 // for (int i = 0; i < pVerticesCount; i++)
1371 // {
1372 //
1373 // string s = vertices[indices[i * 3]].ToString("0.0000");
1374 // s += " " + vertices[indices[i * 3 + 1]].ToString("0.0000");
1375 // s += " " + vertices[indices[i * 3 + 2]].ToString("0.0000");
1376 //
1377 // sw.Write(s + "\n");
1378 //}
1379
1380 sw.Close();
1381 }
1382 //PhysicsScene.World.ptr, m_mapInfo.ID, m_mapInfo.minCoords, m_mapInfo.maxCoords, m_mapInfo.heightMap, PhysicsScene.Params.terrainCollisionMargin
1383 internal static object CreateHeightMapInfo2(object pWorld, uint pId, Vector3 pminCoords, Vector3 pmaxCoords, float[] pheightMap, float pCollisionMargin)
1384 {
1385 BulletHeightMapInfo mapInfo = new BulletHeightMapInfo(pId, pheightMap, null);
1386 mapInfo.heightMap = null;
1387 mapInfo.minCoords = pminCoords;
1388 mapInfo.maxCoords = pmaxCoords;
1389 mapInfo.sizeX = (int) (pmaxCoords.X - pminCoords.X);
1390 mapInfo.sizeY = (int) (pmaxCoords.Y - pminCoords.Y);
1391 mapInfo.ID = pId;
1392 mapInfo.minZ = pminCoords.Z;
1393 mapInfo.maxZ = pmaxCoords.Z;
1394 mapInfo.collisionMargin = pCollisionMargin;
1395 if (mapInfo.minZ == mapInfo.maxZ)
1396 mapInfo.minZ -= 0.2f;
1397 mapInfo.heightMap = pheightMap;
1398
1399 return mapInfo;
1400
1401 }
1402
1403 internal static object CreateTerrainShape2(object pMapInfo)
1404 {
1405 BulletHeightMapInfo mapinfo = pMapInfo as BulletHeightMapInfo;
1406 const int upAxis = 2;
1407 const float scaleFactor = 1.0f;
1408 HeightfieldTerrainShape terrainShape = new HeightfieldTerrainShape((int)mapinfo.sizeX, (int)mapinfo.sizeY,
1409 mapinfo.heightMap, scaleFactor,
1410 mapinfo.minZ, mapinfo.maxZ, upAxis,
1411 false);
1412 terrainShape.SetMargin(mapinfo.collisionMargin + 0.5f);
1413 terrainShape.SetUseDiamondSubdivision(true);
1414 terrainShape.SetUserPointer(mapinfo.ID);
1415 return terrainShape;
1416 }
1417
1418 internal static bool TranslationalLimitMotor2(object pConstraint, float ponOff, float targetVelocity, float maxMotorForce)
1419 {
1420 TypedConstraint tconstrain = pConstraint as TypedConstraint;
1421 bool onOff = ponOff != 0;
1422 bool ret = false;
1423
1424 switch (tconstrain.GetConstraintType())
1425 {
1426 case TypedConstraintType.D6_CONSTRAINT_TYPE:
1427 Generic6DofConstraint constrain = pConstraint as Generic6DofConstraint;
1428 constrain.GetTranslationalLimitMotor().m_enableMotor[0] = onOff;
1429 constrain.GetTranslationalLimitMotor().m_targetVelocity[0] = targetVelocity;
1430 constrain.GetTranslationalLimitMotor().m_maxMotorForce[0] = maxMotorForce;
1431 ret = true;
1432 break;
1433 }
1434
1435
1436 return ret;
1437
1438 }
1439
1440 internal static int PhysicsStep2(object pWorld, float timeStep, int m_maxSubSteps, float m_fixedTimeStep, out int updatedEntityCount, out List<BulletXNA.EntityProperties> updatedEntities, out int collidersCount, out List<BulletXNA.CollisionDesc>colliders)
1441 {
1442 int epic = PhysicsStepint2(pWorld, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out updatedEntities,
1443 out collidersCount, out colliders);
1444 return epic;
1445 }
1446
1447 private static int PhysicsStepint2(object pWorld,float timeStep, int m_maxSubSteps, float m_fixedTimeStep, out int updatedEntityCount, out List<BulletXNA.EntityProperties> updatedEntities, out int collidersCount, out List<BulletXNA.CollisionDesc> colliders)
1448 {
1449 int numSimSteps = 0;
1450
1451
1452 //if (updatedEntities is null)
1453 // updatedEntities = new List<BulletXNA.EntityProperties>();
1454
1455 //if (colliders is null)
1456 // colliders = new List<BulletXNA.CollisionDesc>();
1457
1458
1459 if (pWorld is DiscreteDynamicsWorld)
1460 {
1461 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
1462
1463 numSimSteps = world.StepSimulation(timeStep, m_maxSubSteps, m_fixedTimeStep);
1464 int updates = 0;
1465
1466 updatedEntityCount = world.UpdatedObjects.Count;
1467 updatedEntities = new List<BulletXNA.EntityProperties>(world.UpdatedObjects);
1468 updatedEntityCount = updatedEntities.Count;
1469 world.UpdatedObjects.Clear();
1470
1471
1472 collidersCount = world.UpdatedCollisions.Count;
1473 colliders = new List<BulletXNA.CollisionDesc>(world.UpdatedCollisions);
1474
1475 world.UpdatedCollisions.Clear();
1476 m_collisionsThisFrame = 0;
1477 int numManifolds = world.GetDispatcher().GetNumManifolds();
1478 for (int j = 0; j < numManifolds; j++)
1479 {
1480 PersistentManifold contactManifold = world.GetDispatcher().GetManifoldByIndexInternal(j);
1481 int numContacts = contactManifold.GetNumContacts();
1482 if (numContacts == 0)
1483 continue;
1484
1485 CollisionObject objA = contactManifold.GetBody0() as CollisionObject;
1486 CollisionObject objB = contactManifold.GetBody1() as CollisionObject;
1487
1488 ManifoldPoint manifoldPoint = contactManifold.GetContactPoint(0);
1489 IndexedVector3 contactPoint = manifoldPoint.GetPositionWorldOnB();
1490 IndexedVector3 contactNormal = -manifoldPoint.m_normalWorldOnB; // make relative to A
1491
1492 RecordCollision(world, objA, objB, contactPoint, contactNormal);
1493 m_collisionsThisFrame ++;
1494 if (m_collisionsThisFrame >= 9999999)
1495 break;
1496
1497
1498 }
1499
1500
1501 }
1502 else
1503 {
1504 //if (updatedEntities is null)
1505 updatedEntities = new List<BulletXNA.EntityProperties>();
1506 updatedEntityCount = 0;
1507 //if (colliders is null)
1508 colliders = new List<BulletXNA.CollisionDesc>();
1509 collidersCount = 0;
1510 }
1511 return numSimSteps;
1512 }
1513
1514 private static void RecordCollision(CollisionWorld world,CollisionObject objA, CollisionObject objB, IndexedVector3 contact, IndexedVector3 norm)
1515 {
1516
1517 IndexedVector3 contactNormal = norm;
1518 if ((objA.GetCollisionFlags() & BulletXNA.BulletCollision.CollisionFlags.BS_WANTS_COLLISIONS) == 0 &&
1519 (objB.GetCollisionFlags() & BulletXNA.BulletCollision.CollisionFlags.BS_WANTS_COLLISIONS) == 0)
1520 {
1521 return;
1522 }
1523 uint idA = (uint)objA.GetUserPointer();
1524 uint idB = (uint)objB.GetUserPointer();
1525 if (idA > idB)
1526 {
1527 uint temp = idA;
1528 idA = idB;
1529 idB = temp;
1530 contactNormal = -contactNormal;
1531 }
1532
1533 ulong collisionID = ((ulong) idA << 32) | idB;
1534
1535 BulletXNA.CollisionDesc cDesc = new BulletXNA.CollisionDesc()
1536 {
1537 aID = idA,
1538 bID = idB,
1539 point = contact,
1540 normal = contactNormal
1541 };
1542 world.UpdatedCollisions.Add(cDesc);
1543 m_collisionsThisFrame++;
1544
1545
1546 }
1547 private static EntityProperties GetDebugProperties(object pWorld, object pBody)
1548 {
1549 EntityProperties ent = new EntityProperties();
1550 DiscreteDynamicsWorld world = pWorld as DiscreteDynamicsWorld;
1551 RigidBody body = pBody as RigidBody;
1552 IndexedMatrix transform = body.GetWorldTransform();
1553 IndexedVector3 LinearVelocity = body.GetInterpolationLinearVelocity();
1554 IndexedVector3 AngularVelocity = body.GetInterpolationAngularVelocity();
1555 IndexedQuaternion rotation = transform.GetRotation();
1556 ent.Acceleration = Vector3.Zero;
1557 ent.ID = (uint)body.GetUserPointer();
1558 ent.Position = new Vector3(transform._origin.X,transform._origin.Y,transform._origin.Z);
1559 ent.Rotation = new Quaternion(rotation.X,rotation.Y,rotation.Z,rotation.W);
1560 ent.Velocity = new Vector3(LinearVelocity.X, LinearVelocity.Y, LinearVelocity.Z);
1561 ent.RotationalVelocity = new Vector3(AngularVelocity.X, AngularVelocity.Y, AngularVelocity.Z);
1562 return ent;
1563
1564
1565 }
1566
1567
1568 internal static Vector3 GetLocalScaling2(object pBody)
1569 {
1570 CollisionShape shape = pBody as CollisionShape;
1571 IndexedVector3 scale = shape.GetLocalScaling();
1572 return new Vector3(scale.X,scale.Y,scale.Z);
1573 }
1574
1575 internal static bool RayCastGround(object pWorld, Vector3 _RayOrigin, float pRayHeight, object NotMe)
1576 {
1577 DynamicsWorld world = pWorld as DynamicsWorld;
1578 if (world != null)
1579 {
1580 if (NotMe is CollisionObject || NotMe is RigidBody)
1581 {
1582 CollisionObject AvoidBody = NotMe as CollisionObject;
1583
1584 IndexedVector3 rOrigin = new IndexedVector3(_RayOrigin.X, _RayOrigin.Y, _RayOrigin.Z);
1585 IndexedVector3 rEnd = new IndexedVector3(_RayOrigin.X, _RayOrigin.Y, _RayOrigin.Z - pRayHeight);
1586 using (
1587 ClosestNotMeRayResultCallback rayCallback = new ClosestNotMeRayResultCallback(rOrigin,
1588 rEnd, AvoidBody)
1589 )
1590 {
1591 world.RayTest(ref rOrigin, ref rEnd, rayCallback);
1592 if (rayCallback.HasHit())
1593 {
1594 IndexedVector3 hitLocation = rayCallback.m_hitPointWorld;
1595
1596 }
1597 return rayCallback.HasHit();
1598 }
1599 }
1600 }
1601 return false;
1602 }
1603}
1604}
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BulletSimData.cs b/OpenSim/Region/Physics/BulletSNPlugin/BulletSimData.cs
new file mode 100644
index 0000000..a1ed8d8
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSNPlugin/BulletSimData.cs
@@ -0,0 +1,280 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using OMV = OpenMetaverse;
31
32namespace OpenSim.Region.Physics.BulletSNPlugin
33{
34// Classes to allow some type checking for the API
35// These hold pointers to allocated objects in the unmanaged space.
36
37// The physics engine controller class created at initialization
38public struct BulletSim
39{
40 public BulletSim(uint worldId, BSScene bss, object xx)
41 {
42 ptr = xx;
43 worldID = worldId;
44 physicsScene = bss;
45 }
46 public object ptr;
47 public uint worldID;
48 // The scene is only in here so very low level routines have a handle to print debug/error messages
49 public BSScene physicsScene;
50}
51
52// An allocated Bullet btRigidBody
53public struct BulletBody
54{
55 public BulletBody(uint id) : this(id, null)
56 {
57 }
58 public BulletBody(uint id, object xx)
59 {
60 ID = id;
61 ptr = xx;
62 collisionType = CollisionType.Static;
63 }
64 public object ptr;
65 public uint ID;
66 public CollisionType collisionType;
67
68 public void Clear()
69 {
70 ptr = null;
71 }
72 public bool HasPhysicalBody { get { return ptr != null; } }
73
74 // Apply the specificed collision mask into the physical world
75 public void ApplyCollisionMask()
76 {
77 // Should assert the body has been added to the physical world.
78 // (The collision masks are stored in the collision proxy cache which only exists for
79 // a collision body that is in the world.)
80 BulletSimAPI.SetCollisionGroupMask2(ptr,
81 BulletSimData.CollisionTypeMasks[collisionType].group,
82 BulletSimData.CollisionTypeMasks[collisionType].mask);
83 }
84
85 public override string ToString()
86 {
87 StringBuilder buff = new StringBuilder();
88 buff.Append("<id=");
89 buff.Append(ID.ToString());
90 buff.Append(",p=");
91 buff.Append(ptr.ToString());
92 buff.Append(",c=");
93 buff.Append(collisionType);
94 buff.Append(">");
95 return buff.ToString();
96 }
97}
98
99public struct BulletShape
100{
101 public BulletShape(object xx) : this(xx, BSPhysicsShapeType.SHAPE_UNKNOWN)
102 {
103 }
104 public BulletShape(object xx, BSPhysicsShapeType typ)
105 {
106 ptr = xx;
107 type = typ;
108 shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE;
109 isNativeShape = false;
110 }
111 public object ptr;
112 public BSPhysicsShapeType type;
113 public System.UInt64 shapeKey;
114 public bool isNativeShape;
115
116 public void Clear()
117 {
118 ptr = null;
119 }
120 public bool HasPhysicalShape { get { return ptr != null; } }
121
122 public override string ToString()
123 {
124 StringBuilder buff = new StringBuilder();
125 buff.Append("<p=");
126 buff.Append(ptr.ToString());
127 buff.Append(",s=");
128 buff.Append(type.ToString());
129 buff.Append(",k=");
130 buff.Append(shapeKey.ToString("X"));
131 buff.Append(",n=");
132 buff.Append(isNativeShape.ToString());
133 buff.Append(">");
134 return buff.ToString();
135 }
136}
137
138// An allocated Bullet btConstraint
139public struct BulletConstraint
140{
141 public BulletConstraint(object xx)
142 {
143 ptr = xx;
144 }
145 public object ptr;
146
147 public void Clear()
148 {
149 ptr = null;
150 }
151 public bool HasPhysicalConstraint { get { return ptr != null; } }
152}
153
154// An allocated HeightMapThing which holds various heightmap info.
155// Made a class rather than a struct so there would be only one
156// instance of this and C# will pass around pointers rather
157// than making copies.
158public class BulletHeightMapInfo
159{
160 public BulletHeightMapInfo(uint id, float[] hm, object xx) {
161 ID = id;
162 Ptr = xx;
163 heightMap = hm;
164 terrainRegionBase = OMV.Vector3.Zero;
165 minCoords = new OMV.Vector3(100f, 100f, 25f);
166 maxCoords = new OMV.Vector3(101f, 101f, 26f);
167 minZ = maxZ = 0f;
168 sizeX = sizeY = 256f;
169 }
170 public uint ID;
171 public object Ptr;
172 public float[] heightMap;
173 public OMV.Vector3 terrainRegionBase;
174 public OMV.Vector3 minCoords;
175 public OMV.Vector3 maxCoords;
176 public float sizeX, sizeY;
177 public float minZ, maxZ;
178 public BulletShape terrainShape;
179 public BulletBody terrainBody;
180
181 public float collisionMargin { get; set; }
182}
183
184// The general class of collsion object.
185public enum CollisionType
186{
187 Avatar,
188 Groundplane,
189 Terrain,
190 Static,
191 Dynamic,
192 VolumeDetect,
193 // Linkset, // A linkset should be either Static or Dynamic
194 LinksetChild,
195 Unknown
196};
197
198// Hold specification of group and mask collision flags for a CollisionType
199public struct CollisionTypeFilterGroup
200{
201 public CollisionTypeFilterGroup(CollisionType t, uint g, uint m)
202 {
203 type = t;
204 group = g;
205 mask = m;
206 }
207 public CollisionType type;
208 public uint group;
209 public uint mask;
210};
211
212 /* NOTE: old definitions kept for reference. Delete when things are working.
213 // The collsion filters and masked are defined in one place -- don't want them scattered
214 AvatarGroup = BCharacterGroup,
215 AvatarMask = BAllGroup,
216 ObjectGroup = BSolidGroup,
217 ObjectMask = BAllGroup,
218 StaticObjectGroup = BStaticGroup,
219 StaticObjectMask = AvatarGroup | ObjectGroup, // static things don't interact with much
220 LinksetGroup = BLinksetGroup,
221 LinksetMask = BAllGroup,
222 LinksetChildGroup = BLinksetChildGroup,
223 LinksetChildMask = BNoneGroup, // Linkset children disappear from the world
224 VolumeDetectGroup = BSensorTrigger,
225 VolumeDetectMask = ~BSensorTrigger,
226 TerrainGroup = BTerrainGroup,
227 TerrainMask = BAllGroup & ~BStaticGroup, // static objects on the ground don't collide
228 GroundPlaneGroup = BGroundPlaneGroup,
229 GroundPlaneMask = BAllGroup
230 */
231
232public static class BulletSimData
233{
234
235// Map of collisionTypes to flags for collision groups and masks.
236// As mentioned above, don't use the CollisionFilterGroups definitions directly in the code
237// but, instead, use references to this dictionary. Finding and debugging
238// collision flag problems will be made easier.
239public static Dictionary<CollisionType, CollisionTypeFilterGroup> CollisionTypeMasks
240 = new Dictionary<CollisionType, CollisionTypeFilterGroup>()
241{
242 { CollisionType.Avatar,
243 new CollisionTypeFilterGroup(CollisionType.Avatar,
244 (uint)CollisionFilterGroups.BCharacterGroup,
245 (uint)CollisionFilterGroups.BAllGroup)
246 },
247 { CollisionType.Groundplane,
248 new CollisionTypeFilterGroup(CollisionType.Groundplane,
249 (uint)CollisionFilterGroups.BGroundPlaneGroup,
250 (uint)CollisionFilterGroups.BAllGroup)
251 },
252 { CollisionType.Terrain,
253 new CollisionTypeFilterGroup(CollisionType.Terrain,
254 (uint)CollisionFilterGroups.BTerrainGroup,
255 (uint)(CollisionFilterGroups.BAllGroup & ~CollisionFilterGroups.BStaticGroup))
256 },
257 { CollisionType.Static,
258 new CollisionTypeFilterGroup(CollisionType.Static,
259 (uint)CollisionFilterGroups.BStaticGroup,
260 (uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup))
261 },
262 { CollisionType.Dynamic,
263 new CollisionTypeFilterGroup(CollisionType.Dynamic,
264 (uint)CollisionFilterGroups.BSolidGroup,
265 (uint)(CollisionFilterGroups.BAllGroup))
266 },
267 { CollisionType.VolumeDetect,
268 new CollisionTypeFilterGroup(CollisionType.VolumeDetect,
269 (uint)CollisionFilterGroups.BSensorTrigger,
270 (uint)(~CollisionFilterGroups.BSensorTrigger))
271 },
272 { CollisionType.LinksetChild,
273 new CollisionTypeFilterGroup(CollisionType.LinksetChild,
274 (uint)CollisionFilterGroups.BTerrainGroup,
275 (uint)(CollisionFilterGroups.BNoneGroup))
276 },
277};
278
279}
280}
diff --git a/bin/BulletXNA.dll b/bin/BulletXNA.dll
new file mode 100644
index 0000000..1e3f042
--- /dev/null
+++ b/bin/BulletXNA.dll
Binary files differ
diff --git a/bin/BulletXNA.pdb b/bin/BulletXNA.pdb
new file mode 100644
index 0000000..4b83b83
--- /dev/null
+++ b/bin/BulletXNA.pdb
Binary files differ
diff --git a/prebuild.xml b/prebuild.xml
index 14ffc99..741b660 100644
--- a/prebuild.xml
+++ b/prebuild.xml
@@ -1756,6 +1756,40 @@
1756 </Files> 1756 </Files>
1757 </Project> 1757 </Project>
1758 1758
1759 <Project frameworkVersion="v3_5" name="OpenSim.Region.Physics.BulletSNPlugin" path="OpenSim/Region/Physics/BulletSNPlugin" type="Library">
1760 <Configuration name="Debug">
1761 <Options>
1762 <OutputPath>../../../../bin/Physics/</OutputPath>
1763 </Options>
1764 </Configuration>
1765 <Configuration name="Release">
1766 <Options>
1767 <OutputPath>../../../../bin/Physics/</OutputPath>
1768 </Options>
1769 </Configuration>
1770
1771 <ReferencePath>../../../../bin/</ReferencePath>
1772 <Reference name="System"/>
1773 <Reference name="System.Core"/>
1774 <Reference name="System.Xml"/>
1775 <Reference name="OpenMetaverseTypes" path="../../../../bin/"/>
1776 <Reference name="Nini.dll" path="../../../../bin/"/>
1777 <Reference name="OpenSim.Framework"/>
1778 <Reference name="OpenSim.Region.Framework"/>
1779 <Reference name="OpenSim.Region.CoreModules"/>
1780 <Reference name="OpenSim.Framework.Console"/>
1781 <Reference name="OpenSim.Region.Physics.Manager"/>
1782 <Reference name="OpenSim.Region.Physics.ConvexDecompositionDotNet"/>
1783 <Reference name="BulletXNA.dll" path="../../../../bin/"/>
1784 <Reference name="log4net.dll" path="../../../../bin/"/>
1785
1786 <Files>
1787 <Match pattern="*.cs" recurse="true">
1788 <Exclude name="Tests" pattern="Tests"/>
1789 </Match>
1790 </Files>
1791 </Project>
1792
1759 <!-- OpenSim app --> 1793 <!-- OpenSim app -->
1760 <Project frameworkVersion="v3_5" name="OpenSim" path="OpenSim/Region/Application" type="Exe"> 1794 <Project frameworkVersion="v3_5" name="OpenSim" path="OpenSim/Region/Application" type="Exe">
1761 <Configuration name="Debug"> 1795 <Configuration name="Debug">