diff --git a/addon-modules/BulletSPlugin/BSCharacter.cs b/addon-modules/BulletSPlugin/BSCharacter.cs new file mode 100644 index 0000000000..4194a49142 --- /dev/null +++ b/addon-modules/BulletSPlugin/BSCharacter.cs @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2011 Intel Corporation + * + * 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 Intel Corporation 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 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; + _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.Flying = isFlying ? ShapeData.numericTrue : ShapeData.numericFalse; + shapeData.Dynamic = ShapeData.numericFalse; + + 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() + { + BulletSimAPI.SetObjectFlying(_scene.WorldID, LocalID, _flying); + }); + } + } + 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; + } + else + { + m_log.WarnFormat("{0}: Got a NaN force applied to a Character", LogHeader); + } + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); + }); + //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; + } + 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); + if (_subscribedEventsMs == 0) return; // don't want collisions + + // The following says we're colliding this simulation step + _collidingStep = _scene.SimulationStep; + if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID) + { + _collidingGroundStep = _scene.SimulationStep; + } + + 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/addon-modules/BulletSPlugin/BSPlugin.cs b/addon-modules/BulletSPlugin/BSPlugin.cs new file mode 100644 index 0000000000..0d53e8ed67 --- /dev/null +++ b/addon-modules/BulletSPlugin/BSPlugin.cs @@ -0,0 +1,67 @@ +/* + * Copyright (c) Intel Corporation + * + * 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 Intel Corporation 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/addon-modules/BulletSPlugin/BSPrim.cs b/addon-modules/BulletSPlugin/BSPrim.cs new file mode 100644 index 0000000000..1c251a00f9 --- /dev/null +++ b/addon-modules/BulletSPlugin/BSPrim.cs @@ -0,0 +1,1088 @@ +/* + * Copyright (c) Intel Corporation + * + * 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 Intel Corporation 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 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 List _childrenPrims; + private BSPrim _parentPrim; + + private int _subscribedEventsMs = 0; + long _collidingStep; + long _collidingGroundStep; + + 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; + _mesh = mesh; + _hullKey = 0; + _pbs = pbs; + _isPhysical = pisPhysical; + _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 + _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); + _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; + m_log.DebugFormat("{0}: Size={1}", LogHeader, _size); + _scene.TaintedObject(delegate() + { + if (_isPhysical) _mass = CalculateMass(); // changing size changes the mass + BulletSimAPI.SetObjectScaleMass(_scene.WorldID, _localID, _scale, _mass, _isPhysical); + RecreateGeomAndObject(); + // SyncUpdated = true; + }); + } + } + public override PrimitiveBaseShape Shape { + set { + _pbs = value; + m_log.DebugFormat("{0}: set Shape", LogHeader); + _scene.TaintedObject(delegate() + { + if (_isPhysical) _mass = CalculateMass(); // changing the shape changes the mass + RecreateGeomAndObject(); + // SyncUpdated = true; + }); + } + } + public override uint LocalID { + set { _localID = value; } + get { return _localID; } + } + public override bool Grabbed { + set { _grabbed = value; + m_log.DebugFormat("{0}: Grabbed={1}", LogHeader, _grabbed); + } + } + public override bool Selected { + set { + _isSelected = value; + _scene.TaintedObject(delegate() + { + m_log.DebugFormat("{0}: Selected={1}, localID={2}", LogHeader, _isSelected, _localID); + SetObjectDynamic(); + // SyncUpdated = true; + }); + } + } + 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} to {2}", LogHeader, _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 + m_log.DebugFormat("{0}: delink {1}", LogHeader, _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 { + // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); + return _position; + } + set { + _position = value; + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); + // SyncUpdated = true; + }); + } + } + public override float Mass { + get { return _mass; } + } + public override OMV.Vector3 Force { + get { return _force; } + set { + _force = value; + m_log.DebugFormat("{0}: set Force. f={1}", LogHeader, _force); + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); + // SyncUpdated = true; + }); + } + } + + public override int VehicleType { + get { return 0; } + set { return; } + } + public override void VehicleFloatParam(int param, float value) { } + public override void VehicleVectorParam(int param, OMV.Vector3 value) {} + public override void VehicleRotationParam(int param, OMV.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 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; + // SyncUpdated = true; + } + } + public override OMV.Vector3 Torque { + get { return _torque; } + set { _torque = value; + // SyncUpdated = true; + } + } + public override float CollisionScore { + get { return _collisionScore; } + set { _collisionScore = value; + // SyncUpdated = true; + } + } + 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); + // SyncUpdated = true; + }); + } + } + public override int PhysicsActorType { + get { return _physicsActorType; } + set { _physicsActorType = value; + // SyncUpdated = true; + } + } + public override bool IsPhysical { + get { return _isPhysical; } + set { + _isPhysical = value; + _scene.TaintedObject(delegate() + { + SetObjectDynamic(); + m_log.DebugFormat("{0}: ID={1}, IsPhysical={2}, IsSelected={3}, mass={4}", LogHeader, _localID, _isPhysical, _isSelected, _mass); + // SyncUpdated = true; + }); + } + } + // make gravity work if the object is physical and not selected + // no locking here because only called when it is safe + private void SetObjectDynamic() + { + // a selected object is not physical + if (_isSelected || !_isPhysical) + { + _mass = 0f; // non-physical things work best with a mass of zero + BulletSimAPI.SetObjectDynamic(_scene.WorldID, _localID, false, _mass); + } + else + { + _mass = CalculateMass(); + BulletSimAPI.SetObjectDynamic(_scene.WorldID, _localID, true, _mass); + } + } + public override bool Flying { + get { return _flying; } + set { + _flying = value; + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectFlying(_scene.WorldID, LocalID, _flying); + // SyncUpdated = true; + }); + } + } + 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 _isColliding; } + set { _isColliding = value; } + } + public override bool CollidingGround { + get { return _collidingGround; } + 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; } + } + + // 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; + } + 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. key={1}, hulls={2}", LogHeader, _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 + // 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. + m_log.DebugFormat("{0}: CreateLinkset", LogHeader); + int totalPrimsInLinkset = _childrenPrims.Count + 1; + 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); + } + else + { + // simple object + 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.MeshKey = _hullKey; + shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse; + shape.Flying = _flying ? ShapeData.numericTrue : ShapeData.numericFalse; + shape.Friction = _friction; + shape.Dynamic = _isPhysical ? ShapeData.numericTrue : ShapeData.numericFalse; + } + + // 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() + 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; + // m_log.DebugFormat("{0}: UpdateProperties: position = {1}", LogHeader, _position); + changed = true; + } + if (_orientation != entprop.Rotation) + { + _orientation = entprop.Rotation; + // m_log.DebugFormat("{0}: UpdateProperties: rotation = {1}", LogHeader, _orientation); + changed = true; + } + if (_velocity != entprop.Velocity) + { + _velocity = entprop.Velocity; + // m_log.DebugFormat("{0}: UpdateProperties: velocity = {1}", LogHeader, _velocity); + changed = true; + } + if (_acceleration != entprop.Acceleration) + { + _acceleration = entprop.Acceleration; + // m_log.DebugFormat("{0}: UpdateProperties: acceleration = {1}", LogHeader, _acceleration); + changed = true; + } + if (_rotationalVelocity != entprop.AngularVelocity) + { + _rotationalVelocity = entprop.AngularVelocity; + // m_log.DebugFormat("{0}: UpdateProperties: rotationalVelocity = {1}", LogHeader, _rotationalVelocity); + changed = true; + } + if (changed) + { + 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); + if (_subscribedEventsMs == 0) return; // nothing in the object is waiting for collision events + + // The following makes it so we can sense we're colliding this simulation step + _collidingStep = _scene.SimulationStep; + if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID) + { + _collidingGroundStep = _scene.SimulationStep; + } + + // 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/addon-modules/BulletSPlugin/BSScene.cs b/addon-modules/BulletSPlugin/BSScene.cs new file mode 100644 index 0000000000..de7a9ce754 --- /dev/null +++ b/addon-modules/BulletSPlugin/BSScene.cs @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2011 Intel Corporation + * + * 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 Intel Corporation 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 (both BSScene and BSPrim) +// Does NeedsMeshing() really need to exclude all the different shapes? +// 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) +// Need three states for objects: sense and report collisions, have physical effects, affects other objects +// LinkSets +// Freeing of memory of linksets in BulletSim::DestroyObject +// Should prim.link() and prim.delink() be done at taint time? +// Pass collision enable flags to BulletSim code so collisions are not reported up unless they are really needed +// Set child prims phantom since the physicality is handled by the parent prim +// Linked children need rotation relative to parent (passed as world rotation) +// +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 float[] m_heightMap; + 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 delegate void TaintCallback(); + private List _taintedObjects; + + 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); + } + } + // 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, 1000f)); + } + + 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. + // m_log.DebugFormat("{0}: AddPrimShape2: creating mesh", LogHeader); + 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; + } + + public override void AddPhysicsActorTaint(PhysicsActor prim) { } + + 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(); + + // 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 + if (updatedEntityCount > 0) + { + updatedEntities = new IntPtr[updatedEntityCount]; + 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); + if (m_avatars.ContainsKey(entprop.ID)) + { + BSCharacter actor = m_avatars[entprop.ID]; + actor.UpdateProperties(entprop); + } + if (m_prims.ContainsKey(entprop.ID)) + { + BSPrim prim = m_prims[entprop.ID]; + prim.UpdateProperties(entprop); + } + } + } + + return 11f; // returns frames per second + } + + // 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 (m_avatars.ContainsKey(collidingWith)) + type = ActorTypes.Agent; + else if (collidingWith == TERRAIN_ID || collidingWith == GROUNDPLANE_ID) + type = ActorTypes.Ground; + + if (m_prims.ContainsKey(localID)) + { + BSPrim prim = m_prims[localID]; + prim.Collide(collidingWith, type, Vector3.Zero, Vector3.UnitZ, 0.01f); + return; + } + if (m_avatars.ContainsKey(localID)) + { + BSCharacter actor = m_avatars[localID]; + 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 override void SetWaterLevel(float baseheight) { } + + 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) + { + // do we need to lock? + _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() + { + // swizzle a new list into the list location so we can process what's there + List newList = new List(); + List oldList = (List)Interlocked.Exchange(ref _taintedObjects, newList); + + foreach (TaintCallback callback in oldList) + { + try + { + callback(); + } + catch (Exception e) + { + m_log.ErrorFormat("{0}: ProcessTaints: Exception: {1}", LogHeader, e); + } + } + oldList.Clear(); + } +} +} diff --git a/addon-modules/BulletSPlugin/BulletSimAPI.cs b/addon-modules/BulletSPlugin/BulletSimAPI.cs new file mode 100644 index 0000000000..5487e8e07c --- /dev/null +++ b/addon-modules/BulletSPlugin/BulletSimAPI.cs @@ -0,0 +1,171 @@ +/* +Copyright (c) 2011, Intel Corporation +All rights reserved. + +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 Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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 System.UInt64 MeshKey; + public int Collidable; + public int Flying; + public float Friction; + public int Dynamic; + // 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 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 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 SetObjectFlying(uint worldID, uint id, bool flying); + +[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); +} +} diff --git a/addon-modules/BulletSPlugin/prebuild.xml b/addon-modules/BulletSPlugin/prebuild.xml new file mode 100644 index 0000000000..47ed1a1069 --- /dev/null +++ b/addon-modules/BulletSPlugin/prebuild.xml @@ -0,0 +1,32 @@ + + + + ../../bin/Physics/ + + + + + ../../bin/Physics/ + + + + ../../bin/ + + + + + + + + + + + + + + + + + + +