From 302d72701da35b6d481a538c4cb953976d7e9044 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Mon, 20 Jun 2011 17:14:59 -0700 Subject: BulletSim initial checkin --- .../Region/Physics/BulletSPlugin/BSCharacter.cs | 426 +++++++ OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | 951 ++++++++++++++++ OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs | 68 ++ OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | 1192 ++++++++++++++++++++ OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | 553 +++++++++ .../Region/Physics/BulletSPlugin/BulletSimAPI.cs | 186 +++ 6 files changed, 3376 insertions(+) create mode 100644 OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs create mode 100644 OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs create mode 100644 OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs create mode 100644 OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs create mode 100644 OpenSim/Region/Physics/BulletSPlugin/BSScene.cs create mode 100644 OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs (limited to 'OpenSim/Region/Physics/BulletSPlugin') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs new file mode 100644 index 0000000..d9e236f --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -0,0 +1,426 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Reflection; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSCharacter : PhysicsActor +{ + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[BULLETS CHAR]"; + + private BSScene _scene; + private String _avName; + private bool _stopped; + private Vector3 _size; + private Vector3 _scale; + private PrimitiveBaseShape _pbs; + private uint _localID = 0; + private bool _grabbed; + private bool _selected; + private Vector3 _position; + private float _mass = 80f; + public float _density = 60f; + public float CAPSULE_RADIUS = 0.37f; + public float CAPSULE_LENGTH = 2.140599f; + private Vector3 _force; + private Vector3 _velocity; + private Vector3 _torque; + private float _collisionScore; + private Vector3 _acceleration; + private Quaternion _orientation; + private int _physicsActorType; + private bool _isPhysical; + private bool _flying; + private bool _setAlwaysRun; + private bool _throttleUpdates; + private bool _isColliding; + private long _collidingStep; + private bool _collidingGround; + private long _collidingGroundStep; + private bool _collidingObj; + private bool _floatOnWater; + private Vector3 _rotationalVelocity; + private bool _kinematic; + private float _buoyancy; + + private int _subscribedEventsMs = 0; + private int _lastCollisionTime = 0; + + private Vector3 _PIDTarget; + private bool _usePID; + private float _PIDTau; + private bool _useHoverPID; + private float _PIDHoverHeight; + private PIDHoverType _PIDHoverType; + private float _PIDHoverTao; + + public BSCharacter(uint localID, String avName, BSScene parent_scene, Vector3 pos, Vector3 size, bool isFlying) + { + _localID = localID; + _avName = avName; + _scene = parent_scene; + _position = pos; + _size = size; + _orientation = Quaternion.Identity; + _velocity = Vector3.Zero; + _buoyancy = 0f; // characters return a buoyancy of zero + _scale = new Vector3(1f, 1f, 1f); + float AVvolume = (float) (Math.PI*Math.Pow(CAPSULE_RADIUS, 2)*CAPSULE_LENGTH); + _mass = _density*AVvolume; + + ShapeData shapeData = new ShapeData(); + shapeData.ID = _localID; + shapeData.Type = ShapeData.PhysicsShapeType.SHAPE_AVATAR; + shapeData.Position = _position; + shapeData.Rotation = _orientation; + shapeData.Velocity = _velocity; + shapeData.Scale = _scale; + shapeData.Mass = _mass; + shapeData.Buoyancy = isFlying ? 0f : 1f; + shapeData.Static = ShapeData.numericFalse; + + // do actual create at taint time + _scene.TaintedObject(delegate() + { + BulletSimAPI.CreateObject(parent_scene.WorldID, shapeData); + }); + + return; + } + + // called when this character is being destroyed and the resources should be released + public void Destroy() + { + _scene.TaintedObject(delegate() + { + BulletSimAPI.DestroyObject(_scene.WorldID, _localID); + }); + } + + public override void RequestPhysicsterseUpdate() + { + base.RequestPhysicsterseUpdate(); + } + + public override bool Stopped { + get { return _stopped; } + } + public override Vector3 Size { + get { return _size; } + set { _size = value; + } + } + public override PrimitiveBaseShape Shape { + set { _pbs = value; + } + } + public override uint LocalID { + set { _localID = value; + } + get { return _localID; } + } + public override bool Grabbed { + set { _grabbed = value; + } + } + public override bool Selected { + set { _selected = value; + } + } + public override void CrossingFailure() { return; } + public override void link(PhysicsActor obj) { return; } + public override void delink() { return; } + public override void LockAngularMotion(Vector3 axis) { return; } + + public override Vector3 Position { + get { + // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); + return _position; + } + set { + _position = value; + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); + }); + } + } + public override float Mass { + get { + return _mass; + } + } + public override Vector3 Force { + get { return _force; } + set { + _force = value; + m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force); + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); + }); + } + } + + public override int VehicleType { + get { return 0; } + set { return; } + } + public override void VehicleFloatParam(int param, float value) { } + public override void VehicleVectorParam(int param, Vector3 value) {} + public override void VehicleRotationParam(int param, Quaternion rotation) { } + public override void VehicleFlags(int param, bool remove) { } + + // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more + public override void SetVolumeDetect(int param) { return; } + + public override Vector3 GeometricCenter { get { return Vector3.Zero; } } + public override Vector3 CenterOfMass { get { return Vector3.Zero; } } + public override Vector3 Velocity { + get { return _velocity; } + set { + _velocity = value; + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectVelocity(_scene.WorldID, _localID, _velocity); + }); + } + } + public override Vector3 Torque { + get { return _torque; } + set { _torque = value; + } + } + public override float CollisionScore { + get { return _collisionScore; } + set { _collisionScore = value; + } + } + public override Vector3 Acceleration { + get { return _acceleration; } + } + public override Quaternion Orientation { + get { return _orientation; } + set { + _orientation = value; + _scene.TaintedObject(delegate() + { + // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); + BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); + }); + } + } + public override int PhysicsActorType { + get { return _physicsActorType; } + set { _physicsActorType = value; + } + } + public override bool IsPhysical { + get { return _isPhysical; } + set { _isPhysical = value; + } + } + public override bool Flying { + get { return _flying; } + set { + _flying = value; + _scene.TaintedObject(delegate() + { + // simulate flying by changing the effect of gravity + BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, LocalID, _flying ? 0f : 1f); + }); + } + } + public override bool + SetAlwaysRun { + get { return _setAlwaysRun; } + set { _setAlwaysRun = value; } + } + public override bool ThrottleUpdates { + get { return _throttleUpdates; } + set { _throttleUpdates = value; } + } + public override bool IsColliding { + get { return (_collidingStep == _scene.SimulationStep); } + set { _isColliding = value; } + } + public override bool CollidingGround { + get { return (_collidingGroundStep == _scene.SimulationStep); } + set { _collidingGround = value; } + } + public override bool CollidingObj { + get { return _collidingObj; } + set { _collidingObj = value; } + } + public override bool FloatOnWater { + set { _floatOnWater = value; } + } + public override Vector3 RotationalVelocity { + get { return _rotationalVelocity; } + set { _rotationalVelocity = value; } + } + public override bool Kinematic { + get { return _kinematic; } + set { _kinematic = value; } + } + public override float Buoyancy { + get { return _buoyancy; } + set { _buoyancy = value; } + } + + // Used for MoveTo + public override Vector3 PIDTarget { + set { _PIDTarget = value; } + } + public override bool PIDActive { + set { _usePID = value; } + } + public override float PIDTau { + set { _PIDTau = value; } + } + + // Used for llSetHoverHeight and maybe vehicle height + // Hover Height will override MoveTo target's Z + public override bool PIDHoverActive { + set { _useHoverPID = value; } + } + public override float PIDHoverHeight { + set { _PIDHoverHeight = value; } + } + public override PIDHoverType PIDHoverType { + set { _PIDHoverType = value; } + } + public override float PIDHoverTau { + set { _PIDHoverTao = value; } + } + + // For RotLookAt + public override Quaternion APIDTarget { set { return; } } + public override bool APIDActive { set { return; } } + public override float APIDStrength { set { return; } } + public override float APIDDamping { set { return; } } + + public override void AddForce(Vector3 force, bool pushforce) { + if (force.IsFinite()) + { + _force.X += force.X; + _force.Y += force.Y; + _force.Z += force.Z; + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); + }); + } + else + { + m_log.WarnFormat("{0}: Got a NaN force applied to a Character", LogHeader); + } + //m_lastUpdateSent = false; + } + public override void AddAngularForce(Vector3 force, bool pushforce) { + } + public override void SetMomentum(Vector3 momentum) { + } + public override void SubscribeEvents(int ms) { + _subscribedEventsMs = ms; + _lastCollisionTime = Util.EnvironmentTickCount() - _subscribedEventsMs; // make first collision happen + } + public override void UnSubscribeEvents() { + _subscribedEventsMs = 0; + } + public override bool SubscribedEvents() { + return (_subscribedEventsMs > 0); + } + + // The physics engine says that properties have updated. Update same and inform + // the world that things have changed. + public void UpdateProperties(EntityProperties entprop) + { + bool changed = false; + // we assign to the local variables so the normal set action does not happen + if (_position != entprop.Position) + { + _position = entprop.Position; + changed = true; + } + if (_orientation != entprop.Rotation) + { + _orientation = entprop.Rotation; + changed = true; + } + if (_velocity != entprop.Velocity) + { + _velocity = entprop.Velocity; + changed = true; + } + if (_acceleration != entprop.Acceleration) + { + _acceleration = entprop.Acceleration; + changed = true; + } + if (_rotationalVelocity != entprop.AngularVelocity) + { + _rotationalVelocity = entprop.AngularVelocity; + changed = true; + } + if (changed) + { + // base.RequestPhysicsterseUpdate(); + } + } + + public void Collide(uint collidingWith, ActorTypes type, Vector3 contactPoint, Vector3 contactNormal, float pentrationDepth) + { + // m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith); + + // The following makes IsColliding() and IsCollidingGround() work + _collidingStep = _scene.SimulationStep; + if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID) + { + _collidingGroundStep = _scene.SimulationStep; + } + + if (_subscribedEventsMs == 0) return; // don't want collisions + int nowTime = Util.EnvironmentTickCount(); + if (nowTime < (_lastCollisionTime + _subscribedEventsMs)) return; + _lastCollisionTime = nowTime; + + Dictionary contactPoints = new Dictionary(); + contactPoints.Add(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); + CollisionEventUpdate args = new CollisionEventUpdate(LocalID, (int)type, 1, contactPoints); + base.SendCollisionUpdate(args); + } + +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs new file mode 100644 index 0000000..046726d --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -0,0 +1,951 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* RA: June 14, 2011. Copied from ODEDynamics.cs and converted to + * call the BulletSim system. + */ +/* Revised Aug, Sept 2009 by Kitto Flora. ODEDynamics.cs replaces + * ODEVehicleSettings.cs. It and ODEPrim.cs are re-organised: + * ODEPrim.cs contains methods dealing with Prim editing, Prim + * characteristics and Kinetic motion. + * ODEDynamics.cs contains methods dealing with Prim Physical motion + * (dynamics) and the associated settings. Old Linear and angular + * motors for dynamic motion have been replace with MoveLinear() + * and MoveAngular(); 'Physical' is used only to switch ODE dynamic + * simualtion on/off; VEHICAL_TYPE_NONE/VEHICAL_TYPE_ is to + * switch between 'VEHICLE' parameter use and general dynamics + * settings use. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + public class BSDynamics + { + private int frcount = 0; // Used to limit dynamics debug output to + // every 100th frame + + // private BSScene m_parentScene = null; + private BSPrim m_prim; // the prim this dynamic controller belongs to + + // Vehicle properties + private Vehicle m_type = Vehicle.TYPE_NONE; // If a 'VEHICLE', and what kind + public Vehicle Type + { + get { return m_type; } + } + // private Quaternion m_referenceFrame = Quaternion.Identity; // Axis modifier + private VehicleFlag m_flags = (VehicleFlag) 0; // Boolean settings: + // HOVER_TERRAIN_ONLY + // HOVER_GLOBAL_HEIGHT + // NO_DEFLECTION_UP + // HOVER_WATER_ONLY + // HOVER_UP_ONLY + // LIMIT_MOTOR_UP + // LIMIT_ROLL_ONLY + private VehicleFlag m_Hoverflags = (VehicleFlag)0; + private Vector3 m_BlockingEndPoint = Vector3.Zero; + private Quaternion m_RollreferenceFrame = Quaternion.Identity; + // Linear properties + private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time + private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL + private Vector3 m_dir = Vector3.Zero; // velocity applied to body + private Vector3 m_linearFrictionTimescale = Vector3.Zero; + private float m_linearMotorDecayTimescale = 0; + private float m_linearMotorTimescale = 0; + private Vector3 m_lastLinearVelocityVector = Vector3.Zero; + private Vector3 m_lastPositionVector = Vector3.Zero; + // private bool m_LinearMotorSetLastFrame = false; + // private Vector3 m_linearMotorOffset = Vector3.Zero; + + //Angular properties + private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor + private int m_angularMotorApply = 0; // application frame counter + private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity + private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate + private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate + private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate + private Vector3 m_lastAngularVelocity = Vector3.Zero; // what was last applied to body + // private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body + + //Deflection properties + // private float m_angularDeflectionEfficiency = 0; + // private float m_angularDeflectionTimescale = 0; + // private float m_linearDeflectionEfficiency = 0; + // private float m_linearDeflectionTimescale = 0; + + //Banking properties + // private float m_bankingEfficiency = 0; + // private float m_bankingMix = 0; + // private float m_bankingTimescale = 0; + + //Hover and Buoyancy properties + private float m_VhoverHeight = 0f; +// private float m_VhoverEfficiency = 0f; + private float m_VhoverTimescale = 0f; + private float m_VhoverTargetHeight = -1.0f; // if <0 then no hover, else its the current target height + private float m_VehicleBuoyancy = 0f; //KF: m_VehicleBuoyancy is set by VEHICLE_BUOYANCY for a vehicle. + // Modifies gravity. Slider between -1 (double-gravity) and 1 (full anti-gravity) + // KF: So far I have found no good method to combine a script-requested .Z velocity and gravity. + // Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity. + + //Attractor properties + private float m_verticalAttractionEfficiency = 1.0f; // damped + private float m_verticalAttractionTimescale = 500f; // Timescale > 300 means no vert attractor. + + public BSDynamics(BSPrim myPrim) + { + m_prim = myPrim; + m_type = Vehicle.TYPE_NONE; + } + + internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue) + { + switch (pParam) + { + case Vehicle.ANGULAR_DEFLECTION_EFFICIENCY: + if (pValue < 0.01f) pValue = 0.01f; + // m_angularDeflectionEfficiency = pValue; + break; + case Vehicle.ANGULAR_DEFLECTION_TIMESCALE: + if (pValue < 0.01f) pValue = 0.01f; + // m_angularDeflectionTimescale = pValue; + break; + case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE: + if (pValue < 0.01f) pValue = 0.01f; + m_angularMotorDecayTimescale = pValue; + break; + case Vehicle.ANGULAR_MOTOR_TIMESCALE: + if (pValue < 0.01f) pValue = 0.01f; + m_angularMotorTimescale = pValue; + break; + case Vehicle.BANKING_EFFICIENCY: + if (pValue < 0.01f) pValue = 0.01f; + // m_bankingEfficiency = pValue; + break; + case Vehicle.BANKING_MIX: + if (pValue < 0.01f) pValue = 0.01f; + // m_bankingMix = pValue; + break; + case Vehicle.BANKING_TIMESCALE: + if (pValue < 0.01f) pValue = 0.01f; + // m_bankingTimescale = pValue; + break; + case Vehicle.BUOYANCY: + if (pValue < -1f) pValue = -1f; + if (pValue > 1f) pValue = 1f; + m_VehicleBuoyancy = pValue; + break; +// case Vehicle.HOVER_EFFICIENCY: +// if (pValue < 0f) pValue = 0f; +// if (pValue > 1f) pValue = 1f; +// m_VhoverEfficiency = pValue; +// break; + case Vehicle.HOVER_HEIGHT: + m_VhoverHeight = pValue; + break; + case Vehicle.HOVER_TIMESCALE: + if (pValue < 0.01f) pValue = 0.01f; + m_VhoverTimescale = pValue; + break; + case Vehicle.LINEAR_DEFLECTION_EFFICIENCY: + if (pValue < 0.01f) pValue = 0.01f; + // m_linearDeflectionEfficiency = pValue; + break; + case Vehicle.LINEAR_DEFLECTION_TIMESCALE: + if (pValue < 0.01f) pValue = 0.01f; + // m_linearDeflectionTimescale = pValue; + break; + case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE: + if (pValue < 0.01f) pValue = 0.01f; + m_linearMotorDecayTimescale = pValue; + break; + case Vehicle.LINEAR_MOTOR_TIMESCALE: + if (pValue < 0.01f) pValue = 0.01f; + m_linearMotorTimescale = pValue; + break; + case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY: + if (pValue < 0.1f) pValue = 0.1f; // Less goes unstable + if (pValue > 1.0f) pValue = 1.0f; + m_verticalAttractionEfficiency = pValue; + break; + case Vehicle.VERTICAL_ATTRACTION_TIMESCALE: + if (pValue < 0.01f) pValue = 0.01f; + m_verticalAttractionTimescale = pValue; + break; + + // These are vector properties but the engine lets you use a single float value to + // set all of the components to the same value + case Vehicle.ANGULAR_FRICTION_TIMESCALE: + m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue); + break; + case Vehicle.ANGULAR_MOTOR_DIRECTION: + m_angularMotorDirection = new Vector3(pValue, pValue, pValue); + m_angularMotorApply = 10; + break; + case Vehicle.LINEAR_FRICTION_TIMESCALE: + m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue); + break; + case Vehicle.LINEAR_MOTOR_DIRECTION: + m_linearMotorDirection = new Vector3(pValue, pValue, pValue); + m_linearMotorDirectionLASTSET = new Vector3(pValue, pValue, pValue); + break; + case Vehicle.LINEAR_MOTOR_OFFSET: + // m_linearMotorOffset = new Vector3(pValue, pValue, pValue); + break; + + } + }//end ProcessFloatVehicleParam + + internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue) + { + switch (pParam) + { + case Vehicle.ANGULAR_FRICTION_TIMESCALE: + m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); + break; + case Vehicle.ANGULAR_MOTOR_DIRECTION: + m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); + // Limit requested angular speed to 2 rps= 4 pi rads/sec + if (m_angularMotorDirection.X > 12.56f) m_angularMotorDirection.X = 12.56f; + if (m_angularMotorDirection.X < - 12.56f) m_angularMotorDirection.X = - 12.56f; + if (m_angularMotorDirection.Y > 12.56f) m_angularMotorDirection.Y = 12.56f; + if (m_angularMotorDirection.Y < - 12.56f) m_angularMotorDirection.Y = - 12.56f; + if (m_angularMotorDirection.Z > 12.56f) m_angularMotorDirection.Z = 12.56f; + if (m_angularMotorDirection.Z < - 12.56f) m_angularMotorDirection.Z = - 12.56f; + m_angularMotorApply = 10; + break; + case Vehicle.LINEAR_FRICTION_TIMESCALE: + m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); + break; + case Vehicle.LINEAR_MOTOR_DIRECTION: + m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); + m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z); + break; + case Vehicle.LINEAR_MOTOR_OFFSET: + // m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z); + break; + case Vehicle.BLOCK_EXIT: + m_BlockingEndPoint = new Vector3(pValue.X, pValue.Y, pValue.Z); + break; + } + }//end ProcessVectorVehicleParam + + internal void ProcessRotationVehicleParam(Vehicle pParam, Quaternion pValue) + { + switch (pParam) + { + case Vehicle.REFERENCE_FRAME: + // m_referenceFrame = pValue; + break; + case Vehicle.ROLL_FRAME: + m_RollreferenceFrame = pValue; + break; + } + }//end ProcessRotationVehicleParam + + internal void ProcessVehicleFlags(int pParam, bool remove) + { + if (remove) + { + if (pParam == -1) + { + m_flags = (VehicleFlag)0; + m_Hoverflags = (VehicleFlag)0; + return; + } + if ((pParam & (int)VehicleFlag.HOVER_GLOBAL_HEIGHT) == (int)VehicleFlag.HOVER_GLOBAL_HEIGHT) + { + if ((m_Hoverflags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != (VehicleFlag)0) + m_Hoverflags &= ~(VehicleFlag.HOVER_GLOBAL_HEIGHT); + } + if ((pParam & (int)VehicleFlag.HOVER_TERRAIN_ONLY) == (int)VehicleFlag.HOVER_TERRAIN_ONLY) + { + if ((m_Hoverflags & VehicleFlag.HOVER_TERRAIN_ONLY) != (VehicleFlag)0) + m_Hoverflags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY); + } + if ((pParam & (int)VehicleFlag.HOVER_UP_ONLY) == (int)VehicleFlag.HOVER_UP_ONLY) + { + if ((m_Hoverflags & VehicleFlag.HOVER_UP_ONLY) != (VehicleFlag)0) + m_Hoverflags &= ~(VehicleFlag.HOVER_UP_ONLY); + } + if ((pParam & (int)VehicleFlag.HOVER_WATER_ONLY) == (int)VehicleFlag.HOVER_WATER_ONLY) + { + if ((m_Hoverflags & VehicleFlag.HOVER_WATER_ONLY) != (VehicleFlag)0) + m_Hoverflags &= ~(VehicleFlag.HOVER_WATER_ONLY); + } + if ((pParam & (int)VehicleFlag.LIMIT_MOTOR_UP) == (int)VehicleFlag.LIMIT_MOTOR_UP) + { + if ((m_flags & VehicleFlag.LIMIT_MOTOR_UP) != (VehicleFlag)0) + m_flags &= ~(VehicleFlag.LIMIT_MOTOR_UP); + } + if ((pParam & (int)VehicleFlag.LIMIT_ROLL_ONLY) == (int)VehicleFlag.LIMIT_ROLL_ONLY) + { + if ((m_flags & VehicleFlag.LIMIT_ROLL_ONLY) != (VehicleFlag)0) + m_flags &= ~(VehicleFlag.LIMIT_ROLL_ONLY); + } + if ((pParam & (int)VehicleFlag.MOUSELOOK_BANK) == (int)VehicleFlag.MOUSELOOK_BANK) + { + if ((m_flags & VehicleFlag.MOUSELOOK_BANK) != (VehicleFlag)0) + m_flags &= ~(VehicleFlag.MOUSELOOK_BANK); + } + if ((pParam & (int)VehicleFlag.MOUSELOOK_STEER) == (int)VehicleFlag.MOUSELOOK_STEER) + { + if ((m_flags & VehicleFlag.MOUSELOOK_STEER) != (VehicleFlag)0) + m_flags &= ~(VehicleFlag.MOUSELOOK_STEER); + } + if ((pParam & (int)VehicleFlag.NO_DEFLECTION_UP) == (int)VehicleFlag.NO_DEFLECTION_UP) + { + if ((m_flags & VehicleFlag.NO_DEFLECTION_UP) != (VehicleFlag)0) + m_flags &= ~(VehicleFlag.NO_DEFLECTION_UP); + } + if ((pParam & (int)VehicleFlag.CAMERA_DECOUPLED) == (int)VehicleFlag.CAMERA_DECOUPLED) + { + if ((m_flags & VehicleFlag.CAMERA_DECOUPLED) != (VehicleFlag)0) + m_flags &= ~(VehicleFlag.CAMERA_DECOUPLED); + } + if ((pParam & (int)VehicleFlag.NO_X) == (int)VehicleFlag.NO_X) + { + if ((m_flags & VehicleFlag.NO_X) != (VehicleFlag)0) + m_flags &= ~(VehicleFlag.NO_X); + } + if ((pParam & (int)VehicleFlag.NO_Y) == (int)VehicleFlag.NO_Y) + { + if ((m_flags & VehicleFlag.NO_Y) != (VehicleFlag)0) + m_flags &= ~(VehicleFlag.NO_Y); + } + if ((pParam & (int)VehicleFlag.NO_Z) == (int)VehicleFlag.NO_Z) + { + if ((m_flags & VehicleFlag.NO_Z) != (VehicleFlag)0) + m_flags &= ~(VehicleFlag.NO_Z); + } + if ((pParam & (int)VehicleFlag.LOCK_HOVER_HEIGHT) == (int)VehicleFlag.LOCK_HOVER_HEIGHT) + { + if ((m_Hoverflags & VehicleFlag.LOCK_HOVER_HEIGHT) != (VehicleFlag)0) + m_Hoverflags &= ~(VehicleFlag.LOCK_HOVER_HEIGHT); + } + if ((pParam & (int)VehicleFlag.NO_DEFLECTION) == (int)VehicleFlag.NO_DEFLECTION) + { + if ((m_flags & VehicleFlag.NO_DEFLECTION) != (VehicleFlag)0) + m_flags &= ~(VehicleFlag.NO_DEFLECTION); + } + if ((pParam & (int)VehicleFlag.LOCK_ROTATION) == (int)VehicleFlag.LOCK_ROTATION) + { + if ((m_flags & VehicleFlag.LOCK_ROTATION) != (VehicleFlag)0) + m_flags &= ~(VehicleFlag.LOCK_ROTATION); + } + } + else + { + if ((pParam & (int)VehicleFlag.HOVER_GLOBAL_HEIGHT) == (int)VehicleFlag.HOVER_GLOBAL_HEIGHT) + { + m_Hoverflags |= (VehicleFlag.HOVER_GLOBAL_HEIGHT | m_flags); + } + if ((pParam & (int)VehicleFlag.HOVER_TERRAIN_ONLY) == (int)VehicleFlag.HOVER_TERRAIN_ONLY) + { + m_Hoverflags |= (VehicleFlag.HOVER_TERRAIN_ONLY | m_flags); + } + if ((pParam & (int)VehicleFlag.HOVER_UP_ONLY) == (int)VehicleFlag.HOVER_UP_ONLY) + { + m_Hoverflags |= (VehicleFlag.HOVER_UP_ONLY | m_flags); + } + if ((pParam & (int)VehicleFlag.HOVER_WATER_ONLY) == (int)VehicleFlag.HOVER_WATER_ONLY) + { + m_Hoverflags |= (VehicleFlag.HOVER_WATER_ONLY | m_flags); + } + if ((pParam & (int)VehicleFlag.LIMIT_MOTOR_UP) == (int)VehicleFlag.LIMIT_MOTOR_UP) + { + m_flags |= (VehicleFlag.LIMIT_MOTOR_UP | m_flags); + } + if ((pParam & (int)VehicleFlag.MOUSELOOK_BANK) == (int)VehicleFlag.MOUSELOOK_BANK) + { + m_flags |= (VehicleFlag.MOUSELOOK_BANK | m_flags); + } + if ((pParam & (int)VehicleFlag.MOUSELOOK_STEER) == (int)VehicleFlag.MOUSELOOK_STEER) + { + m_flags |= (VehicleFlag.MOUSELOOK_STEER | m_flags); + } + if ((pParam & (int)VehicleFlag.NO_DEFLECTION_UP) == (int)VehicleFlag.NO_DEFLECTION_UP) + { + m_flags |= (VehicleFlag.NO_DEFLECTION_UP | m_flags); + } + if ((pParam & (int)VehicleFlag.CAMERA_DECOUPLED) == (int)VehicleFlag.CAMERA_DECOUPLED) + { + m_flags |= (VehicleFlag.CAMERA_DECOUPLED | m_flags); + } + if ((pParam & (int)VehicleFlag.NO_X) == (int)VehicleFlag.NO_X) + { + m_flags |= (VehicleFlag.NO_X); + } + if ((pParam & (int)VehicleFlag.NO_Y) == (int)VehicleFlag.NO_Y) + { + m_flags |= (VehicleFlag.NO_Y); + } + if ((pParam & (int)VehicleFlag.NO_Z) == (int)VehicleFlag.NO_Z) + { + m_flags |= (VehicleFlag.NO_Z); + } + if ((pParam & (int)VehicleFlag.LOCK_HOVER_HEIGHT) == (int)VehicleFlag.LOCK_HOVER_HEIGHT) + { + m_Hoverflags |= (VehicleFlag.LOCK_HOVER_HEIGHT); + } + if ((pParam & (int)VehicleFlag.NO_DEFLECTION) == (int)VehicleFlag.NO_DEFLECTION) + { + m_flags |= (VehicleFlag.NO_DEFLECTION); + } + if ((pParam & (int)VehicleFlag.LOCK_ROTATION) == (int)VehicleFlag.LOCK_ROTATION) + { + m_flags |= (VehicleFlag.LOCK_ROTATION); + } + } + }//end ProcessVehicleFlags + + internal void ProcessTypeChange(Vehicle pType) + { + // Set Defaults For Type + m_type = pType; + switch (pType) + { + case Vehicle.TYPE_NONE: + m_linearFrictionTimescale = new Vector3(0, 0, 0); + m_angularFrictionTimescale = new Vector3(0, 0, 0); + m_linearMotorDirection = Vector3.Zero; + m_linearMotorTimescale = 0; + m_linearMotorDecayTimescale = 0; + m_angularMotorDirection = Vector3.Zero; + m_angularMotorTimescale = 0; + m_angularMotorDecayTimescale = 0; + m_VhoverHeight = 0; + m_VhoverTimescale = 0; + m_VehicleBuoyancy = 0; + m_flags = (VehicleFlag)0; + break; + + case Vehicle.TYPE_SLED: + m_linearFrictionTimescale = new Vector3(30, 1, 1000); + m_angularFrictionTimescale = new Vector3(1000, 1000, 1000); + m_linearMotorDirection = Vector3.Zero; + m_linearMotorTimescale = 1000; + m_linearMotorDecayTimescale = 120; + m_angularMotorDirection = Vector3.Zero; + m_angularMotorTimescale = 1000; + m_angularMotorDecayTimescale = 120; + m_VhoverHeight = 0; +// m_VhoverEfficiency = 1; + m_VhoverTimescale = 10; + m_VehicleBuoyancy = 0; + // m_linearDeflectionEfficiency = 1; + // m_linearDeflectionTimescale = 1; + // m_angularDeflectionEfficiency = 1; + // m_angularDeflectionTimescale = 1000; + // m_bankingEfficiency = 0; + // m_bankingMix = 1; + // m_bankingTimescale = 10; + // m_referenceFrame = Quaternion.Identity; + m_Hoverflags &= + ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | + VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY); + m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.LIMIT_MOTOR_UP); + break; + case Vehicle.TYPE_CAR: + m_linearFrictionTimescale = new Vector3(100, 2, 1000); + m_angularFrictionTimescale = new Vector3(1000, 1000, 1000); + m_linearMotorDirection = Vector3.Zero; + m_linearMotorTimescale = 1; + m_linearMotorDecayTimescale = 60; + m_angularMotorDirection = Vector3.Zero; + m_angularMotorTimescale = 1; + m_angularMotorDecayTimescale = 0.8f; + m_VhoverHeight = 0; +// m_VhoverEfficiency = 0; + m_VhoverTimescale = 1000; + m_VehicleBuoyancy = 0; + // // m_linearDeflectionEfficiency = 1; + // // m_linearDeflectionTimescale = 2; + // // m_angularDeflectionEfficiency = 0; + // m_angularDeflectionTimescale = 10; + m_verticalAttractionEfficiency = 1f; + m_verticalAttractionTimescale = 10f; + // m_bankingEfficiency = -0.2f; + // m_bankingMix = 1; + // m_bankingTimescale = 1; + // m_referenceFrame = Quaternion.Identity; + m_Hoverflags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT); + m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY | + VehicleFlag.LIMIT_MOTOR_UP); + m_Hoverflags |= (VehicleFlag.HOVER_UP_ONLY); + break; + case Vehicle.TYPE_BOAT: + m_linearFrictionTimescale = new Vector3(10, 3, 2); + m_angularFrictionTimescale = new Vector3(10,10,10); + m_linearMotorDirection = Vector3.Zero; + m_linearMotorTimescale = 5; + m_linearMotorDecayTimescale = 60; + m_angularMotorDirection = Vector3.Zero; + m_angularMotorTimescale = 4; + m_angularMotorDecayTimescale = 4; + m_VhoverHeight = 0; +// m_VhoverEfficiency = 0.5f; + m_VhoverTimescale = 2; + m_VehicleBuoyancy = 1; + // m_linearDeflectionEfficiency = 0.5f; + // m_linearDeflectionTimescale = 3; + // m_angularDeflectionEfficiency = 0.5f; + // m_angularDeflectionTimescale = 5; + m_verticalAttractionEfficiency = 0.5f; + m_verticalAttractionTimescale = 5f; + // m_bankingEfficiency = -0.3f; + // m_bankingMix = 0.8f; + // m_bankingTimescale = 1; + // m_referenceFrame = Quaternion.Identity; + m_Hoverflags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY | + VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY); + m_flags &= ~(VehicleFlag.LIMIT_ROLL_ONLY); + m_flags |= (VehicleFlag.NO_DEFLECTION_UP | + VehicleFlag.LIMIT_MOTOR_UP); + m_Hoverflags |= (VehicleFlag.HOVER_WATER_ONLY); + break; + case Vehicle.TYPE_AIRPLANE: + m_linearFrictionTimescale = new Vector3(200, 10, 5); + m_angularFrictionTimescale = new Vector3(20, 20, 20); + m_linearMotorDirection = Vector3.Zero; + m_linearMotorTimescale = 2; + m_linearMotorDecayTimescale = 60; + m_angularMotorDirection = Vector3.Zero; + m_angularMotorTimescale = 4; + m_angularMotorDecayTimescale = 4; + m_VhoverHeight = 0; +// m_VhoverEfficiency = 0.5f; + m_VhoverTimescale = 1000; + m_VehicleBuoyancy = 0; + // m_linearDeflectionEfficiency = 0.5f; + // m_linearDeflectionTimescale = 3; + // m_angularDeflectionEfficiency = 1; + // m_angularDeflectionTimescale = 2; + m_verticalAttractionEfficiency = 0.9f; + m_verticalAttractionTimescale = 2f; + // m_bankingEfficiency = 1; + // m_bankingMix = 0.7f; + // m_bankingTimescale = 2; + // m_referenceFrame = Quaternion.Identity; + m_Hoverflags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | + VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY); + m_flags &= ~(VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_MOTOR_UP); + m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY); + break; + case Vehicle.TYPE_BALLOON: + m_linearFrictionTimescale = new Vector3(5, 5, 5); + m_angularFrictionTimescale = new Vector3(10, 10, 10); + m_linearMotorDirection = Vector3.Zero; + m_linearMotorTimescale = 5; + m_linearMotorDecayTimescale = 60; + m_angularMotorDirection = Vector3.Zero; + m_angularMotorTimescale = 6; + m_angularMotorDecayTimescale = 10; + m_VhoverHeight = 5; +// m_VhoverEfficiency = 0.8f; + m_VhoverTimescale = 10; + m_VehicleBuoyancy = 1; + // m_linearDeflectionEfficiency = 0; + // m_linearDeflectionTimescale = 5; + // m_angularDeflectionEfficiency = 0; + // m_angularDeflectionTimescale = 5; + m_verticalAttractionEfficiency = 1f; + m_verticalAttractionTimescale = 100f; + // m_bankingEfficiency = 0; + // m_bankingMix = 0.7f; + // m_bankingTimescale = 5; + // m_referenceFrame = Quaternion.Identity; + m_Hoverflags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | + VehicleFlag.HOVER_UP_ONLY); + m_flags &= ~(VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_MOTOR_UP); + m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY); + m_Hoverflags |= (VehicleFlag.HOVER_GLOBAL_HEIGHT); + break; + + } + }//end SetDefaultsForType + + internal void Step(float pTimestep, BSScene pParentScene) + { + if (m_type == Vehicle.TYPE_NONE) return; + + frcount++; // used to limit debug comment output + if (frcount > 100) + frcount = 0; + + MoveLinear(pTimestep, pParentScene); + MoveAngular(pTimestep); + LimitRotation(pTimestep); + }// end Step + + private void MoveLinear(float pTimestep, BSScene _pParentScene) + { + if (!m_linearMotorDirection.ApproxEquals(Vector3.Zero, 0.01f)) // requested m_linearMotorDirection is significant + { + // add drive to body + Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale/pTimestep); + m_lastLinearVelocityVector += (addAmount*10); // lastLinearVelocityVector is the current body velocity vector? + + // This will work temporarily, but we really need to compare speed on an axis + // KF: Limit body velocity to applied velocity? + if (Math.Abs(m_lastLinearVelocityVector.X) > Math.Abs(m_linearMotorDirectionLASTSET.X)) + m_lastLinearVelocityVector.X = m_linearMotorDirectionLASTSET.X; + if (Math.Abs(m_lastLinearVelocityVector.Y) > Math.Abs(m_linearMotorDirectionLASTSET.Y)) + m_lastLinearVelocityVector.Y = m_linearMotorDirectionLASTSET.Y; + if (Math.Abs(m_lastLinearVelocityVector.Z) > Math.Abs(m_linearMotorDirectionLASTSET.Z)) + m_lastLinearVelocityVector.Z = m_linearMotorDirectionLASTSET.Z; + + // decay applied velocity + Vector3 decayfraction = ((Vector3.One/(m_linearMotorDecayTimescale/pTimestep))); + //Console.WriteLine("decay: " + decayfraction); + m_linearMotorDirection -= m_linearMotorDirection * decayfraction * 0.5f; + //Console.WriteLine("actual: " + m_linearMotorDirection); + } + else + { // requested is not significant + // if what remains of applied is small, zero it. + if (m_lastLinearVelocityVector.ApproxEquals(Vector3.Zero, 0.01f)) + m_lastLinearVelocityVector = Vector3.Zero; + } + + // convert requested object velocity to world-referenced vector + m_dir = m_lastLinearVelocityVector; + Quaternion rot = m_prim.Orientation; + Quaternion rotq = new Quaternion(rot.X, rot.Y, rot.Z, rot.W); // rotq = rotation of object + m_dir *= rotq; // apply obj rotation to velocity vector + + // add Gravity andBuoyancy + // KF: So far I have found no good method to combine a script-requested + // .Z velocity and gravity. Therefore only 0g will used script-requested + // .Z velocity. >0g (m_VehicleBuoyancy < 1) will used modified gravity only. + Vector3 grav = Vector3.Zero; + // There is some gravity, make a gravity force vector + // that is applied after object velocity. + float objMass = m_prim.Mass; + // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; + grav.Z = _pParentScene.DefaultGravity.Z * objMass * (1f - m_VehicleBuoyancy); + // Preserve the current Z velocity + Vector3 vel_now = m_prim.Velocity; + m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity + + Vector3 pos = m_prim.Position; +// Vector3 accel = new Vector3(-(m_dir.X - m_lastLinearVelocityVector.X / 0.1f), -(m_dir.Y - m_lastLinearVelocityVector.Y / 0.1f), m_dir.Z - m_lastLinearVelocityVector.Z / 0.1f); + Vector3 posChange = new Vector3(); + posChange.X = pos.X - m_lastPositionVector.X; + posChange.Y = pos.Y - m_lastPositionVector.Y; + posChange.Z = pos.Z - m_lastPositionVector.Z; + double Zchange = Math.Abs(posChange.Z); + if (m_BlockingEndPoint != Vector3.Zero) + { + if (pos.X >= (m_BlockingEndPoint.X - (float)1)) + { + pos.X -= posChange.X + 1; + m_prim.Position = pos; + } + if (pos.Y >= (m_BlockingEndPoint.Y - (float)1)) + { + pos.Y -= posChange.Y + 1; + m_prim.Position = pos; + } + if (pos.Z >= (m_BlockingEndPoint.Z - (float)1)) + { + pos.Z -= posChange.Z + 1; + m_prim.Position = pos; + } + if (pos.X <= 0) + { + pos.X += posChange.X + 1; + m_prim.Position = pos; + } + if (pos.Y <= 0) + { + pos.Y += posChange.Y + 1; + m_prim.Position = pos; + } + } + if (pos.Z < _pParentScene.GetTerrainHeightAtXY(pos.X, pos.Y)) + { + pos.Z = _pParentScene.GetTerrainHeightAtXY(pos.X, pos.Y) + 2; + m_prim.Position = pos; + } + + // Check if hovering + if ((m_Hoverflags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0) + { + // We should hover, get the target height + if ((m_Hoverflags & VehicleFlag.HOVER_WATER_ONLY) != 0) + { + m_VhoverTargetHeight = _pParentScene.GetWaterLevel() + m_VhoverHeight; + } + if ((m_Hoverflags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0) + { + m_VhoverTargetHeight = _pParentScene.GetTerrainHeightAtXY(pos.X, pos.Y) + m_VhoverHeight; + } + if ((m_Hoverflags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0) + { + m_VhoverTargetHeight = m_VhoverHeight; + } + + if ((m_Hoverflags & VehicleFlag.HOVER_UP_ONLY) != 0) + { + // If body is aready heigher, use its height as target height + if (pos.Z > m_VhoverTargetHeight) m_VhoverTargetHeight = pos.Z; + } + if ((m_Hoverflags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0) + { + if ((pos.Z - m_VhoverTargetHeight) > .2 || (pos.Z - m_VhoverTargetHeight) < -.2) + { + m_prim.Position = pos; + } + } + else + { + float herr0 = pos.Z - m_VhoverTargetHeight; + // Replace Vertical speed with correction figure if significant + if (Math.Abs(herr0) > 0.01f) + { + m_dir.Z = -((herr0 * pTimestep * 50.0f) / m_VhoverTimescale); + //KF: m_VhoverEfficiency is not yet implemented + } + else + { + m_dir.Z = 0f; + } + } + +// m_VhoverEfficiency = 0f; // 0=boucy, 1=Crit.damped +// m_VhoverTimescale = 0f; // time to acheive height +// pTimestep is time since last frame,in secs + } + + if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0) + { + //Start Experimental Values + if (Zchange > .3) + { + grav.Z = (float)(grav.Z * 3); + } + if (Zchange > .15) + { + grav.Z = (float)(grav.Z * 2); + } + if (Zchange > .75) + { + grav.Z = (float)(grav.Z * 1.5); + } + if (Zchange > .05) + { + grav.Z = (float)(grav.Z * 1.25); + } + if (Zchange > .025) + { + grav.Z = (float)(grav.Z * 1.125); + } + float terraintemp = _pParentScene.GetTerrainHeightAtXY(pos.X, pos.Y); + float postemp = (pos.Z - terraintemp); + if (postemp > 2.5f) + { + grav.Z = (float)(grav.Z * 1.037125); + } + //End Experimental Values + } + if ((m_flags & (VehicleFlag.NO_X)) != 0) + { + m_dir.X = 0; + } + if ((m_flags & (VehicleFlag.NO_Y)) != 0) + { + m_dir.Y = 0; + } + if ((m_flags & (VehicleFlag.NO_Z)) != 0) + { + m_dir.Z = 0; + } + + m_lastPositionVector = m_prim.Position; + + // Apply velocity + m_prim.Velocity = m_dir; + // apply gravity force + m_prim.Force = grav; + + + // apply friction + Vector3 decayamount = Vector3.One / (m_linearFrictionTimescale / pTimestep); + m_lastLinearVelocityVector -= m_lastLinearVelocityVector * decayamount; + } // end MoveLinear() + + private void MoveAngular(float pTimestep) + { + /* + private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor + private int m_angularMotorApply = 0; // application frame counter + private float m_angularMotorVelocity = 0; // current angular motor velocity (ramps up and down) + private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate + private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate + private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate + private Vector3 m_lastAngularVelocity = Vector3.Zero; // what was last applied to body + */ + + // Get what the body is doing, this includes 'external' influences + Vector3 angularVelocity = m_prim.AngularVelocity; + // Vector3 angularVelocity = Vector3.Zero; + + if (m_angularMotorApply > 0) + { + // ramp up to new value + // current velocity += error / (time to get there / step interval) + // requested speed - last motor speed + m_angularMotorVelocity.X += (m_angularMotorDirection.X - m_angularMotorVelocity.X) / (m_angularMotorTimescale / pTimestep); + m_angularMotorVelocity.Y += (m_angularMotorDirection.Y - m_angularMotorVelocity.Y) / (m_angularMotorTimescale / pTimestep); + m_angularMotorVelocity.Z += (m_angularMotorDirection.Z - m_angularMotorVelocity.Z) / (m_angularMotorTimescale / pTimestep); + + m_angularMotorApply--; // This is done so that if script request rate is less than phys frame rate the expected + // velocity may still be acheived. + } + else + { + // no motor recently applied, keep the body velocity + /* m_angularMotorVelocity.X = angularVelocity.X; + m_angularMotorVelocity.Y = angularVelocity.Y; + m_angularMotorVelocity.Z = angularVelocity.Z; */ + + // and decay the velocity + m_angularMotorVelocity -= m_angularMotorVelocity / (m_angularMotorDecayTimescale / pTimestep); + } // end motor section + + // Vertical attractor section + Vector3 vertattr = Vector3.Zero; + + if (m_verticalAttractionTimescale < 300) + { + float VAservo = 0.2f / (m_verticalAttractionTimescale * pTimestep); + // get present body rotation + Quaternion rotq = m_prim.Orientation; + // make a vector pointing up + Vector3 verterr = Vector3.Zero; + verterr.Z = 1.0f; + // rotate it to Body Angle + verterr = verterr * rotq; + // verterr.X and .Y are the World error ammounts. They are 0 when there is no error (Vehicle Body is 'vertical'), and .Z will be 1. + // As the body leans to its side |.X| will increase to 1 and .Z fall to 0. As body inverts |.X| will fall and .Z will go + // negative. Similar for tilt and |.Y|. .X and .Y must be modulated to prevent a stable inverted body. + if (verterr.Z < 0.0f) + { + verterr.X = 2.0f - verterr.X; + verterr.Y = 2.0f - verterr.Y; + } + // Error is 0 (no error) to +/- 2 (max error) + // scale it by VAservo + verterr = verterr * VAservo; +//if (frcount == 0) Console.WriteLine("VAerr=" + verterr); + + // As the body rotates around the X axis, then verterr.Y increases; Rotated around Y then .X increases, so + // Change Body angular velocity X based on Y, and Y based on X. Z is not changed. + vertattr.X = verterr.Y; + vertattr.Y = - verterr.X; + vertattr.Z = 0f; + + // scaling appears better usingsquare-law + float bounce = 1.0f - (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency); + vertattr.X += bounce * angularVelocity.X; + vertattr.Y += bounce * angularVelocity.Y; + + } // else vertical attractor is off + + // m_lastVertAttractor = vertattr; + + // Bank section tba + // Deflection section tba + + // Sum velocities + m_lastAngularVelocity = m_angularMotorVelocity + vertattr; // + bank + deflection + + if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) + { + m_lastAngularVelocity.X = 0; + m_lastAngularVelocity.Y = 0; + } + + if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f)) + { + m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero. + } + + // apply friction + Vector3 decayamount = Vector3.One / (m_angularFrictionTimescale / pTimestep); + m_lastAngularVelocity -= m_lastAngularVelocity * decayamount; + + // Apply to the body + m_prim.AngularVelocity = m_lastAngularVelocity; + + } //end MoveAngular + internal void LimitRotation(float timestep) + { + Quaternion rotq = m_prim.Orientation; // rotq = rotation of object + Quaternion m_rot = rotq; + bool changed = false; + if (m_RollreferenceFrame != Quaternion.Identity) + { + if (rotq.X >= m_RollreferenceFrame.X) + { + m_rot.X = rotq.X - (m_RollreferenceFrame.X / 2); + } + if (rotq.Y >= m_RollreferenceFrame.Y) + { + m_rot.Y = rotq.Y - (m_RollreferenceFrame.Y / 2); + } + if (rotq.X <= -m_RollreferenceFrame.X) + { + m_rot.X = rotq.X + (m_RollreferenceFrame.X / 2); + } + if (rotq.Y <= -m_RollreferenceFrame.Y) + { + m_rot.Y = rotq.Y + (m_RollreferenceFrame.Y / 2); + } + changed = true; + } + if ((m_flags & VehicleFlag.LOCK_ROTATION) != 0) + { + m_rot.X = 0; + m_rot.Y = 0; + changed = true; + } + if (changed) + m_prim.Orientation = m_rot; + } + } +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs new file mode 100644 index 0000000..61be56d --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs @@ -0,0 +1,68 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSPlugin : IPhysicsPlugin +{ + //private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private BSScene _mScene; + + public BSPlugin() + { + } + + public bool Init() + { + return true; + } + + public PhysicsScene GetScene(String sceneIdentifier) + { + if (_mScene == null) + { + _mScene = new BSScene(sceneIdentifier); + } + return (_mScene); + } + + public string GetName() + { + return ("BulletSim"); + } + + public void Dispose() + { + } +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs new file mode 100644 index 0000000..82a8803 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -0,0 +1,1192 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Xml; +using log4net; +using OMV = OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; +using OpenSim.Region.Physics.ConvexDecompositionDotNet; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + [Serializable] +public sealed class BSPrim : PhysicsActor +{ + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[BULLETS PRIM]"; + + private IMesh _mesh; + private PrimitiveBaseShape _pbs; + private ShapeData.PhysicsShapeType _shapeType; + private ulong _hullKey; + private List _hulls; + + private BSScene _scene; + private String _avName; + private uint _localID = 0; + + private OMV.Vector3 _size; + private OMV.Vector3 _scale; + private bool _stopped; + private bool _grabbed; + private bool _isSelected; + private bool _isVolumeDetect; + private OMV.Vector3 _position; + private float _mass; + private float _density; + private OMV.Vector3 _force; + private OMV.Vector3 _velocity; + private OMV.Vector3 _torque; + private float _collisionScore; + private OMV.Vector3 _acceleration; + private OMV.Quaternion _orientation; + private int _physicsActorType; + private bool _isPhysical; + private bool _flying; + private float _friction; + private bool _setAlwaysRun; + private bool _throttleUpdates; + private bool _isColliding; + private bool _collidingGround; + private bool _collidingObj; + private bool _floatOnWater; + private OMV.Vector3 _rotationalVelocity; + private bool _kinematic; + private float _buoyancy; + private OMV.Vector3 _angularVelocity; + + private List _childrenPrims; + private BSPrim _parentPrim; + + private int _subscribedEventsMs = 0; + private int _lastCollisionTime = 0; + long _collidingStep; + long _collidingGroundStep; + + private BSDynamics _vehicle; + + private OMV.Vector3 _PIDTarget; + private bool _usePID; + private float _PIDTau; + private bool _useHoverPID; + private float _PIDHoverHeight; + private PIDHoverType _PIDHoverType; + private float _PIDHoverTao; + + public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, + OMV.Quaternion rotation, IMesh mesh, PrimitiveBaseShape pbs, bool pisPhysical) + { + // m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID); + _localID = localID; + _avName = primName; + _scene = parent_scene; + _position = pos; + _size = size; + _scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type + _orientation = rotation; + _buoyancy = 1f; + _velocity = OMV.Vector3.Zero; + _angularVelocity = OMV.Vector3.Zero; + _mesh = mesh; + _hullKey = 0; + _pbs = pbs; + _isPhysical = pisPhysical; + _isVolumeDetect = false; + _subscribedEventsMs = 0; + _friction = _scene.DefaultFriction; // TODO: compute based on object material + _density = _scene.DefaultDensity; // TODO: compute based on object material + _parentPrim = null; // not a child or a parent + _vehicle = new BSDynamics(this); // add vehicleness + _childrenPrims = new List(); + if (_isPhysical) + _mass = CalculateMass(); + else + _mass = 0f; + // do the actual object creation at taint time + _scene.TaintedObject(delegate() + { + CreateGeom(); + CreateObject(); + }); + } + + // called when this prim is being destroyed and we should free all the resources + public void Destroy() + { + // m_log.DebugFormat("{0}: Destroy", LogHeader); + // Undo any vehicle properties + _vehicle.ProcessTypeChange(Vehicle.TYPE_NONE); + _scene.RemoveVehiclePrim(this); // just to make sure + _scene.TaintedObject(delegate() + { + BulletSimAPI.DestroyObject(_scene.WorldID, _localID); + }); + } + + public override bool Stopped { + get { return _stopped; } + } + public override OMV.Vector3 Size { + get { return _size; } + set { + _size = value; + _scene.TaintedObject(delegate() + { + if (_isPhysical) _mass = CalculateMass(); // changing size changes the mass + BulletSimAPI.SetObjectScaleMass(_scene.WorldID, _localID, _scale, _mass, _isPhysical); + RecreateGeomAndObject(); + }); + } + } + public override PrimitiveBaseShape Shape { + set { + _pbs = value; + _scene.TaintedObject(delegate() + { + if (_isPhysical) _mass = CalculateMass(); // changing the shape changes the mass + RecreateGeomAndObject(); + }); + } + } + public override uint LocalID { + set { _localID = value; } + get { return _localID; } + } + public override bool Grabbed { + set { _grabbed = value; + } + } + public override bool Selected { + set { + _isSelected = value; + _scene.TaintedObject(delegate() + { + SetObjectDynamic(); + }); + } + } + public override void CrossingFailure() { return; } + + // link me to the specified parent + public override void link(PhysicsActor obj) { + BSPrim parent = (BSPrim)obj; + // m_log.DebugFormat("{0}: link {1}/{2} to {3}", LogHeader, _avName, _localID, obj.LocalID); + // TODO: decide if this parent checking needs to happen at taint time + if (_parentPrim == null) + { + if (parent != null) + { + // I don't have a parent so I am joining a linkset + parent.AddChildToLinkset(this); + } + } + else + { + // I already have a parent, is parenting changing? + if (parent != _parentPrim) + { + if (parent == null) + { + // we are being removed from a linkset + _parentPrim.RemoveChildFromLinkset(this); + } + else + { + // asking to reparent a prim should not happen + m_log.ErrorFormat("{0}: Reparenting a prim. ", LogHeader); + } + } + } + return; + } + + // delink me from my linkset + public override void delink() { + // TODO: decide if this parent checking needs to happen at taint time + // Race condition here: if link() and delink() in same simulation tick, the delink will not happen + // m_log.DebugFormat("{0}: delink {1}/{2}", LogHeader, _avName, _localID); + if (_parentPrim != null) + { + _parentPrim.RemoveChildFromLinkset(this); + } + return; + } + + public void AddChildToLinkset(BSPrim pchild) + { + BSPrim child = pchild; + _scene.TaintedObject(delegate() + { + if (!_childrenPrims.Contains(child)) + { + _childrenPrims.Add(child); + child.ParentPrim = this; // the child has gained a parent + RecreateGeomAndObject(); // rebuild my shape with the new child added + } + }); + return; + } + + public void RemoveChildFromLinkset(BSPrim pchild) + { + BSPrim child = pchild; + _scene.TaintedObject(delegate() + { + if (_childrenPrims.Contains(child)) + { + _childrenPrims.Remove(child); + child.ParentPrim = null; // the child has lost its parent + RecreateGeomAndObject(); // rebuild my shape with the child removed + } + else + { + m_log.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset"); + } + }); + return; + } + + public BSPrim ParentPrim + { + set { _parentPrim = value; } + } + + public ulong HullKey + { + get { return _hullKey; } + } + + // return true if we are the root of a linkset (there are children to manage) + public bool IsRootOfLinkset + { + get { return (_parentPrim == null && _childrenPrims.Count != 0); } + } + + public override void LockAngularMotion(OMV.Vector3 axis) { return; } + + public override OMV.Vector3 Position { + get { + // don't do the following GetObjectPosition because this function is called a zillion times + // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); + return _position; + } + set { + _position = value; + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); + // m_log.DebugFormat("{0}: setPosition: id={1}, position={2}", LogHeader, _localID, _position); + }); + } + } + public override float Mass { + get { return _mass; } + } + public override OMV.Vector3 Force { + get { return _force; } + set { + _force = value; + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); + }); + } + } + + public override int VehicleType { + get { + return (int)_vehicle.Type; // if we are a vehicle, return that type + } + set { + Vehicle type = (Vehicle)value; + _vehicle.ProcessTypeChange(type); + _scene.TaintedObject(delegate() + { + if (type == Vehicle.TYPE_NONE) + { + _scene.RemoveVehiclePrim(this); + } + else + { + // make it so the scene will call us each tick to do vehicle things + _scene.AddVehiclePrim(this); + } + return; + }); + } + } + public override void VehicleFloatParam(int param, float value) + { + _vehicle.ProcessFloatVehicleParam((Vehicle)param, value); + } + public override void VehicleVectorParam(int param, OMV.Vector3 value) + { + _vehicle.ProcessVectorVehicleParam((Vehicle)param, value); + } + public override void VehicleRotationParam(int param, OMV.Quaternion rotation) + { + _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation); + } + public override void VehicleFlags(int param, bool remove) + { + _vehicle.ProcessVehicleFlags(param, remove); + } + // Called each simulation step to advance vehicle characteristics + public void StepVehicle(float timeStep) + { + _vehicle.Step(timeStep, _scene); + } + + // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more + public override void SetVolumeDetect(int param) { + bool newValue = (param != 0); + if (_isVolumeDetect != newValue) + { + _isVolumeDetect = newValue; + _scene.TaintedObject(delegate() + { + SetObjectDynamic(); + }); + } + return; + } + + public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } } + public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } } + public override OMV.Vector3 Velocity { + get { return _velocity; } + set { _velocity = value; + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectVelocity(_scene.WorldID, LocalID, _velocity); + }); + } + } + public OMV.Vector3 AngularVelocity + { + get { return _angularVelocity; } + set + { + _angularVelocity = value; + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectAngularVelocity(_scene.WorldID, LocalID, _angularVelocity); + }); + } + } + public override OMV.Vector3 Torque { + get { return _torque; } + set { _torque = value; + } + } + public override float CollisionScore { + get { return _collisionScore; } + set { _collisionScore = value; + } + } + public override OMV.Vector3 Acceleration { + get { return _acceleration; } + } + public override OMV.Quaternion Orientation { + get { return _orientation; } + set { + _orientation = value; + _scene.TaintedObject(delegate() + { + // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); + BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); + // m_log.DebugFormat("{0}: set orientation: {1}", LogHeader, _orientation); + }); + } + } + public override int PhysicsActorType { + get { return _physicsActorType; } + set { _physicsActorType = value; + } + } + public override bool IsPhysical { + get { return _isPhysical; } + set { + _isPhysical = value; + _scene.TaintedObject(delegate() + { + SetObjectDynamic(); + }); + } + } + + // An object is static (does not move) if selected or not physical + private bool IsStatic + { + get { return _isSelected || !IsPhysical; } + } + + // An object is solid if it's not phantom and if it's not doing VolumeDetect + private bool IsSolid + { + get { return !IsPhantom && !_isVolumeDetect; } + } + + // make gravity work if the object is physical and not selected + // no locking here because only called when it is safe + private void SetObjectDynamic() + { + // non-physical things work best with a mass of zero + _mass = IsStatic ? 0f : CalculateMass(); + BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), _mass); + // m_log.DebugFormat("{0}: ID={1}, SetObjectDynamic: IsStatic={2}, IsSolid={3}, mass={4}", LogHeader, _localID, IsStatic, IsSolid, _mass); + } + + // prims don't fly + public override bool Flying { + get { return _flying; } + set { _flying = value; } + } + public override bool SetAlwaysRun { + get { return _setAlwaysRun; } + set { _setAlwaysRun = value; } + } + public override bool ThrottleUpdates { + get { return _throttleUpdates; } + set { _throttleUpdates = value; } + } + public override bool IsColliding { + get { return (_collidingStep == _scene.SimulationStep); } + set { _isColliding = value; } + } + public override bool CollidingGround { + get { return (_collidingGroundStep == _scene.SimulationStep); } + set { _collidingGround = value; } + } + public override bool CollidingObj { + get { return _collidingObj; } + set { _collidingObj = value; } + } + public bool IsPhantom { + get { + // SceneObjectPart removes phantom objects from the physics scene + // so, although we could implement touching and such, we never + // are invoked as a phantom object + return false; + } + } + public override bool FloatOnWater { + set { _floatOnWater = value; } + } + public override OMV.Vector3 RotationalVelocity { + get { return _rotationalVelocity; } + set { _rotationalVelocity = value; + // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); + } + } + public override bool Kinematic { + get { return _kinematic; } + set { _kinematic = value; + // m_log.DebugFormat("{0}: Kinematic={1}", LogHeader, _kinematic); + } + } + public override float Buoyancy { + get { return _buoyancy; } + set { _buoyancy = value; + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, _localID, _buoyancy); + }); + } + } + + // Used for MoveTo + public override OMV.Vector3 PIDTarget { + set { _PIDTarget = value; } + } + public override bool PIDActive { + set { _usePID = value; } + } + public override float PIDTau { + set { _PIDTau = value; } + } + + // Used for llSetHoverHeight and maybe vehicle height + // Hover Height will override MoveTo target's Z + public override bool PIDHoverActive { + set { _useHoverPID = value; } + } + public override float PIDHoverHeight { + set { _PIDHoverHeight = value; } + } + public override PIDHoverType PIDHoverType { + set { _PIDHoverType = value; } + } + public override float PIDHoverTau { + set { _PIDHoverTao = value; } + } + + // For RotLookAt + public override OMV.Quaternion APIDTarget { set { return; } } + public override bool APIDActive { set { return; } } + public override float APIDStrength { set { return; } } + public override float APIDDamping { set { return; } } + + public override void AddForce(OMV.Vector3 force, bool pushforce) { + if (force.IsFinite()) + { + _force.X += force.X; + _force.Y += force.Y; + _force.Z += force.Z; + } + else + { + m_log.WarnFormat("{0}: Got a NaN force applied to a Character", LogHeader); + } + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); + }); + } + + public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { + // m_log.DebugFormat("{0}: AddAngularForce. f={1}, push={2}", LogHeader, force, pushforce); + } + public override void SetMomentum(OMV.Vector3 momentum) { + } + public override void SubscribeEvents(int ms) { + _subscribedEventsMs = ms; + _lastCollisionTime = Util.EnvironmentTickCount() - _subscribedEventsMs; // make first collision happen + } + public override void UnSubscribeEvents() { + _subscribedEventsMs = 0; + } + public override bool SubscribedEvents() { + return (_subscribedEventsMs > 0); + } + + #region Mass Calculation + + private float CalculateMass() + { + float volume = _size.X * _size.Y * _size.Z; // default + float tmp; + + float returnMass = 0; + float hollowAmount = (float)_pbs.ProfileHollow * 2.0e-5f; + float hollowVolume = hollowAmount * hollowAmount; + + switch (_pbs.ProfileShape) + { + case ProfileShape.Square: + // default box + + if (_pbs.PathCurve == (byte)Extrusion.Straight) + { + if (hollowAmount > 0.0) + { + switch (_pbs.HollowShape) + { + case HollowShape.Square: + case HollowShape.Same: + break; + + case HollowShape.Circle: + + hollowVolume *= 0.78539816339f; + break; + + case HollowShape.Triangle: + + hollowVolume *= (0.5f * .5f); + break; + + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + + else if (_pbs.PathCurve == (byte)Extrusion.Curve1) + { + //a tube + + volume *= 0.78539816339e-2f * (float)(200 - _pbs.PathScaleX); + tmp= 1.0f -2.0e-2f * (float)(200 - _pbs.PathScaleY); + volume -= volume*tmp*tmp; + + if (hollowAmount > 0.0) + { + hollowVolume *= hollowAmount; + + switch (_pbs.HollowShape) + { + case HollowShape.Square: + case HollowShape.Same: + break; + + case HollowShape.Circle: + hollowVolume *= 0.78539816339f;; + break; + + case HollowShape.Triangle: + hollowVolume *= 0.5f * 0.5f; + break; + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + + break; + + case ProfileShape.Circle: + + if (_pbs.PathCurve == (byte)Extrusion.Straight) + { + volume *= 0.78539816339f; // elipse base + + if (hollowAmount > 0.0) + { + switch (_pbs.HollowShape) + { + case HollowShape.Same: + case HollowShape.Circle: + break; + + case HollowShape.Square: + hollowVolume *= 0.5f * 2.5984480504799f; + break; + + case HollowShape.Triangle: + hollowVolume *= .5f * 1.27323954473516f; + break; + + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + + else if (_pbs.PathCurve == (byte)Extrusion.Curve1) + { + volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - _pbs.PathScaleX); + tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); + volume *= (1.0f - tmp * tmp); + + if (hollowAmount > 0.0) + { + + // calculate the hollow volume by it's shape compared to the prim shape + hollowVolume *= hollowAmount; + + switch (_pbs.HollowShape) + { + case HollowShape.Same: + case HollowShape.Circle: + break; + + case HollowShape.Square: + hollowVolume *= 0.5f * 2.5984480504799f; + break; + + case HollowShape.Triangle: + hollowVolume *= .5f * 1.27323954473516f; + break; + + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + break; + + case ProfileShape.HalfCircle: + if (_pbs.PathCurve == (byte)Extrusion.Curve1) + { + volume *= 0.52359877559829887307710723054658f; + } + break; + + case ProfileShape.EquilateralTriangle: + + if (_pbs.PathCurve == (byte)Extrusion.Straight) + { + volume *= 0.32475953f; + + if (hollowAmount > 0.0) + { + + // calculate the hollow volume by it's shape compared to the prim shape + switch (_pbs.HollowShape) + { + case HollowShape.Same: + case HollowShape.Triangle: + hollowVolume *= .25f; + break; + + case HollowShape.Square: + hollowVolume *= 0.499849f * 3.07920140172638f; + break; + + case HollowShape.Circle: + // Hollow shape is a perfect cyllinder in respect to the cube's scale + // Cyllinder hollow volume calculation + + hollowVolume *= 0.1963495f * 3.07920140172638f; + break; + + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + else if (_pbs.PathCurve == (byte)Extrusion.Curve1) + { + volume *= 0.32475953f; + volume *= 0.01f * (float)(200 - _pbs.PathScaleX); + tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); + volume *= (1.0f - tmp * tmp); + + if (hollowAmount > 0.0) + { + + hollowVolume *= hollowAmount; + + switch (_pbs.HollowShape) + { + case HollowShape.Same: + case HollowShape.Triangle: + hollowVolume *= .25f; + break; + + case HollowShape.Square: + hollowVolume *= 0.499849f * 3.07920140172638f; + break; + + case HollowShape.Circle: + + hollowVolume *= 0.1963495f * 3.07920140172638f; + break; + + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + break; + + default: + break; + } + + + + float taperX1; + float taperY1; + float taperX; + float taperY; + float pathBegin; + float pathEnd; + float profileBegin; + float profileEnd; + + if (_pbs.PathCurve == (byte)Extrusion.Straight || _pbs.PathCurve == (byte)Extrusion.Flexible) + { + taperX1 = _pbs.PathScaleX * 0.01f; + if (taperX1 > 1.0f) + taperX1 = 2.0f - taperX1; + taperX = 1.0f - taperX1; + + taperY1 = _pbs.PathScaleY * 0.01f; + if (taperY1 > 1.0f) + taperY1 = 2.0f - taperY1; + taperY = 1.0f - taperY1; + } + else + { + taperX = _pbs.PathTaperX * 0.01f; + if (taperX < 0.0f) + taperX = -taperX; + taperX1 = 1.0f - taperX; + + taperY = _pbs.PathTaperY * 0.01f; + if (taperY < 0.0f) + taperY = -taperY; + taperY1 = 1.0f - taperY; + + } + + + volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY); + + pathBegin = (float)_pbs.PathBegin * 2.0e-5f; + pathEnd = 1.0f - (float)_pbs.PathEnd * 2.0e-5f; + volume *= (pathEnd - pathBegin); + + // this is crude aproximation + profileBegin = (float)_pbs.ProfileBegin * 2.0e-5f; + profileEnd = 1.0f - (float)_pbs.ProfileEnd * 2.0e-5f; + volume *= (profileEnd - profileBegin); + + returnMass = _density * volume; + + if (returnMass <= 0) + returnMass = 0.0001f;//ckrinke: Mass must be greater then zero. + + if (IsRootOfLinkset) + { + foreach (BSPrim prim in _childrenPrims) + { + returnMass += prim.CalculateMass(); + } + } + + if (returnMass > _scene.maximumMassObject) + returnMass = _scene.maximumMassObject; + return returnMass; + }// end CalculateMass + #endregion Mass Calculation + + // Create the geometry information in Bullet for later use + // No locking here because this is done when we know physics is not simulating + private void CreateGeom() + { + if (_mesh == null) + { + // the mesher thought this was too simple to mesh. Use a native Bullet collision shape. + if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1) + { + if (_size.X == _size.Y && _size.Y == _size.Z && _size.X == _size.Z) + { + // m_log.DebugFormat("{0}: CreateGeom: mesh null. Defaulting to sphere of size {1}", LogHeader, _size); + _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE; + // Bullet native objects are scaled by the Bullet engine so pass the size in + _scale = _size; + } + } + else + { + // m_log.DebugFormat("{0}: CreateGeom: mesh null. Defaulting to box of size {1}", LogHeader, _size); + _shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX; + _scale = _size; + } + } + else + { + if (_hullKey != 0) + { + // m_log.DebugFormat("{0}: CreateGeom: deleting old hull. Key={1}", LogHeader, _hullKey); + BulletSimAPI.DestroyHull(_scene.WorldID, _hullKey); + _hullKey = 0; + _hulls.Clear(); + } + + int[] indices = _mesh.getIndexListAsInt(); + List vertices = _mesh.getVertexList(); + + //format conversion from IMesh format to DecompDesc format + List convIndices = new List(); + List convVertices = new List(); + for (int ii = 0; ii < indices.GetLength(0); ii++) + { + convIndices.Add(indices[ii]); + } + foreach (OMV.Vector3 vv in vertices) + { + convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); + } + + // setup and do convex hull conversion + _hulls = new List(); + DecompDesc dcomp = new DecompDesc(); + dcomp.mIndices = convIndices; + dcomp.mVertices = convVertices; + ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); + // create the hull into the _hulls variable + convexBuilder.process(dcomp); + + // Convert the vertices and indices for passing to unmanaged + // The hull information is passed as a large floating point array. + // The format is: + // convHulls[0] = number of hulls + // convHulls[1] = number of vertices in first hull + // convHulls[2] = hull centroid X coordinate + // convHulls[3] = hull centroid Y coordinate + // convHulls[4] = hull centroid Z coordinate + // convHulls[5] = first hull vertex X + // convHulls[6] = first hull vertex Y + // convHulls[7] = first hull vertex Z + // convHulls[8] = second hull vertex X + // ... + // convHulls[n] = number of vertices in second hull + // convHulls[n+1] = second hull centroid X coordinate + // ... + // + // TODO: is is very inefficient. Someday change the convex hull generator to return + // data structures that do not need to be converted in order to pass to Bullet. + // And maybe put the values directly into pinned memory rather than marshaling. + int hullCount = _hulls.Count; + int totalVertices = 1; // include one for the count of the hulls + foreach (ConvexResult cr in _hulls) + { + totalVertices += 4; // add four for the vertex count and centroid + totalVertices += cr.HullIndices.Count * 3; // we pass just triangles + } + float[] convHulls = new float[totalVertices]; + + convHulls[0] = (float)hullCount; + int jj = 1; + foreach (ConvexResult cr in _hulls) + { + // copy vertices for index access + float3[] verts = new float3[cr.HullVertices.Count]; + int kk = 0; + foreach (float3 ff in cr.HullVertices) + { + verts[kk++] = ff; + } + + // add to the array one hull's worth of data + convHulls[jj++] = cr.HullIndices.Count; + convHulls[jj++] = 0f; // centroid x,y,z + convHulls[jj++] = 0f; + convHulls[jj++] = 0f; + foreach (int ind in cr.HullIndices) + { + convHulls[jj++] = verts[ind].x; + convHulls[jj++] = verts[ind].y; + convHulls[jj++] = verts[ind].z; + } + } + + // create the hull definition in Bullet + _hullKey = (ulong)_pbs.GetHashCode(); + // m_log.DebugFormat("{0}: CreateGeom: calling CreateHull. lid= {1}, key={2}, hulls={3}", LogHeader, _localID, _hullKey, hullCount); + BulletSimAPI.CreateHull(_scene.WorldID, _hullKey, hullCount, convHulls); + _shapeType = ShapeData.PhysicsShapeType.SHAPE_HULL; + // meshes are already scaled by the meshmerizer + _scale = new OMV.Vector3(1f, 1f, 1f); + } + return; + } + + private void HullReturn(ConvexResult result) + { + _hulls.Add(result); + return; + } + + // Create an object in Bullet + // No locking here because this is done when the physics engine is not simulating + private void CreateObject() + { + if (IsRootOfLinkset) + { + // Create a linkset around this object + /* + * NOTE: the original way of creating a linkset was to create a compound hull in the + * root which consisted of the hulls of all the children. This didn't work well because + * OpenSimulator needs updates and collisions for all the children and the physics + * engine didn't create events for the children when the root hull was moved. + * This code creates the compound hull. + // If I am the root prim of a linkset, replace my physical shape with all the + // pieces of the children. + // All of the children should have called CreateGeom so they have a hull + // in the physics engine already. Here we pull together all of those hulls + // into one shape. + int totalPrimsInLinkset = _childrenPrims.Count + 1; + // m_log.DebugFormat("{0}: CreateLinkset. Root prim={1}, prims={2}", LogHeader, LocalID, totalPrimsInLinkset); + ShapeData[] shapes = new ShapeData[totalPrimsInLinkset]; + FillShapeInfo(out shapes[0]); + int ii = 1; + foreach (BSPrim prim in _childrenPrims) + { + // m_log.DebugFormat("{0}: CreateLinkset: adding prim {1}", LogHeader, prim.LocalID); + prim.FillShapeInfo(out shapes[ii]); + ii++; + } + BulletSimAPI.CreateLinkset(_scene.WorldID, totalPrimsInLinkset, shapes); + */ + // Create the linkset by putting constraints between the objects of the set so they cannot move + // relative to each other. + // m_log.DebugFormat("{0}: CreateLinkset. Root prim={1}, prims={2}", LogHeader, LocalID, _childrenPrims.Count+1); + + // remove any constraints that might be in place + foreach (BSPrim prim in _childrenPrims) + { + BulletSimAPI.RemoveConstraint(_scene.WorldID, LocalID, prim.LocalID); + } + // create constraints between the root prim and each of the children + foreach (BSPrim prim in _childrenPrims) + { + // this is a constraint that allows no freedom of movement between the two objects + // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 + BulletSimAPI.AddConstraint(_scene.WorldID, LocalID, prim.LocalID, OMV.Vector3.Zero, OMV.Vector3.Zero, + OMV.Vector3.Zero, OMV.Vector3.Zero, OMV.Vector3.Zero, OMV.Vector3.Zero); + } + } + else + { + // simple object + // m_log.DebugFormat("{0}: CreateObject. ID={1}", LogHeader, LocalID); + ShapeData shape; + FillShapeInfo(out shape); + BulletSimAPI.CreateObject(_scene.WorldID, shape); + } + } + + // Copy prim's info into the BulletSim shape description structure + public void FillShapeInfo(out ShapeData shape) + { + shape.ID = _localID; + shape.Type = _shapeType; + shape.Position = _position; + shape.Rotation = _orientation; + shape.Velocity = _velocity; + shape.Scale = _scale; + shape.Mass = _isPhysical ? _mass : 0f; + shape.Buoyancy = _buoyancy; + shape.MeshKey = _hullKey; + shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse; + shape.Friction = _friction; + shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue; + } + + // Rebuild the geometry and object. + // This is called when the shape changes so we need to recreate the mesh/hull. + // No locking here because this is done when the physics engine is not simulating + private void RecreateGeomAndObject() + { + if (_hullKey != 0) + { + // if a hull already exists, delete the old one + BulletSimAPI.DestroyHull(_scene.WorldID, _hullKey); + _hullKey = 0; + } + // If this object is complex or we are the root of a linkset, build a mesh. + // The root of a linkset must be a mesh so we can create the linked compound object. + if (_scene.NeedsMeshing(_pbs) || IsRootOfLinkset ) + { + // m_log.DebugFormat("{0}: RecreateGeomAndObject: creating mesh", LogHeader); + _mesh = _scene.mesher.CreateMesh(_avName, _pbs, _size, _scene.meshLOD, _isPhysical); + } + else + { + // it's a BulletSim native shape. + _mesh = null; + } + CreateGeom(); // create the geometry for this prim + CreateObject(); + return; + } + + // The physics engine says that properties have updated. Update same and inform + // the world that things have changed. + // TODO: do we really need to check for changed? Maybe just copy values and call RequestPhysicsterseUpdate() + private int UpPropPosition = 1 << 0; + private int UpPropRotation = 1 << 1; + private int UpPropVelocity = 1 << 2; + private int UpPropAcceleration = 1 << 3; + private int UpPropAngularVel = 1 << 4; + + public void UpdateProperties(EntityProperties entprop) + { + int changed = 0; + // assign to the local variables so the normal set action does not happen + if (_position != entprop.Position) + { + _position = entprop.Position; + // m_log.DebugFormat("{0}: UpdateProperties: position = {1}", LogHeader, _position); + changed |= UpPropPosition; + } + if (_orientation != entprop.Rotation) + { + _orientation = entprop.Rotation; + // m_log.DebugFormat("{0}: UpdateProperties: rotation = {1}", LogHeader, _orientation); + changed |= UpPropRotation; + } + if (_velocity != entprop.Velocity) + { + _velocity = entprop.Velocity; + // m_log.DebugFormat("{0}: UpdateProperties: velocity = {1}", LogHeader, _velocity); + changed |= UpPropVelocity; + } + if (_acceleration != entprop.Acceleration) + { + _acceleration = entprop.Acceleration; + // m_log.DebugFormat("{0}: UpdateProperties: acceleration = {1}", LogHeader, _acceleration); + changed |= UpPropAcceleration; + } + if (_rotationalVelocity != entprop.AngularVelocity) + { + _rotationalVelocity = entprop.AngularVelocity; + // m_log.DebugFormat("{0}: UpdateProperties: rotationalVelocity = {1}", LogHeader, _rotationalVelocity); + changed |= UpPropAngularVel; + } + if (changed != 0) + { + // m_log.DebugFormat("{0}: UpdateProperties: id={1}, c={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation); + base.RequestPhysicsterseUpdate(); + } + } + + // I've collided with something + public void Collide(uint collidingWith, ActorTypes type, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) + { + // m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith); + // The following makes IsColliding() and IsCollidingGround() work + _collidingStep = _scene.SimulationStep; + if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID) + { + _collidingGroundStep = _scene.SimulationStep; + } + + if (_subscribedEventsMs == 0) return; // nothing in the object is waiting for collision events + // throttle the collisions to the number of milliseconds specified in the subscription + int nowTime = Util.EnvironmentTickCount(); + if (nowTime < (_lastCollisionTime + _subscribedEventsMs)) return; + _lastCollisionTime = nowTime; + + // create the event for the collision + Dictionary contactPoints = new Dictionary(); + contactPoints.Add(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); + CollisionEventUpdate args = new CollisionEventUpdate(LocalID, (int)type, 1, contactPoints); + base.SendCollisionUpdate(args); + } +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs new file mode 100644 index 0000000..beffd21 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -0,0 +1,553 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using Nini.Config; +using log4net; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; +using OpenMetaverse; +using OpenSim.Region.Framework; + +// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) +// Fix folding up feet +// Fix terrain. Only flat terrain works. Terrain with shape is oriented wrong? Origined wrong? +// Parameterize BulletSim. Pass a structure of parameters to the C++ code. Capsule size, friction, ... +// Shift drag duplication of objects does not work +// Adjust character capsule size when height is adjusted (ScenePresence.SetHeight) +// Test sculpties +// Compute physics FPS reasonably +// Based on material, set density and friction +// More efficient memory usage in passing hull information from BSPrim to BulletSim +// Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly? +// In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground) +// At the moment, physical and phantom causes object to drop through the terrain +// Should prim.link() and prim.delink() membership checking happen at taint time? +// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once +// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect +// Implement the genCollisions feature in BulletSim::SetObjectProperties (don't pass up unneeded collisions) +// Implement LockAngularMotion +// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation) +// Built Galton board (lots of MoveTo's) and some slats were not positioned correctly (mistakes scattered) +// No mistakes with ODE. Shape creation race condition? +// Does NeedsMeshing() really need to exclude all the different shapes? +// +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSScene : PhysicsScene +{ + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[BULLETS SCENE]"; + + private Dictionary m_avatars = new Dictionary(); + private Dictionary m_prims = new Dictionary(); + private List m_vehicles = new List(); + private float[] m_heightMap; + private float m_waterLevel; + private uint m_worldID; + public uint WorldID { get { return m_worldID; } } + + public IMesher mesher; + public int meshLOD = 32; + + private int m_maxSubSteps = 10; + private float m_fixedTimeStep = 1f / 60f; + private long m_simulationStep = 0; + public long SimulationStep { get { return m_simulationStep; } } + + private bool _meshSculptedPrim = true; // cause scuplted prims to get meshed + private bool _forceSimplePrimMeshing = false; // if a cube or sphere, let Bullet do internal shapes + public float maximumMassObject = 10000.01f; + + public const uint TERRAIN_ID = 0; + public const uint GROUNDPLANE_ID = 1; + + public float DefaultFriction = 0.70f; + public float DefaultDensity = 10.000006836f; // Aluminum g/cm3; TODO: compute based on object material + public Vector3 DefaultGravity = new Vector3(0, 0, -9.80665f); + + public delegate void TaintCallback(); + private List _taintedObjects; + private Object _taintLock = new Object(); + + private BulletSimAPI.DebugLogCallback debugLogCallbackHandle; + + public BSScene(string identifier) + { + } + + public override void Initialise(IMesher meshmerizer, IConfigSource config) + { + if (config != null) + { + IConfig pConfig = config.Configs["BulletSim"]; + if (pConfig != null) + { + DefaultFriction = pConfig.GetFloat("Friction", DefaultFriction); + DefaultDensity = pConfig.GetFloat("Density", DefaultDensity); + // TODO: a lot more parameters that are passed to BulletSim + } + } + // if Debug, enable logging from the unmanaged code + if (m_log.IsDebugEnabled) + { + m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader); + debugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger); + BulletSimAPI.SetDebugLogCallback(debugLogCallbackHandle); + } + + _meshSculptedPrim = true; // mesh sculpted prims + _forceSimplePrimMeshing = false; // use complex meshing if called for + + _taintedObjects = new List(); + + mesher = meshmerizer; + // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); + m_worldID = BulletSimAPI.Initialize(new Vector3(Constants.RegionSize, Constants.RegionSize, 4096f)); + } + + // Called directly from unmanaged code so don't do much + private void BulletLogger(string msg) + { + m_log.Debug("[BULLETS UNMANAGED]:" + msg); + } + + public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying) + { + m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader); + return null; + } + + public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying) + { + // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName); + BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); + lock (m_avatars) m_avatars.Add(localID, actor); + return actor; + } + + public override void RemoveAvatar(PhysicsActor actor) + { + // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader); + if (actor is BSCharacter) + { + ((BSCharacter)actor).Destroy(); + } + try + { + lock (m_avatars) m_avatars.Remove(actor.LocalID); + } + catch (Exception e) + { + m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e); + } + } + + public override void RemovePrim(PhysicsActor prim) + { + // m_log.DebugFormat("{0}: RemovePrim", LogHeader); + if (prim is BSPrim) + { + ((BSPrim)prim).Destroy(); + } + try + { + lock (m_prims) m_prims.Remove(prim.LocalID); + } + catch (Exception e) + { + m_log.WarnFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e); + } + } + + public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position, + Vector3 size, Quaternion rotation) // deprecated + { + return null; + } + public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position, + Vector3 size, Quaternion rotation, bool isPhysical) + { + m_log.ErrorFormat("{0}: CALL TO AddPrimShape in BSScene. NOT IMPLEMENTED", LogHeader); + return null; + } + + public override PhysicsActor AddPrimShape(uint localID, string primName, PrimitiveBaseShape pbs, Vector3 position, + Vector3 size, Quaternion rotation, bool isPhysical) + { + // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName); + IMesh mesh = null; + if (NeedsMeshing(pbs)) + { + // if the prim is complex, create the mesh for it. + // If simple (box or sphere) leave 'mesh' null and physics will do a native shape. + mesh = mesher.CreateMesh(primName, pbs, size, this.meshLOD, isPhysical); + } + BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, mesh, pbs, isPhysical); + lock (m_prims) m_prims.Add(localID, prim); + return prim; + } + + // This is a call from the simulator saying that some physical property has been updated. + // The BulletS driver senses the changing of relevant properties so this taint + // information call is not needed. + public override void AddPhysicsActorTaint(PhysicsActor prim) { } + + // Simulate one timestep + public override float Simulate(float timeStep) + { + int updatedEntityCount; + IntPtr updatedEntitiesPtr; + IntPtr[] updatedEntities; + int collidersCount; + IntPtr collidersPtr; + int[] colliders; // should be uint but Marshal.Copy does not have that overload + + // update the prim states while we know the physics engine is not busy + ProcessTaints(); + + // Some of the prims operate with special vehicle properties + ProcessVehicles(timeStep); + ProcessTaints(); // the vehicles might have added taints + + // step the physical world one interval + m_simulationStep++; + int numSubSteps = BulletSimAPI.PhysicsStep(m_worldID, timeStep, m_maxSubSteps, m_fixedTimeStep, + out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); + + // if there were collisions, they show up here + if (collidersCount > 0) + { + colliders = new int[collidersCount]; + Marshal.Copy(collidersPtr, colliders, 0, collidersCount); + for (int ii = 0; ii < collidersCount; ii+=2) + { + uint cA = (uint)colliders[ii]; + uint cB = (uint)colliders[ii+1]; + SendCollision(cA, cB); + SendCollision(cB, cA); + } + } + + // if any of the objects had updated properties, they are returned in the updatedEntity structure + // TODO: figure out how to pass all of the EntityProperties structures in one marshal call. + if (updatedEntityCount > 0) + { + updatedEntities = new IntPtr[updatedEntityCount]; + // fetch all the pointers to all the EntityProperties structures for these updates + Marshal.Copy(updatedEntitiesPtr, updatedEntities, 0, updatedEntityCount); + for (int ii = 0; ii < updatedEntityCount; ii++) + { + IntPtr updatePointer = updatedEntities[ii]; + EntityProperties entprop = (EntityProperties)Marshal.PtrToStructure(updatePointer, typeof(EntityProperties)); + // m_log.DebugFormat("{0}: entprop: id={1}, pos={2}", LogHeader, entprop.ID, entprop.Position); + BSCharacter actor; + if (m_avatars.TryGetValue(entprop.ID, out actor)) + { + actor.UpdateProperties(entprop); + continue; + } + BSPrim prim; + if (m_prims.TryGetValue(entprop.ID, out prim)) + { + prim.UpdateProperties(entprop); + } + } + } + + // fps calculation wrong. This calculation returns about 1 in normal operation. + return timeStep / (numSubSteps * m_fixedTimeStep) * 1000f; + } + + // Something has collided + private void SendCollision(uint localID, uint collidingWith) + { + if (localID == TERRAIN_ID || localID == GROUNDPLANE_ID) + { + // we never send collisions to the terrain + return; + } + + ActorTypes type = ActorTypes.Prim; + if (collidingWith == TERRAIN_ID || collidingWith == GROUNDPLANE_ID) + type = ActorTypes.Ground; + else if (m_avatars.ContainsKey(collidingWith)) + type = ActorTypes.Agent; + + BSPrim prim; + if (m_prims.TryGetValue(localID, out prim)) { + prim.Collide(collidingWith, type, Vector3.Zero, Vector3.UnitZ, 0.01f); + return; + } + BSCharacter actor; + if (m_avatars.TryGetValue(localID, out actor)) { + actor.Collide(collidingWith, type, Vector3.Zero, Vector3.UnitZ, 0.01f); + return; + } + return; + } + + public override void GetResults() { } + + public override void SetTerrain(float[] heightMap) { + m_log.DebugFormat("{0}: SetTerrain", LogHeader); + m_heightMap = heightMap; + this.TaintedObject(delegate() + { + BulletSimAPI.SetHeightmap(m_worldID, m_heightMap); + }); + } + + public float GetTerrainHeightAtXY(float tX, float tY) + { + return m_heightMap[((int)tX) * Constants.RegionSize + ((int)tY)]; + } + + public override void SetWaterLevel(float baseheight) + { + m_waterLevel = baseheight; + } + public float GetWaterLevel() + { + return m_waterLevel; + } + + public override void DeleteTerrain() + { + m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader); + } + + public override void Dispose() + { + m_log.DebugFormat("{0}: Dispose()", LogHeader); + } + + public override Dictionary GetTopColliders() + { + return new Dictionary(); + } + + public override bool IsThreaded { get { return false; } } + + /// + /// Routine to figure out if we need to mesh this prim with our mesher + /// + /// + /// true if the prim needs meshing + public bool NeedsMeshing(PrimitiveBaseShape pbs) + { + // most of this is redundant now as the mesher will return null if it cant mesh a prim + // but we still need to check for sculptie meshing being enabled so this is the most + // convenient place to do it for now... + + // int iPropertiesNotSupportedDefault = 0; + + if (pbs.SculptEntry && !_meshSculptedPrim) + { + // m_log.DebugFormat("{0}: NeedsMeshing: scultpy mesh", LogHeader); + return false; + } + + // if it's a standard box or sphere with no cuts, hollows, twist or top shear, return false since Bullet + // can use an internal representation for the prim + if (!_forceSimplePrimMeshing) + { + // m_log.DebugFormat("{0}: NeedsMeshing: simple mesh: profshape={1}, curve={2}", LogHeader, pbs.ProfileShape, pbs.PathCurve); + if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) + || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 + && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)) + { + + if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 + && pbs.ProfileHollow == 0 + && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0 + && pbs.PathBegin == 0 && pbs.PathEnd == 0 + && pbs.PathTaperX == 0 && pbs.PathTaperY == 0 + && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 + && pbs.PathShearX == 0 && pbs.PathShearY == 0) + { + return false; + } + } + } + + /* TODO: verify that the mesher will now do all these shapes + if (pbs.ProfileHollow != 0) + iPropertiesNotSupportedDefault++; + + if ((pbs.PathBegin != 0) || pbs.PathEnd != 0) + iPropertiesNotSupportedDefault++; + + if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0)) + iPropertiesNotSupportedDefault++; + + if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0) + iPropertiesNotSupportedDefault++; + + if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100)) + iPropertiesNotSupportedDefault++; + + if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0)) + iPropertiesNotSupportedDefault++; + + if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight) + iPropertiesNotSupportedDefault++; + + if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 && (pbs.Scale.X != pbs.Scale.Y || pbs.Scale.Y != pbs.Scale.Z || pbs.Scale.Z != pbs.Scale.X)) + iPropertiesNotSupportedDefault++; + + if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1) + iPropertiesNotSupportedDefault++; + + // test for torus + if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square) + { + if (pbs.PathCurve == (byte)Extrusion.Curve1) + { + iPropertiesNotSupportedDefault++; + } + } + else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle) + { + if (pbs.PathCurve == (byte)Extrusion.Straight) + { + iPropertiesNotSupportedDefault++; + } + // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits + else if (pbs.PathCurve == (byte)Extrusion.Curve1) + { + iPropertiesNotSupportedDefault++; + } + } + else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) + { + if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2) + { + iPropertiesNotSupportedDefault++; + } + } + else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle) + { + if (pbs.PathCurve == (byte)Extrusion.Straight) + { + iPropertiesNotSupportedDefault++; + } + else if (pbs.PathCurve == (byte)Extrusion.Curve1) + { + iPropertiesNotSupportedDefault++; + } + } + if (iPropertiesNotSupportedDefault == 0) + { + return false; + } + */ + return true; + } + + // The calls to the PhysicsActors can't directly call into the physics engine + // because it might be busy. We we delay changes to a known time. + // We rely on C#'s closure to save and restore the context for the delegate. + public void TaintedObject(TaintCallback callback) + { + lock (_taintLock) + _taintedObjects.Add(callback); + return; + } + + // When someone tries to change a property on a BSPrim or BSCharacter, the object queues + // a callback into itself to do the actual property change. That callback is called + // here just before the physics engine is called to step the simulation. + public void ProcessTaints() + { + if (_taintedObjects.Count > 0) // save allocating new list if there is nothing to process + { + // swizzle a new list into the list location so we can process what's there + List oldList; + lock (_taintLock) + { + oldList = _taintedObjects; + _taintedObjects = new List(); + } + + foreach (TaintCallback callback in oldList) + { + try + { + callback(); + } + catch (Exception e) + { + m_log.ErrorFormat("{0}: ProcessTaints: Exception: {1}", LogHeader, e); + } + } + oldList.Clear(); + } + } + + #region Vehicles + // Make so the scene will call this prim for vehicle actions each tick. + // Safe to call if prim is already in the vehicle list. + public void AddVehiclePrim(BSPrim vehicle) + { + lock (m_vehicles) + { + if (!m_vehicles.Contains(vehicle)) + { + m_vehicles.Add(vehicle); + } + } + } + + // Remove a prim from our list of vehicles. + // Safe to call if the prim is not in the vehicle list. + public void RemoveVehiclePrim(BSPrim vehicle) + { + lock (m_vehicles) + { + if (m_vehicles.Contains(vehicle)) + { + m_vehicles.Remove(vehicle); + } + } + } + + // Some prims have extra vehicle actions + // no locking because only called when physics engine is not busy + private void ProcessVehicles(float timeStep) + { + foreach (BSPrim prim in m_vehicles) + { + prim.StepVehicle(timeStep); + } + } + #endregion Vehicles +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs new file mode 100644 index 0000000..b2bc0d7 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs @@ -0,0 +1,186 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin { + +public struct ConvexHull +{ + Vector3 Offset; + int VertexCount; + Vector3[] Vertices; +} +public struct ShapeData +{ + public enum PhysicsShapeType + { + SHAPE_AVATAR = 0, + SHAPE_BOX = 1, + SHAPE_CONE = 2, + SHAPE_CYLINDER = 3, + SHAPE_SPHERE = 4, + SHAPE_HULL = 5 + }; + public const int numericTrue = 1; + public const int numericFalse = 0; + public uint ID; + public PhysicsShapeType Type; + public Vector3 Position; + public Quaternion Rotation; + public Vector3 Velocity; + public Vector3 Scale; + public float Mass; + public float Buoyancy; + public System.UInt64 MeshKey; + public int Collidable; + public float Friction; + public int Static; // true if a static object. Otherwise gravity, etc. + // note that bools are passed as ints since bool size changes by language +} +public struct SweepHit +{ + public uint ID; + public float Fraction; + public Vector3 Normal; + public Vector3 Point; +} +public struct RaycastHit +{ + public uint ID; + public float Fraction; + public Vector3 Normal; +} + +public struct EntityProperties +{ + public uint ID; + public Vector3 Position; + public Quaternion Rotation; + public Vector3 Velocity; + public Vector3 Acceleration; + public Vector3 AngularVelocity; +} + +static class BulletSimAPI { + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern uint Initialize(Vector3 maxPosition); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetHeightmap(uint worldID, [MarshalAs(UnmanagedType.LPArray)] float[] heightMap); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void Shutdown(uint worldID); + + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern int PhysicsStep(uint worldID, float timeStep, int maxSubSteps, float fixedTimeStep, + out int updatedEntityCount, + out IntPtr updatedEntitiesPtr, + out int collidersCount, + out IntPtr collidersPtr); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool CreateHull(uint worldID, System.UInt64 meshKey, int hullCount, + [MarshalAs(UnmanagedType.LPArray)] float[] hulls + ); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool DestroyHull(uint worldID, System.UInt64 meshKey); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool CreateObject(uint worldID, ShapeData shapeData); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void CreateLinkset(uint worldID, int objectCount, ShapeData[] shapeDatas); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void AddConstraint(uint worldID, uint id1, uint id2, + Vector3 frame1, Vector3 frame2, Vector3 lowLinear, Vector3 hiLinear, Vector3 lowAngular, Vector3 hiAngular); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool RemoveConstraint(uint worldID, uint id1, uint id2); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetObjectPosition(uint WorldID, uint id); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectTranslation(uint worldID, uint id, Vector3 position, Quaternion rotation); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectVelocity(uint worldID, uint id, Vector3 velocity); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectAngularVelocity(uint worldID, uint id, Vector3 angularVelocity); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectForce(uint worldID, uint id, Vector3 force); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectScaleMass(uint worldID, uint id, Vector3 scale, float mass, bool isDynamic); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectCollidable(uint worldID, uint id, bool phantom); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectDynamic(uint worldID, uint id, bool isDynamic, float mass); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectGhost(uint worldID, uint id, bool ghostly); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectProperties(uint worldID, uint id, bool isStatic, bool isSolid, bool genCollisions, float mass); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectBuoyancy(uint worldID, uint id, float buoyancy); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool HasObject(uint worldID, uint id); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool DestroyObject(uint worldID, uint id); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern SweepHit ConvexSweepTest(uint worldID, uint id, Vector3 to, float extraMargin); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern RaycastHit RayTest(uint worldID, uint id, Vector3 from, Vector3 to); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 RecoverFromPenetration(uint worldID, uint id); + +// Log a debug message +[UnmanagedFunctionPointer(CallingConvention.Cdecl)] +public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg); +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetDebugLogCallback(DebugLogCallback callback); +} +} -- cgit v1.1