diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs index 31989e50ad..4844336e31 100644 --- a/OpenSim/Framework/Monitoring/StatsManager.cs +++ b/OpenSim/Framework/Monitoring/StatsManager.cs @@ -207,7 +207,7 @@ namespace OpenSim.Framework.Monitoring return false; newContainer = new Dictionary(container); - newContainer.Remove(stat.UniqueName); + newContainer.Remove(stat.ShortName); newCategory = new Dictionary>(category); newCategory.Remove(stat.Container); @@ -248,6 +248,19 @@ namespace OpenSim.Framework.Monitoring } } + /// + /// Stat type. + /// + /// + /// A push stat is one which is continually updated and so it's value can simply by read. + /// A pull stat is one where reading the value triggers a collection method - the stat is not continually updated. + /// + public enum StatType + { + Push, + Pull + } + /// /// Verbosity of stat. /// @@ -265,11 +278,6 @@ namespace OpenSim.Framework.Monitoring /// public class Stat { - /// - /// Unique stat name used for indexing. Each ShortName in a Category must be unique. - /// - public string UniqueName { get; private set; } - /// /// Category of this stat (e.g. cache, scene, etc). /// @@ -285,29 +293,65 @@ namespace OpenSim.Framework.Monitoring /// public string Container { get; private set; } + public StatType StatType { get; private set; } + + /// + /// Action used to update this stat when the value is requested if it's a pull type. + /// + public Action PullAction { get; private set; } + public StatVerbosity Verbosity { get; private set; } public string ShortName { get; private set; } public string Name { get; private set; } public string Description { get; private set; } public virtual string UnitName { get; private set; } - public virtual double Value { get; set; } + public virtual double Value + { + get + { + // Asking for an update here means that the updater cannot access this value without infinite recursion. + // XXX: A slightly messy but simple solution may be to flick a flag so we can tell if this is being + // called by the pull action and just return the value. + if (StatType == StatType.Pull) + PullAction(this); + + return m_value; + } + + set + { + m_value = value; + } + } + + private double m_value; /// /// Constructor /// /// Short name for the stat. Must not contain spaces. e.g. "LongFrames" /// Human readable name for the stat. e.g. "Long frames" + /// Description of stat /// /// Unit name for the stat. Should be preceeded by a space if the unit name isn't normally appeneded immediately to the value. /// e.g. " frames" /// /// Category under which this stat should appear, e.g. "scene". Do not capitalize. /// Entity to which this stat relates. e.g. scene name if this is a per scene stat. + /// Push or pull + /// Pull stats need an action to update the stat on request. Push stats should set null here. /// Verbosity of stat. Controls whether it will appear in short stat display or only full display. - /// Description of stat public Stat( - string shortName, string name, string unitName, string category, string container, StatVerbosity verbosity, string description) + string shortName, + string name, + string description, + string unitName, + string category, + string container, + StatType type, + Action pullAction, + StatVerbosity verbosity) { if (StatsManager.SubCommands.Contains(category)) throw new Exception( @@ -315,18 +359,18 @@ namespace OpenSim.Framework.Monitoring ShortName = shortName; Name = name; + Description = description; UnitName = unitName; Category = category; Container = container; + StatType = type; + + if (StatType == StatType.Push && pullAction != null) + throw new Exception("A push stat cannot have a pull action"); + else + PullAction = pullAction; + Verbosity = verbosity; - Description = description; - - UniqueName = GenUniqueName(Container, Category, ShortName); - } - - public static string GenUniqueName(string container, string category, string shortName) - { - return string.Format("{0}+{1}+{2}", container, category, shortName); } public virtual string ToConsoleString() @@ -361,8 +405,15 @@ namespace OpenSim.Framework.Monitoring } public PercentageStat( - string shortName, string name, string category, string container, StatVerbosity verbosity, string description) - : base(shortName, name, "%", category, container, verbosity, description) {} + string shortName, + string name, + string description, + string category, + string container, + StatType type, + Action pullAction, + StatVerbosity verbosity) + : base(shortName, name, description, "%", category, container, type, pullAction, verbosity) {} public override string ToConsoleString() { diff --git a/OpenSim/Framework/Pool.cs b/OpenSim/Framework/Pool.cs index 1ca06c3065..5484f5c2d5 100644 --- a/OpenSim/Framework/Pool.cs +++ b/OpenSim/Framework/Pool.cs @@ -38,8 +38,23 @@ namespace OpenSim.Framework /// public class Pool { + /// + /// Number of objects in the pool. + /// + public int Count + { + get + { + lock (m_pool) + return m_pool.Count; + } + } + private Stack m_pool; + /// + /// Maximum pool size. Beyond this, any returned objects are not pooled. + /// private int m_maxPoolSize; private Func m_createFunction; diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs index b3e31a6283..3198891a30 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs @@ -543,11 +543,8 @@ namespace OpenSim.Framework.Servers.HttpServer { case null: case "text/html": - if (DebugLevel >= 3) - m_log.DebugFormat( - "[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}", - RequestNumber, Port, request.ContentType, request.HttpMethod, request.Url.PathAndQuery, request.RemoteIPEndPoint); + LogIncomingToContentTypeHandler(request); buffer = HandleHTTPRequest(request, response); break; @@ -555,11 +552,8 @@ namespace OpenSim.Framework.Servers.HttpServer case "application/llsd+xml": case "application/xml+llsd": case "application/llsd+json": - if (DebugLevel >= 3) - m_log.DebugFormat( - "[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}", - RequestNumber, Port, request.ContentType, request.HttpMethod, request.Url.PathAndQuery, request.RemoteIPEndPoint); + LogIncomingToContentTypeHandler(request); buffer = HandleLLSDRequests(request, response); break; @@ -694,7 +688,7 @@ namespace OpenSim.Framework.Servers.HttpServer "[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}", RequestNumber, Port, - request.ContentType, + (request.ContentType == null || request.ContentType == "") ? "not set" : request.ContentType, request.HttpMethod, request.Url.PathAndQuery, request.RemoteIPEndPoint); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index df4bbb3785..b8951d966c 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -173,6 +173,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private ExpiringCache> m_pendingCache = new ExpiringCache>(); private Pool m_incomingPacketPool; + private Stat m_incomingPacketPoolStat; + private int m_defaultRTO = 0; private int m_maxRTO = 0; private int m_ackTimeout = 0; @@ -217,6 +219,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_circuitManager = circuitManager; int sceneThrottleBps = 0; + bool usePools = false; IConfig config = configSource.Configs["ClientStack.LindenUDP"]; if (config != null) @@ -249,7 +252,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true); PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true); - UsePools = packetConfig.GetBoolean("RecycleBaseUDPPackets", false); + usePools = packetConfig.GetBoolean("RecycleBaseUDPPackets", usePools); } #region BinaryStats @@ -280,8 +283,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_throttle = new TokenBucket(null, sceneThrottleBps); ThrottleRates = new ThrottleRates(configSource); - if (UsePools) - m_incomingPacketPool = new Pool(() => new IncomingPacket(), 500); + if (usePools) + EnablePools(); } public void Start() @@ -334,6 +337,50 @@ namespace OpenSim.Region.ClientStack.LindenUDP base.StopInbound(); } + protected override bool EnablePools() + { + if (!UsePools) + { + base.EnablePools(); + + m_incomingPacketPool = new Pool(() => new IncomingPacket(), 500); + + m_incomingPacketPoolStat + = new Stat( + "IncomingPacketPoolCount", + "Objects within incoming packet pool", + "The number of objects currently stored within the incoming packet pool", + "", + "clientstack", + "packetpool", + StatType.Pull, + stat => stat.Value = m_incomingPacketPool.Count, + StatVerbosity.Debug); + + StatsManager.RegisterStat(m_incomingPacketPoolStat); + + return true; + } + + return false; + } + + protected override bool DisablePools() + { + if (UsePools) + { + base.DisablePools(); + + StatsManager.DeregisterStat(m_incomingPacketPoolStat); + + // We won't null out the pool to avoid a race condition with code that may be in the middle of using it. + + return true; + } + + return false; + } + /// /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging. /// @@ -397,6 +444,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP + "all - stop in and outbound processing.\n", HandleStopCommand); + MainConsole.Instance.Commands.AddCommand( + "Debug", + false, + "debug lludp pool", + "debug lludp pool ", + "Turn object pooling within the lludp component on or off.", + HandlePoolCommand); + MainConsole.Instance.Commands.AddCommand( "Debug", false, @@ -440,6 +495,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP StopOutbound(); } + private void HandlePoolCommand(string module, string[] args) + { + if (args.Length != 4) + { + MainConsole.Instance.Output("Usage: debug lludp pool "); + return; + } + + string enabled = args[3]; + + if (enabled == "on") + { + if (EnablePools()) + MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", m_scene.Name); + } + else if (enabled == "off") + { + if (DisablePools()) + MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", m_scene.Name); + } + else + { + MainConsole.Instance.Output("Usage: debug lludp pool "); + } + } + private void HandleStatusCommand(string module, string[] args) { MainConsole.Instance.OutputFormat( @@ -447,6 +528,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP MainConsole.Instance.OutputFormat( "OUT LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningOutbound ? "enabled" : "disabled"); + + MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", m_scene.Name, UsePools ? "on" : "off"); } public bool HandlesRegion(Location x) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs index e7d8a30b00..8bd346149e 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs @@ -31,6 +31,7 @@ using System.Net.Sockets; using System.Threading; using log4net; using OpenSim.Framework; +using OpenSim.Framework.Monitoring; namespace OpenMetaverse { @@ -76,6 +77,8 @@ namespace OpenMetaverse /// If IsRunningOut = false, then any request to send a packet is simply dropped. public bool IsRunningOutbound { get; private set; } + private Stat m_poolCountStat; + /// /// Default constructor /// @@ -106,11 +109,6 @@ namespace OpenMetaverse /// necessary public void StartInbound(int recvBufferSize, bool asyncPacketHandling) { - if (UsePools) - m_pool = new Pool(() => new UDPPacketBuffer(), 500); - else - m_pool = null; - m_asyncPacketHandling = asyncPacketHandling; if (!IsRunningInbound) @@ -176,6 +174,49 @@ namespace OpenMetaverse IsRunningOutbound = false; } + protected virtual bool EnablePools() + { + if (!UsePools) + { + m_pool = new Pool(() => new UDPPacketBuffer(), 500); + + m_poolCountStat + = new Stat( + "UDPPacketBufferPoolCount", + "Objects within the UDPPacketBuffer pool", + "The number of objects currently stored within the UDPPacketBuffer pool", + "", + "clientstack", + "packetpool", + StatType.Pull, + stat => stat.Value = m_pool.Count, + StatVerbosity.Debug); + + StatsManager.RegisterStat(m_poolCountStat); + + UsePools = true; + + return true; + } + + return false; + } + + protected virtual bool DisablePools() + { + if (UsePools) + { + UsePools = false; + StatsManager.DeregisterStat(m_poolCountStat); + + // We won't null out the pool to avoid a race condition with code that may be in the middle of using it. + + return true; + } + + return false; + } + private void AsyncBeginReceive() { UDPPacketBuffer buf; diff --git a/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs b/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs index 2a3d14f69f..9f22fb48ec 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs @@ -47,18 +47,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP private PercentageStat m_packetsReusedStat = new PercentageStat( "PacketsReused", "Packets reused", + "Number of packets reused out of all requests to the packet pool", "clientstack", "packetpool", - StatVerbosity.Debug, - "Number of packets reused out of all requests to the packet pool"); + StatType.Push, + null, + StatVerbosity.Debug); private PercentageStat m_blocksReusedStat = new PercentageStat( - "BlocksReused", - "Blocks reused", + "PacketDataBlocksReused", + "Packet data blocks reused", + "Number of data blocks reused out of all requests to the packet pool", "clientstack", "packetpool", - StatVerbosity.Debug, - "Number of data blocks reused out of all requests to the packet pool"); + StatType.Push, + null, + StatVerbosity.Debug); /// /// Pool of packets available for reuse. @@ -88,6 +92,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP { StatsManager.RegisterStat(m_packetsReusedStat); StatsManager.RegisterStat(m_blocksReusedStat); + + StatsManager.RegisterStat( + new Stat( + "PacketsPoolCount", + "Objects within the packet pool", + "The number of objects currently stored within the packet pool", + "", + "clientstack", + "packetpool", + StatType.Pull, + stat => { lock (pool) { stat.Value = pool.Count; } }, + StatVerbosity.Debug)); + + StatsManager.RegisterStat( + new Stat( + "PacketDataBlocksPoolCount", + "Objects within the packet data block pool", + "The number of objects currently stored within the packet data block pool", + "", + "clientstack", + "packetpool", + StatType.Pull, + stat => { lock (DataBlocks) { stat.Value = DataBlocks.Count; } }, + StatVerbosity.Debug)); } /// diff --git a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs index 0d359b9117..5398ab9816 100644 --- a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs +++ b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs @@ -246,11 +246,13 @@ namespace OpenSim.Region.Framework.Scenes = new Stat( "SlowFrames", "Slow Frames", + "Number of frames where frame time has been significantly longer than the desired frame time.", " frames", "scene", m_scene.Name, - StatVerbosity.Info, - "Number of frames where frame time has been significantly longer than the desired frame time."); + StatType.Push, + null, + StatVerbosity.Info); StatsManager.RegisterStat(SlowFramesStat); } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index 623ac8ff16..07dd6137bf 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -165,7 +165,7 @@ public class BSCharacter : BSPhysObject // Do this after the object has been added to the world BulletSimAPI.SetCollisionFilterMask2(BSBody.ptr, - (uint)CollisionFilterGroups.AvatarFilter, + (uint)CollisionFilterGroups.AvatarFilter, (uint)CollisionFilterGroups.AvatarMask); } @@ -269,7 +269,7 @@ public class BSCharacter : BSPhysObject private bool PositionSanityCheck() { bool ret = false; - + // If below the ground, move the avatar up float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); if (Position.Z < terrainHeight) @@ -413,7 +413,7 @@ public class BSCharacter : BSPhysObject }); } } - // Go directly to Bullet to get/set the value. + // Go directly to Bullet to get/set the value. public override OMV.Quaternion ForceOrientation { get @@ -478,7 +478,7 @@ public class BSCharacter : BSPhysObject set { _collidingObj = value; } } public override bool FloatOnWater { - set { + set { _floatOnWater = value; PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate() { @@ -588,7 +588,7 @@ public class BSCharacter : BSPhysObject newScale.X = PhysicsScene.Params.avatarCapsuleRadius; newScale.Y = PhysicsScene.Params.avatarCapsuleRadius; - // From the total height, remote the capsule half spheres that are at each end + // From the total height, remote the capsule half spheres that are at each end newScale.Z = (size.Z * 2f) - Math.Min(newScale.X, newScale.Y); // newScale.Z = (size.Z * 2f); Scale = newScale; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs index a20be3a285..f017cdd669 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs @@ -34,6 +34,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin public abstract class BSConstraint : IDisposable { + private static string LogHeader = "[BULLETSIM CONSTRAINT]"; + protected BulletSim m_world; protected BulletBody m_body1; protected BulletBody m_body2; @@ -53,7 +55,7 @@ public abstract class BSConstraint : IDisposable { bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr); m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}", - BSScene.DetailLogZero, + BSScene.DetailLogZero, m_body1.ID, m_body1.ptr.ToString("X"), m_body2.ID, m_body2.ptr.ToString("X"), success); @@ -124,7 +126,7 @@ public abstract class BSConstraint : IDisposable } else { - m_world.physicsScene.Logger.ErrorFormat("[BULLETSIM CONSTRAINT] CalculateTransforms failed. A={0}, B={1}", Body1.ID, Body2.ID); + m_world.physicsScene.Logger.ErrorFormat("{0} CalculateTransforms failed. A={1}, B={2}", LogHeader, Body1.ID, Body2.ID); } } return ret; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index 56342b88f8..117c8787d1 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -23,7 +23,7 @@ * 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. @@ -352,7 +352,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // m_bankingMix = 1; // m_bankingTimescale = 1; // m_referenceFrame = Quaternion.Identity; - m_flags |= (VehicleFlag.NO_DEFLECTION_UP + m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.LIMIT_MOTOR_UP); m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT); @@ -382,7 +382,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // m_bankingTimescale = 1; // m_referenceFrame = Quaternion.Identity; m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY - | VehicleFlag.HOVER_GLOBAL_HEIGHT + | VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.HOVER_UP_ONLY); m_flags |= (VehicleFlag.NO_DEFLECTION_UP @@ -458,14 +458,15 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Do any updating needed for a vehicle public void Refresh() { - if (!IsActive) + if (!IsActive) return; // Set the prim's inertia to zero. The vehicle code handles that and this // removes the motion and torque actions introduced by Bullet. Vector3 inertia = Vector3.Zero; - BulletSimAPI.SetMassProps2(Prim.BSBody.ptr, Prim.MassRaw, inertia); - BulletSimAPI.UpdateInertiaTensor2(Prim.BSBody.ptr); + // comment out for DEBUG test + // BulletSimAPI.SetMassProps2(Prim.BSBody.ptr, Prim.MassRaw, inertia); + // BulletSimAPI.UpdateInertiaTensor2(Prim.BSBody.ptr); } // One step of the vehicle properties for the next 'pTimestep' seconds. @@ -791,7 +792,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Sum velocities m_lastAngularVelocity = m_angularMotorVelocity + vertattr; // + bank + deflection - + if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) { m_lastAngularVelocity.X = 0; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs index 43b12623cc..c984824278 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs @@ -32,10 +32,27 @@ using OMV = OpenMetaverse; namespace OpenSim.Region.Physics.BulletSPlugin { -public class BSLinkset +public abstract class BSLinkset { // private static string LogHeader = "[BULLETSIM LINKSET]"; + // Create the correct type of linkset for this child + public static BSLinkset Factory(BSScene physScene, BSPhysObject parent) + { + BSLinkset ret = null; + /* + if (parent.IsPhysical) + ret = new BSLinksetConstraints(physScene, parent); + else + ret = new BSLinksetManual(physScene, parent); + */ + + // at the moment, there is only one + ret = new BSLinksetConstraints(physScene, parent); + + return ret; + } + public BSPhysObject LinksetRoot { get; protected set; } public BSScene PhysicsScene { get; private set; } @@ -52,16 +69,16 @@ public class BSLinkset // the physical 'taint' children separately. // After taint processing and before the simulation step, these // two lists must be the same. - private HashSet m_children; - private HashSet m_taintChildren; + protected HashSet m_children; + protected HashSet m_taintChildren; // We lock the diddling of linkset classes to prevent any badness. // This locks the modification of the instances of this class. Changes // to the physical representation is done via the tainting mechenism. - private object m_linksetActivityLock = new Object(); + protected object m_linksetActivityLock = new Object(); // We keep the prim's mass in the linkset structure since it could be dependent on other prims - private float m_mass; + protected float m_mass; public float LinksetMass { get @@ -81,7 +98,7 @@ public class BSLinkset get { return ComputeLinksetGeometricCenter(); } } - public BSLinkset(BSScene scene, BSPhysObject parent) + protected void Initialize(BSScene scene, BSPhysObject parent) { // A simple linkset of one (no children) LinksetID = m_nextLinksetID++; @@ -128,7 +145,7 @@ public class BSLinkset } // The child is down to a linkset of just itself - return new BSLinkset(PhysicsScene, child); + return BSLinkset.Factory(PhysicsScene, child); } // Return 'true' if the passed object is the root object of this linkset @@ -148,6 +165,9 @@ public class BSLinkset bool ret = false; lock (m_linksetActivityLock) { + if (m_children.Contains(child)) + ret = true; + /* foreach (BSPhysObject bp in m_children) { if (child.LocalID == bp.LocalID) @@ -156,6 +176,7 @@ public class BSLinkset break; } } + */ } return ret; } @@ -163,24 +184,7 @@ public class BSLinkset // When physical properties are changed the linkset needs to recalculate // its internal properties. // May be called at runtime or taint-time (just pass the appropriate flag). - public void Refresh(BSPhysObject requestor, bool inTaintTime) - { - // If there are no children, not physical or not root, I am not the one that recomputes the constraints - // (For the moment, static linksets do create constraints so remove the test for physical.) - if (!HasAnyChildren || /*!requestor.IsPhysical ||*/ !IsRoot(requestor)) - return; - - BSScene.TaintCallback refreshOperation = delegate() - { - RecomputeLinksetConstraintVariables(); - DetailLog("{0},BSLinkset.Refresh,complete,rBody={1}", - LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X")); - }; - if (inTaintTime) - refreshOperation(); - else - PhysicsScene.TaintedObject("BSLinkSet.Refresh", refreshOperation); - } + public abstract void Refresh(BSPhysObject requestor, bool inTaintTime); // The object is going dynamic (physical). Do any setup necessary // for a dynamic linkset. @@ -188,102 +192,35 @@ public class BSLinkset // has not yet been fully constructed. // Return 'true' if any properties updated on the passed object. // Called at taint-time! - public bool MakeDynamic(BSPhysObject child) - { - // What is done for each object in BSPrim is what we want. - return false; - } + public abstract bool MakeDynamic(BSPhysObject child); // The object is going static (non-physical). Do any setup necessary // for a static linkset. // Return 'true' if any properties updated on the passed object. // Called at taint-time! - public bool MakeStatic(BSPhysObject child) - { - // What is done for each object in BSPrim is what we want. - return false; - } + public abstract bool MakeStatic(BSPhysObject child); - // If the software is handling the movement of all the objects in a linkset - // (like if one doesn't use constraints for static linksets), this is called - // when an update for the root of the linkset is received. + // Called when a parameter update comes from the physics engine for any object + // of the linkset is received. // Called at taint-time!! - public void UpdateProperties(BSPhysObject physObject) - { - // The root local properties have been updated. Apply to the children if appropriate. - if (IsRoot(physObject) && HasAnyChildren) - { - if (!physObject.IsPhysical) - { - // TODO: implement software linkset update for static object linksets - } - } - } + public abstract void UpdateProperties(BSPhysObject physObject); // Routine used when rebuilding the body of the root of the linkset // Destroy all the constraints have have been made to root. // This is called when the root body is changing. - // Returns 'true' of something eas actually removed and would need restoring + // Returns 'true' of something was actually removed and would need restoring // Called at taint-time!! - public bool RemoveBodyDependencies(BSPrim child) - { - bool ret = false; - - lock (m_linksetActivityLock) - { - if (IsRoot(child)) - { - // If the one with the dependency is root, must undo all children - DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}", - child.LocalID, LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X")); - - ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot); - } - else - { - DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeSingleChild,rID={1},rBody={2},cID={3},cBody={4}", - child.LocalID, - LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"), - child.LocalID, child.BSBody.ptr.ToString("X")); - // ret = PhysicallyUnlinkAChildFromRoot(LinksetRoot, child); - // Despite the function name, this removes any link to the specified object. - ret = PhysicallyUnlinkAllChildrenFromRoot(child); - } - } - return ret; - } + public abstract bool RemoveBodyDependencies(BSPrim child); // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true', - // this routine will restore the removed constraints. + // this routine will restore the removed constraints. // Called at taint-time!! - public void RestoreBodyDependencies(BSPrim child) - { - lock (m_linksetActivityLock) - { - if (IsRoot(child)) - { - DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreChildrenForRoot,rID={1},numChild={2}", - child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count); - foreach (BSPhysObject bpo in m_taintChildren) - { - PhysicallyLinkAChildToRoot(LinksetRoot, bpo); - } - } - else - { - DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreSingleChild,rID={1},rBody={2},cID={3},cBody={4}", - LinksetRoot.LocalID, - LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"), - child.LocalID, child.BSBody.ptr.ToString("X")); - PhysicallyLinkAChildToRoot(LinksetRoot, child); - } - } - } + public abstract void RestoreBodyDependencies(BSPrim child); // ================================================================ // Below this point is internal magic - private float ComputeLinksetMass() + protected virtual float ComputeLinksetMass() { float mass; lock (m_linksetActivityLock) @@ -297,7 +234,7 @@ public class BSLinkset return mass; } - private OMV.Vector3 ComputeLinksetCenterOfMass() + protected virtual OMV.Vector3 ComputeLinksetCenterOfMass() { OMV.Vector3 com; lock (m_linksetActivityLock) @@ -317,7 +254,7 @@ public class BSLinkset return com; } - private OMV.Vector3 ComputeLinksetGeometricCenter() + protected virtual OMV.Vector3 ComputeLinksetGeometricCenter() { OMV.Vector3 com; lock (m_linksetActivityLock) @@ -336,236 +273,21 @@ public class BSLinkset // I am the root of a linkset and a new child is being added // Called while LinkActivity is locked. - private void AddChildToLinkset(BSPhysObject child) - { - if (!HasChild(child)) - { - m_children.Add(child); - - BSPhysObject rootx = LinksetRoot; // capture the root as of now - BSPhysObject childx = child; - - DetailLog("{0},AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); - - PhysicsScene.TaintedObject("AddChildToLinkset", delegate() - { - DetailLog("{0},AddChildToLinkset,taint,rID={1},rBody={2},cID={3},cBody={4}", - rootx.LocalID, - rootx.LocalID, rootx.BSBody.ptr.ToString("X"), - childx.LocalID, childx.BSBody.ptr.ToString("X")); - // Since this is taint-time, the body and shape could have changed for the child - rootx.ForcePosition = rootx.Position; // DEBUG - childx.ForcePosition = childx.Position; // DEBUG - PhysicallyLinkAChildToRoot(rootx, childx); - m_taintChildren.Add(child); - }); - } - return; - } + protected abstract void AddChildToLinkset(BSPhysObject child); // Forcefully removing a child from a linkset. // This is not being called by the child so we have to make sure the child doesn't think // it's still connected to the linkset. // Normal OpenSimulator operation will never do this because other SceneObjectPart information // also has to be updated (like pointer to prim's parent). - private void RemoveChildFromOtherLinkset(BSPhysObject pchild) - { - pchild.Linkset = new BSLinkset(PhysicsScene, pchild); - RemoveChildFromLinkset(pchild); - } + protected abstract void RemoveChildFromOtherLinkset(BSPhysObject pchild); // I am the root of a linkset and one of my children is being removed. // Safe to call even if the child is not really in my linkset. - private void RemoveChildFromLinkset(BSPhysObject child) - { - if (m_children.Remove(child)) - { - BSPhysObject rootx = LinksetRoot; // capture the root and body as of now - BSPhysObject childx = child; - - DetailLog("{0},RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", - childx.LocalID, - rootx.LocalID, rootx.BSBody.ptr.ToString("X"), - childx.LocalID, childx.BSBody.ptr.ToString("X")); - - PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate() - { - m_taintChildren.Remove(child); - PhysicallyUnlinkAChildFromRoot(rootx, childx); - RecomputeLinksetConstraintVariables(); - }); - - } - else - { - // This will happen if we remove the root of the linkset first. Non-fatal occurance. - // PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader); - } - return; - } - - // Create a constraint between me (root of linkset) and the passed prim (the child). - // Called at taint time! - private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim) - { - // Zero motion for children so they don't interpolate - childPrim.ZeroMotion(); - - // Relative position normalized to the root prim - // Essentually a vector pointing from center of rootPrim to center of childPrim - OMV.Vector3 childRelativePosition = childPrim.Position - rootPrim.Position; - - // real world coordinate of midpoint between the two objects - OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2); - - DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}", - rootPrim.LocalID, - rootPrim.LocalID, rootPrim.BSBody.ptr.ToString("X"), - childPrim.LocalID, childPrim.BSBody.ptr.ToString("X"), - rootPrim.Position, childPrim.Position, midPoint); - - // create a constraint that allows no freedom of movement between the two objects - // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 - - BS6DofConstraint constrain = new BS6DofConstraint( - PhysicsScene.World, rootPrim.BSBody, childPrim.BSBody, midPoint, true, true ); - - /* NOTE: below is an attempt to build constraint with full frame computation, etc. - * Using the midpoint is easier since it lets the Bullet code manipulate the transforms - * of the objects. - * Code left as a warning to future programmers. - // ================================================================================== - // relative position normalized to the root prim - OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation); - OMV.Vector3 childRelativePosition = (childPrim.Position - rootPrim.Position) * invThisOrientation; - - // relative rotation of the child to the parent - OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation; - OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation); - - // create a constraint that allows no freedom of movement between the two objects - // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 - DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID); - BS6DofConstraint constrain = new BS6DofConstraint( - PhysicsScene.World, rootPrim.Body, childPrim.Body, - OMV.Vector3.Zero, - OMV.Quaternion.Inverse(rootPrim.Orientation), - OMV.Vector3.Zero, - OMV.Quaternion.Inverse(childPrim.Orientation), - // A point half way between the parent and child - // childRelativePosition/2, - // childRelativeRotation, - // childRelativePosition/2, - // inverseChildRelativeRotation, - true, - true - ); - // ================================================================================== - */ - - PhysicsScene.Constraints.AddConstraint(constrain); - - // zero linear and angular limits makes the objects unable to move in relation to each other - constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); - constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); - - // tweek the constraint to increase stability - constrain.UseFrameOffset(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintUseFrameOffset)); - constrain.TranslationalLimitMotor(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintEnableTransMotor), - PhysicsScene.Params.linkConstraintTransMotorMaxVel, - PhysicsScene.Params.linkConstraintTransMotorMaxForce); - constrain.SetCFMAndERP(PhysicsScene.Params.linkConstraintCFM, PhysicsScene.Params.linkConstraintERP); - if (PhysicsScene.Params.linkConstraintSolverIterations != 0f) - { - constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations); - } - } - - // Remove linkage between myself and a particular child - // The root and child bodies are passed in because we need to remove the constraint between - // the bodies that were at unlink time. - // Called at taint time! - private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim) - { - bool ret = false; - DetailLog("{0},BSLinkset.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", - rootPrim.LocalID, - rootPrim.LocalID, rootPrim.BSBody.ptr.ToString("X"), - childPrim.LocalID, childPrim.BSBody.ptr.ToString("X")); - - // Find the constraint for this link and get rid of it from the overall collection and from my list - if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody, childPrim.BSBody)) - { - // Make the child refresh its location - BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr); - ret = true; - } - - return ret; - } - - // Remove linkage between myself and any possible children I might have. - // Called at taint time! - private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim) - { - DetailLog("{0},BSLinkset.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); - bool ret = false; - - if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody)) - { - ret = true; - } - return ret; - } - - // Call each of the constraints that make up this linkset and recompute the - // various transforms and variables. Used when objects are added or removed - // from a linkset to make sure the constraints know about the new mass and - // geometry. - // Must only be called at taint time!! - private void RecomputeLinksetConstraintVariables() - { - float linksetMass = LinksetMass; - foreach (BSPhysObject child in m_taintChildren) - { - BSConstraint constrain; - if (PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.BSBody, child.BSBody, out constrain)) - { - // DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,taint,child={1},mass={2},A={3},B={4}", - // LinksetRoot.LocalID, child.LocalID, linksetMass, constrain.Body1.ID, constrain.Body2.ID); - constrain.RecomputeConstraintVariables(linksetMass); - } - else - { - // Non-fatal error that happens when children are being added to the linkset but - // their constraints have not been created yet. - break; - } - } - - // If the whole linkset is not here, doesn't make sense to recompute linkset wide values - if (m_children.Count == m_taintChildren.Count) - { - // If this is a multiple object linkset, set everybody's center of mass to the set's center of mass - OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass(); - BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.ptr, - centerOfMass, OMV.Quaternion.Identity); - DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,setCenterOfMass,COM={1},rBody={2}", - LinksetRoot.LocalID, centerOfMass, LinksetRoot.BSBody.ptr.ToString("X")); - foreach (BSPhysObject child in m_taintChildren) - { - BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.ptr, - centerOfMass, OMV.Quaternion.Identity); - } - - // BulletSimAPI.DumpAllInfo2(PhysicsScene.World.ptr); // DEBUG DEBUG DEBUG - } - return; - } - + protected abstract void RemoveChildFromLinkset(BSPhysObject child); // Invoke the detailed logger and output something if it's enabled. - private void DetailLog(string msg, params Object[] args) + protected void DetailLog(string msg, params Object[] args) { if (PhysicsScene.PhysicsLogging.Enabled) PhysicsScene.DetailLog(msg, args); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs new file mode 100755 index 0000000000..8a750b52b7 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs @@ -0,0 +1,385 @@ +/* + * 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.Text; + +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSLinksetConstraints : BSLinkset +{ + // private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]"; + + public BSLinksetConstraints(BSScene scene, BSPhysObject parent) + { + base.Initialize(scene, parent); + } + + // When physical properties are changed the linkset needs to recalculate + // its internal properties. + // May be called at runtime or taint-time (just pass the appropriate flag). + public override void Refresh(BSPhysObject requestor, bool inTaintTime) + { + // If there are no children or not root, I am not the one that recomputes the constraints + if (!HasAnyChildren || !IsRoot(requestor)) + return; + + BSScene.TaintCallback refreshOperation = delegate() + { + RecomputeLinksetConstraintVariables(); + DetailLog("{0},BSLinkset.Refresh,complete,rBody={1}", + LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X")); + }; + if (inTaintTime) + refreshOperation(); + else + PhysicsScene.TaintedObject("BSLinkSet.Refresh", refreshOperation); + } + + // The object is going dynamic (physical). Do any setup necessary + // for a dynamic linkset. + // Only the state of the passed object can be modified. The rest of the linkset + // has not yet been fully constructed. + // Return 'true' if any properties updated on the passed object. + // Called at taint-time! + public override bool MakeDynamic(BSPhysObject child) + { + // What is done for each object in BSPrim is what we want. + return false; + } + + // The object is going static (non-physical). Do any setup necessary + // for a static linkset. + // Return 'true' if any properties updated on the passed object. + // Called at taint-time! + public override bool MakeStatic(BSPhysObject child) + { + // What is done for each object in BSPrim is what we want. + return false; + } + + // Called at taint-time!! + public override void UpdateProperties(BSPhysObject updated) + { + // Nothing to do for constraints on property updates + } + + // Routine used when rebuilding the body of the root of the linkset + // Destroy all the constraints have have been made to root. + // This is called when the root body is changing. + // Returns 'true' of something eas actually removed and would need restoring + // Called at taint-time!! + public override bool RemoveBodyDependencies(BSPrim child) + { + bool ret = false; + + lock (m_linksetActivityLock) + { + if (IsRoot(child)) + { + // If the one with the dependency is root, must undo all children + DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}", + child.LocalID, LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X")); + + ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot); + } + else + { + DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeSingleChild,rID={1},rBody={2},cID={3},cBody={4}", + child.LocalID, + LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"), + child.LocalID, child.BSBody.ptr.ToString("X")); + // ret = PhysicallyUnlinkAChildFromRoot(LinksetRoot, child); + // Despite the function name, this removes any link to the specified object. + ret = PhysicallyUnlinkAllChildrenFromRoot(child); + } + } + return ret; + } + + // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true', + // this routine will restore the removed constraints. + // Called at taint-time!! + public override void RestoreBodyDependencies(BSPrim child) + { + lock (m_linksetActivityLock) + { + if (IsRoot(child)) + { + DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreChildrenForRoot,rID={1},numChild={2}", + child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count); + foreach (BSPhysObject bpo in m_taintChildren) + { + PhysicallyLinkAChildToRoot(LinksetRoot, bpo); + } + } + else + { + DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreSingleChild,rID={1},rBody={2},cID={3},cBody={4}", + LinksetRoot.LocalID, + LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"), + child.LocalID, child.BSBody.ptr.ToString("X")); + PhysicallyLinkAChildToRoot(LinksetRoot, child); + } + } + } + + // ================================================================ + // Below this point is internal magic + + // I am the root of a linkset and a new child is being added + // Called while LinkActivity is locked. + protected override void AddChildToLinkset(BSPhysObject child) + { + if (!HasChild(child)) + { + m_children.Add(child); + + BSPhysObject rootx = LinksetRoot; // capture the root as of now + BSPhysObject childx = child; + + DetailLog("{0},AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); + + PhysicsScene.TaintedObject("AddChildToLinkset", delegate() + { + DetailLog("{0},AddChildToLinkset,taint,rID={1},rBody={2},cID={3},cBody={4}", + rootx.LocalID, + rootx.LocalID, rootx.BSBody.ptr.ToString("X"), + childx.LocalID, childx.BSBody.ptr.ToString("X")); + // Since this is taint-time, the body and shape could have changed for the child + rootx.ForcePosition = rootx.Position; // DEBUG + childx.ForcePosition = childx.Position; // DEBUG + PhysicallyLinkAChildToRoot(rootx, childx); + m_taintChildren.Add(child); + }); + } + return; + } + + // Forcefully removing a child from a linkset. + // This is not being called by the child so we have to make sure the child doesn't think + // it's still connected to the linkset. + // Normal OpenSimulator operation will never do this because other SceneObjectPart information + // also has to be updated (like pointer to prim's parent). + protected override void RemoveChildFromOtherLinkset(BSPhysObject pchild) + { + pchild.Linkset = BSLinkset.Factory(PhysicsScene, pchild); + RemoveChildFromLinkset(pchild); + } + + // I am the root of a linkset and one of my children is being removed. + // Safe to call even if the child is not really in my linkset. + protected override void RemoveChildFromLinkset(BSPhysObject child) + { + if (m_children.Remove(child)) + { + BSPhysObject rootx = LinksetRoot; // capture the root and body as of now + BSPhysObject childx = child; + + DetailLog("{0},RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", + childx.LocalID, + rootx.LocalID, rootx.BSBody.ptr.ToString("X"), + childx.LocalID, childx.BSBody.ptr.ToString("X")); + + PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate() + { + m_taintChildren.Remove(child); + PhysicallyUnlinkAChildFromRoot(rootx, childx); + RecomputeLinksetConstraintVariables(); + }); + + } + else + { + // This will happen if we remove the root of the linkset first. Non-fatal occurance. + // PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader); + } + return; + } + + // Create a constraint between me (root of linkset) and the passed prim (the child). + // Called at taint time! + private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim) + { + // Zero motion for children so they don't interpolate + childPrim.ZeroMotion(); + + // Relative position normalized to the root prim + // Essentually a vector pointing from center of rootPrim to center of childPrim + OMV.Vector3 childRelativePosition = childPrim.Position - rootPrim.Position; + + // real world coordinate of midpoint between the two objects + OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2); + + DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}", + rootPrim.LocalID, + rootPrim.LocalID, rootPrim.BSBody.ptr.ToString("X"), + childPrim.LocalID, childPrim.BSBody.ptr.ToString("X"), + rootPrim.Position, childPrim.Position, midPoint); + + // create a constraint that allows no freedom of movement between the two objects + // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 + + BS6DofConstraint constrain = new BS6DofConstraint( + PhysicsScene.World, rootPrim.BSBody, childPrim.BSBody, midPoint, true, true ); + + /* NOTE: below is an attempt to build constraint with full frame computation, etc. + * Using the midpoint is easier since it lets the Bullet code manipulate the transforms + * of the objects. + * Code left as a warning to future programmers. + // ================================================================================== + // relative position normalized to the root prim + OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation); + OMV.Vector3 childRelativePosition = (childPrim.Position - rootPrim.Position) * invThisOrientation; + + // relative rotation of the child to the parent + OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation; + OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation); + + // create a constraint that allows no freedom of movement between the two objects + // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 + DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID); + BS6DofConstraint constrain = new BS6DofConstraint( + PhysicsScene.World, rootPrim.Body, childPrim.Body, + OMV.Vector3.Zero, + OMV.Quaternion.Inverse(rootPrim.Orientation), + OMV.Vector3.Zero, + OMV.Quaternion.Inverse(childPrim.Orientation), + // A point half way between the parent and child + // childRelativePosition/2, + // childRelativeRotation, + // childRelativePosition/2, + // inverseChildRelativeRotation, + true, + true + ); + // ================================================================================== + */ + + PhysicsScene.Constraints.AddConstraint(constrain); + + // zero linear and angular limits makes the objects unable to move in relation to each other + constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); + constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); + + // tweek the constraint to increase stability + constrain.UseFrameOffset(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintUseFrameOffset)); + constrain.TranslationalLimitMotor(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintEnableTransMotor), + PhysicsScene.Params.linkConstraintTransMotorMaxVel, + PhysicsScene.Params.linkConstraintTransMotorMaxForce); + constrain.SetCFMAndERP(PhysicsScene.Params.linkConstraintCFM, PhysicsScene.Params.linkConstraintERP); + if (PhysicsScene.Params.linkConstraintSolverIterations != 0f) + { + constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations); + } + } + + // Remove linkage between myself and a particular child + // The root and child bodies are passed in because we need to remove the constraint between + // the bodies that were at unlink time. + // Called at taint time! + private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim) + { + bool ret = false; + DetailLog("{0},BSLinkset.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", + rootPrim.LocalID, + rootPrim.LocalID, rootPrim.BSBody.ptr.ToString("X"), + childPrim.LocalID, childPrim.BSBody.ptr.ToString("X")); + + // Find the constraint for this link and get rid of it from the overall collection and from my list + if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody, childPrim.BSBody)) + { + // Make the child refresh its location + BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr); + ret = true; + } + + return ret; + } + + // Remove linkage between myself and any possible children I might have. + // Called at taint time! + private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim) + { + DetailLog("{0},BSLinkset.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); + bool ret = false; + + if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody)) + { + ret = true; + } + return ret; + } + + // Call each of the constraints that make up this linkset and recompute the + // various transforms and variables. Used when objects are added or removed + // from a linkset to make sure the constraints know about the new mass and + // geometry. + // Must only be called at taint time!! + private void RecomputeLinksetConstraintVariables() + { + float linksetMass = LinksetMass; + foreach (BSPhysObject child in m_taintChildren) + { + BSConstraint constrain; + if (PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.BSBody, child.BSBody, out constrain)) + { + // DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,taint,child={1},mass={2},A={3},B={4}", + // LinksetRoot.LocalID, child.LocalID, linksetMass, constrain.Body1.ID, constrain.Body2.ID); + constrain.RecomputeConstraintVariables(linksetMass); + } + else + { + // Non-fatal error that happens when children are being added to the linkset but + // their constraints have not been created yet. + break; + } + } + + // If the whole linkset is not here, doesn't make sense to recompute linkset wide values + if (m_children.Count == m_taintChildren.Count) + { + // If this is a multiple object linkset, set everybody's center of mass to the set's center of mass + OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass(); + BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.ptr, + centerOfMass, OMV.Quaternion.Identity); + DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,setCenterOfMass,COM={1},rBody={2}", + LinksetRoot.LocalID, centerOfMass, LinksetRoot.BSBody.ptr.ToString("X")); + foreach (BSPhysObject child in m_taintChildren) + { + BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.ptr, + centerOfMass, OMV.Quaternion.Identity); + } + + // BulletSimAPI.DumpAllInfo2(PhysicsScene.World.ptr); // DEBUG DEBUG DEBUG + } + return; + } +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index ead6a08f43..538f9056a5 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -46,7 +46,7 @@ public abstract class BSPhysObject : PhysicsActor PhysObjectName = name; TypeName = typeName; - Linkset = new BSLinkset(PhysicsScene, this); + Linkset = BSLinkset.Factory(PhysicsScene, this); LastAssetBuildFailed = false; CollisionCollection = new CollisionEventUpdate(); @@ -78,7 +78,7 @@ public abstract class BSPhysObject : PhysicsActor public PrimitiveBaseShape BaseShape { get; protected set; } // When the physical properties are updated, an EntityProperty holds the update values. - // Keep the current and last EntityProperties to enable computation of differences + // Keep the current and last EntityProperties to enable computation of differences // between the current update and the previous values. public EntityProperties CurrentEntityProperties { get; set; } public EntityProperties LastEntityProperties { get; set; } @@ -213,7 +213,7 @@ public abstract class BSPhysObject : PhysicsActor UnSubscribeEvents(); } } - public override void UnSubscribeEvents() { + public override void UnSubscribeEvents() { // DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName); SubscribedEventsMs = 0; PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate() @@ -222,7 +222,7 @@ public abstract class BSPhysObject : PhysicsActor }); } // Return 'true' if the simulator wants collision events - public override bool SubscribedEvents() { + public override bool SubscribedEvents() { return (SubscribedEventsMs > 0); } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index aeeb4dd27b..38ab3de65d 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -173,6 +173,7 @@ public sealed class BSPrim : BSPhysObject } public override bool ForceBodyShapeRebuild(bool inTaintTime) { + LastAssetBuildFailed = false; BSScene.TaintCallback rebuildOperation = delegate() { _mass = CalculateMass(); // changing the shape changes the mass @@ -295,7 +296,7 @@ public sealed class BSPrim : BSPhysObject private bool PositionSanityCheck() { bool ret = false; - + // If totally below the ground, move the prim up // TODO: figure out the right solution for this... only for dynamic objects? /* @@ -398,7 +399,7 @@ public sealed class BSPrim : BSPhysObject { // Done at taint time so we're sure the physics engine is not using the variables // Vehicle code changes the parameters for this vehicle type. - this._vehicle.ProcessTypeChange(type); + _vehicle.ProcessTypeChange(type); }); } } @@ -510,7 +511,7 @@ public sealed class BSPrim : BSPhysObject }); } } - // Go directly to Bullet to get/set the value. + // Go directly to Bullet to get/set the value. public override OMV.Quaternion ForceOrientation { get @@ -768,7 +769,7 @@ public sealed class BSPrim : BSPhysObject } } public override bool FloatOnWater { - set { + set { _floatOnWater = value; PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate() { @@ -971,7 +972,7 @@ public sealed class BSPrim : BSPhysObject if (hollowAmount > 0.0) { hollowVolume *= hollowAmount; - + switch (BaseShape.HollowShape) { case HollowShape.Square: @@ -1245,13 +1246,14 @@ public sealed class BSPrim : BSPhysObject FillShapeInfo(out shapeData); // If this prim is part of a linkset, we must remove and restore the physical - // links of the body is rebuilt. + // links if the body is rebuilt. bool needToRestoreLinkset = false; // Create the correct physical representation for this type of object. // Updates BSBody and BSShape with the new information. // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. - PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, BaseShape, + // Returns 'true' if either the body or the shape was changed. + PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, BaseShape, null, delegate(BulletBody dBody) { // Called if the current prim body is about to be destroyed. @@ -1354,7 +1356,7 @@ public sealed class BSPrim : BSPhysObject DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); - // BulletSimAPI.DumpRigidBody2(Scene.World.Ptr, BSBody.Ptr); + // BulletSimAPI.DumpRigidBody2(PhysicsScene.World.ptr, BSBody.ptr); // DEBUG DEBUG DEBUG base.RequestPhysicsterseUpdate(); } @@ -1367,8 +1369,8 @@ public sealed class BSPrim : BSPhysObject entprop.Acceleration, entprop.RotationalVelocity); } */ - // The linkset implimentation might want to know about this. + // The linkset implimentation might want to know about this. Linkset.UpdateProperties(this); } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 6621d39f2c..e686f2fdc7 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -320,7 +320,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters { m_log.Debug("[BULLETS UNMANAGED]:" + msg); } - + // Called directly from unmanaged code so don't do much private void BulletLoggerPhysLog(string msg) { @@ -545,7 +545,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters } // This is a kludge to get avatar movement updates. - // The simulator expects collisions for avatars even if there are have been no collisions. + // The simulator expects collisions for avatars even if there are have been no collisions. // The event updates avatar animations and stuff. // If you fix avatar animation updates, remove this overhead and let normal collision processing happen. foreach (BSPhysObject bsp in m_avatars) @@ -716,6 +716,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters } catch (Exception e) { + DetailLog("{0},BSScene.ProcessTaints,doTaintException,id={1}", DetailLogZero, oneCallback.ident); // DEBUG DEBUG DEBUG m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, oneCallback.ident, e); } } @@ -1333,7 +1334,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters // Add the Flush() if debugging crashes to get all the messages written out. // PhysicsLogging.Flush(); } - // used to fill in the LocalID when there isn't one + // Used to fill in the LocalID when there isn't one. It's the correct number of characters. public const string DetailLogZero = "0000000000"; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index d3ba2732a8..b1833c5e88 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -36,7 +36,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin { public class BSShapeCollection : IDisposable { - // private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]"; + private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]"; protected BSScene PhysicsScene { get; set; } @@ -89,7 +89,7 @@ public class BSShapeCollection : IDisposable // higher level dependencies on the shape or body. Mostly used for LinkSets to // remove the physical constraints before the body is destroyed. // Called at taint-time!! - public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim, + public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback) { @@ -105,7 +105,7 @@ public class BSShapeCollection : IDisposable // If we had to select a new shape geometry for the object, // rebuild the body around it. // Updates prim.BSBody with information/pointers to requested body - bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, + bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, prim.BSShape, shapeData, bodyCallback); ret = newGeom || newBody; } @@ -325,7 +325,7 @@ public class BSShapeCollection : IDisposable // Info in prim.BSShape is updated to the new shape. // Returns 'true' if the geometry was rebuilt. // Called at taint-time! - private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeData shapeData, + private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback) { bool ret = false; @@ -335,7 +335,7 @@ public class BSShapeCollection : IDisposable if (shapeData.Type == ShapeData.PhysicsShapeType.SHAPE_AVATAR) { // an avatar capsule is close to a native shape (it is not shared) - ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_AVATAR, + ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_AVATAR, ShapeData.FixedShapeKey.KEY_CAPSULE, shapeCallback); DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.BSShape); haveShape = true; @@ -362,7 +362,7 @@ public class BSShapeCollection : IDisposable || prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE ) { - ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_SPHERE, + ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_SPHERE, ShapeData.FixedShapeKey.KEY_SPHERE, shapeCallback); DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}", prim.LocalID, forceRebuild, prim.BSShape); @@ -376,7 +376,7 @@ public class BSShapeCollection : IDisposable || prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX ) { - ret = GetReferenceToNativeShape( prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_BOX, + ret = GetReferenceToNativeShape( prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_BOX, ShapeData.FixedShapeKey.KEY_BOX, shapeCallback); DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}", prim.LocalID, forceRebuild, prim.BSShape); @@ -423,27 +423,37 @@ public class BSShapeCollection : IDisposable BulletShape newShape = BuildPhysicalNativeShape(shapeType, shapeData, shapeKey); // Don't need to do a 'ReferenceShape()' here because native shapes are not shared. - DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}", + DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}", shapeData.ID, newShape, shapeData.Scale); prim.BSShape = newShape; return true; } - private BulletShape BuildPhysicalNativeShape(ShapeData.PhysicsShapeType shapeType, + private BulletShape BuildPhysicalNativeShape(ShapeData.PhysicsShapeType shapeType, ShapeData shapeData, ShapeData.FixedShapeKey shapeKey) { BulletShape newShape; + // Need to make sure the passed shape information is for the native type. + ShapeData nativeShapeData = shapeData; + nativeShapeData.Type = shapeType; + nativeShapeData.MeshKey = (ulong)shapeKey; + nativeShapeData.HullKey = (ulong)shapeKey; if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR) { newShape = new BulletShape( - BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1.0f, 1.0f, shapeData.Scale), - shapeType); + BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1.0f, 1.0f, nativeShapeData.Scale), shapeType); + DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", nativeShapeData.ID, nativeShapeData.Scale); } else { - newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType); + newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType); + } + if (newShape.ptr == IntPtr.Zero) + { + PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", + LogHeader, nativeShapeData.ID, nativeShapeData.Type); } newShape.shapeKey = (System.UInt64)shapeKey; newShape.isNativeShape = true; @@ -698,19 +708,26 @@ public class BSShapeCollection : IDisposable return ComputeShapeKey(shapeData, pbs, out lod); } + // The creation of a mesh or hull can fail if an underlying asset is not available. + // There are two cases: 1) the asset is not in the cache and it needs to be fetched; + // and 2) the asset cannot be converted (like decompressing JPEG2000s). + // The first case causes the asset to be fetched. The second case just requires + // us to not loop forever. + // Called after creating a physical mesh or hull. If the physical shape was created, + // just return. private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs) { // If the shape was successfully created, nothing more to do if (newShape.ptr != IntPtr.Zero) return newShape; - // The most common reason for failure is that an underlying asset is not available - // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset if (pbs.SculptEntry && !prim.LastAssetBuildFailed && pbs.SculptTexture != OMV.UUID.Zero) { prim.LastAssetBuildFailed = true; BSPhysObject xprim = prim; + DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lID={1},lastFailed={2}", + LogHeader, shapeData.ID.ToString("X"), prim.LastAssetBuildFailed); Util.FireAndForget(delegate { RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod; @@ -724,20 +741,28 @@ public class BSShapeCollection : IDisposable if (yprim.BaseShape.SculptTexture.ToString() != asset.ID) return; - yprim.BaseShape.SculptData = new byte[asset.Data.Length]; - asset.Data.CopyTo(yprim.BaseShape.SculptData, 0); + yprim.BaseShape.SculptData = asset.Data; // This will cause the prim to see that the filler shape is not the right // one and try again to build the object. + // No race condition with the native sphere setting since the rebuild is at taint time. yprim.ForceBodyShapeRebuild(false); }); } }); } + else + { + if (prim.LastAssetBuildFailed) + { + PhysicsScene.Logger.ErrorFormat("{0} Mesh failed to fetch asset. lID={1}, texture={2}", + LogHeader, shapeData.ID, pbs.SculptTexture); + } + } // While we figure out the real problem, stick a simple native shape on the object. BulletShape fillinShape = - BuildPhysicalNativeShape(ShapeData.PhysicsShapeType.SHAPE_SPHERE, shapeData, ShapeData.FixedShapeKey.KEY_SPHERE); + BuildPhysicalNativeShape(ShapeData.PhysicsShapeType.SHAPE_BOX, shapeData, ShapeData.FixedShapeKey.KEY_BOX); return fillinShape; } @@ -746,7 +771,7 @@ public class BSShapeCollection : IDisposable // Updates prim.BSBody with the information about the new body if one is created. // Returns 'true' if an object was actually created. // Called at taint-time. - private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape, + private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape, ShapeData shapeData, BodyDestructionCallback bodyCallback) { bool ret = false; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index 41065344e2..ae267e3521 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -201,9 +201,7 @@ public class BSTerrainManager // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new // terrain shape is created and added to the body. // This call is most often used to update the heightMap and parameters of the terrain. - // The 'doNow' boolean says whether to do all the unmanaged activities right now (like when - // calling this routine from initialization or taint-time routines) or whether to delay - // all the unmanaged activities to taint-time. + // (The above does suggest that some simplification/refactoring is in order.) private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool inTaintTime) { DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},inTaintTime={3}",