diff --git a/OpenSim/Data/Tests/BasicDataServiceTest.cs b/OpenSim/Data/Tests/BasicDataServiceTest.cs index d8019ba9b4..69b79bf6e0 100644 --- a/OpenSim/Data/Tests/BasicDataServiceTest.cs +++ b/OpenSim/Data/Tests/BasicDataServiceTest.cs @@ -44,9 +44,15 @@ namespace OpenSim.Data.Tests /// This is a base class for testing any Data service for any DBMS. /// Requires NUnit 2.5 or better (to support the generics). /// + /// + /// FIXME: Should extend OpenSimTestCase but compile on mono 2.4.3 currently fails with + /// AssetTests`2 : System.MemberAccessException : Cannot create an instance of OpenSim.Data.Tests.AssetTests`2[TConn,TAssetData] because Type.ContainsGenericParameters is true. + /// and similar on EstateTests, InventoryTests and RegionTests. + /// Runs fine with mono 2.10.8.1, so easiest thing is to wait until min Mono version uplifts. + /// /// /// - public class BasicDataServiceTest : OpenSimTestCase + public class BasicDataServiceTest where TConn : DbConnection, new() where TService : class, new() { diff --git a/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs b/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs index af54c1a1de..b735c611bb 100644 --- a/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs @@ -81,7 +81,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Groups } if (groupsConfig.GetString("Module", "Default") != "Default") + { + m_Enabled = false; return; + } } } diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs index 3a44cc5ee1..2d46276274 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs @@ -189,20 +189,24 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage { foreach (GridInstantMessage im in msglist) { - // client.SendInstantMessage(im); + if (im.dialog == (byte)InstantMessageDialog.InventoryOffered) + // send it directly or else the item will be given twice + client.SendInstantMessage(im); + else + { + // Send through scene event manager so all modules get a chance + // to look at this message before it gets delivered. + // + // Needed for proper state management for stored group + // invitations + // - // Send through scene event manager so all modules get a chance - // to look at this message before it gets delivered. - // - // Needed for proper state management for stored group - // invitations - // + im.offline = 1; - im.offline = 1; - - Scene s = FindScene(client.AgentId); - if (s != null) - s.EventManager.TriggerIncomingInstantMessage(im); + Scene s = FindScene(client.AgentId); + if (s != null) + s.EventManager.TriggerIncomingInstantMessage(im); + } } } } diff --git a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs index ab8f14344b..7b235aef74 100644 --- a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs +++ b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs @@ -365,7 +365,8 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands if (mainParams.Count < 4) { - m_console.OutputFormat("Usage: show part id [--full] "); + //m_console.OutputFormat("Usage: show part id [--full] "); + m_console.OutputFormat("Usage: show part id "); return; } @@ -405,6 +406,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands if (mainParams.Count < 5) { + //m_console.OutputFormat("Usage: show part pos to "); m_console.OutputFormat("Usage: show part pos [--full] to "); return; } @@ -445,7 +447,8 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands if (mainParams.Count < 4) { - m_console.OutputFormat("Usage: show part name [--full] [--regex] "); + m_console.OutputFormat("Usage: show part name [--regex] "); + //m_console.OutputFormat("Usage: show part name [--full] [--regex] "); return; } @@ -577,6 +580,58 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands cdl.AddRow("Link number", sop.LinkNum); cdl.AddRow("Flags", sop.Flags); + if (showFull) + { + PrimitiveBaseShape s = sop.Shape; + cdl.AddRow("FlexiDrag", s.FlexiDrag); + cdl.AddRow("FlexiEntry", s.FlexiEntry); + cdl.AddRow("FlexiForce", string.Format("<{0},{1},{2}>", s.FlexiForceX, s.FlexiForceY, s.FlexiForceZ)); + cdl.AddRow("FlexiGravity", s.FlexiGravity); + cdl.AddRow("FlexiSoftness", s.FlexiSoftness); + cdl.AddRow("HollowShape", s.HollowShape); + cdl.AddRow( + "LightColor", + string.Format("<{0},{1},{2},{3}>", s.LightColorR, s.LightColorB, s.LightColorG, s.LightColorA)); + cdl.AddRow("FlexiDrag", s.LightCutoff); + cdl.AddRow("FlexiDrag", s.LightEntry); + cdl.AddRow("FlexiDrag", s.LightFalloff); + cdl.AddRow("FlexiDrag", s.LightIntensity); + cdl.AddRow("FlexiDrag", s.LightRadius); + cdl.AddRow("Media", string.Format("{0} entries", s.Media != null ? s.Media.Count.ToString() : "n/a")); + cdl.AddRow("PathBegin", s.PathBegin); + cdl.AddRow("PathEnd", s.PathEnd); + cdl.AddRow("PathCurve", s.PathCurve); + cdl.AddRow("PathRadiusOffset", s.PathRadiusOffset); + cdl.AddRow("PathRevolutions", s.PathRevolutions); + cdl.AddRow("PathScale", string.Format("<{0},{1}>", s.PathScaleX, s.PathScaleY)); + cdl.AddRow("PathSkew", string.Format("<{0},{1}>", s.PathShearX, s.PathShearY)); + cdl.AddRow("FlexiDrag", s.PathSkew); + cdl.AddRow("PathTaper", string.Format("<{0},{1}>", s.PathTaperX, s.PathTaperY)); + cdl.AddRow("PathTwist", s.PathTwist); + cdl.AddRow("PathTwistBegin", s.PathTwistBegin); + cdl.AddRow("PCode", s.PCode); + cdl.AddRow("ProfileBegin", s.ProfileBegin); + cdl.AddRow("ProfileEnd", s.ProfileEnd); + cdl.AddRow("ProfileHollow", s.ProfileHollow); + cdl.AddRow("ProfileShape", s.ProfileShape); + cdl.AddRow("ProjectionAmbiance", s.ProjectionAmbiance); + cdl.AddRow("ProjectionEntry", s.ProjectionEntry); + cdl.AddRow("ProjectionFocus", s.ProjectionFocus); + cdl.AddRow("ProjectionFOV", s.ProjectionFOV); + cdl.AddRow("ProjectionTextureUUID", s.ProjectionTextureUUID); + cdl.AddRow("Scale", s.Scale); + cdl.AddRow( + "SculptData", + string.Format("{0} bytes", s.SculptData != null ? s.SculptData.Length.ToString() : "n/a")); + cdl.AddRow("SculptEntry", s.SculptEntry); + cdl.AddRow("SculptTexture", s.SculptTexture); + cdl.AddRow("SculptType", s.SculptType); + cdl.AddRow("State", s.State); + + // TODO, unpack and display texture entries + //cdl.AddRow("Textures", string.Format("{0} entries", s.Textures. + } + object itemsOutput; if (showFull) { @@ -588,7 +643,6 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands itemsOutput = sop.Inventory.Count; } - cdl.AddRow("Items", itemsOutput); return sb.Append(cdl.ToString()); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index 4c195e1cfc..1dfc4204dd 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -307,7 +307,7 @@ public sealed class BSCharacter : BSPhysObject } if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) { - float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); + float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position); if (Position.Z < waterHeight) { _position.Z = waterHeight; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index dbc9039705..3a73fba7b8 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -80,10 +80,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin private Quaternion m_referenceFrame = Quaternion.Identity; // Linear properties + private BSVMotor m_linearMotor = new BSVMotor("LinearMotor"); private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time private Vector3 m_linearMotorOffset = Vector3.Zero; // the point of force can be offset from the center private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL - private Vector3 m_newVelocity = Vector3.Zero; // velocity computed to be applied to body private Vector3 m_linearFrictionTimescale = Vector3.Zero; private float m_linearMotorDecayTimescale = 0; private float m_linearMotorTimescale = 0; @@ -93,6 +93,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // private Vector3 m_linearMotorOffset = Vector3.Zero; //Angular properties + private BSVMotor m_angularMotor = new BSVMotor("AngularMotor"); private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor // private int m_angularMotorApply = 0; // application frame counter private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity @@ -124,6 +125,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity. //Attractor properties + private BSVMotor m_verticalAttractionMotor = new BSVMotor("VerticalAttraction"); private float m_verticalAttractionEfficiency = 1.0f; // damped private float m_verticalAttractionTimescale = 500f; // Timescale > 300 means no vert attractor. @@ -152,10 +154,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_angularDeflectionTimescale = Math.Max(pValue, 0.01f); break; case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE: - m_angularMotorDecayTimescale = Math.Max(pValue, 0.01f); + m_angularMotorDecayTimescale = Math.Max(0.01f, Math.Min(pValue,120)); + m_angularMotor.TargetValueDecayTimeScale = m_angularMotorDecayTimescale; break; case Vehicle.ANGULAR_MOTOR_TIMESCALE: m_angularMotorTimescale = Math.Max(pValue, 0.01f); + m_angularMotor.TimeScale = m_angularMotorTimescale; break; case Vehicle.BANKING_EFFICIENCY: m_bankingEfficiency = Math.Max(-1f, Math.Min(pValue, 1f)); @@ -185,33 +189,40 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_linearDeflectionTimescale = Math.Max(pValue, 0.01f); break; case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE: - m_linearMotorDecayTimescale = Math.Max(pValue, 0.01f); + m_linearMotorDecayTimescale = Math.Max(0.01f, Math.Min(pValue,120)); + m_linearMotor.TargetValueDecayTimeScale = m_linearMotorDecayTimescale; break; case Vehicle.LINEAR_MOTOR_TIMESCALE: m_linearMotorTimescale = Math.Max(pValue, 0.01f); + m_linearMotor.TimeScale = m_linearMotorTimescale; break; case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY: m_verticalAttractionEfficiency = Math.Max(0.1f, Math.Min(pValue, 1f)); + m_verticalAttractionMotor.Efficiency = m_verticalAttractionEfficiency; break; case Vehicle.VERTICAL_ATTRACTION_TIMESCALE: m_verticalAttractionTimescale = Math.Max(pValue, 0.01f); + m_verticalAttractionMotor.TimeScale = m_verticalAttractionTimescale; break; // These are vector properties but the engine lets you use a single float value to // set all of the components to the same value case Vehicle.ANGULAR_FRICTION_TIMESCALE: m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue); + m_angularMotor.FrictionTimescale = m_angularFrictionTimescale; break; case Vehicle.ANGULAR_MOTOR_DIRECTION: m_angularMotorDirection = new Vector3(pValue, pValue, pValue); - // m_angularMotorApply = 100; + m_angularMotor.SetTarget(m_angularMotorDirection); break; case Vehicle.LINEAR_FRICTION_TIMESCALE: m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue); + m_linearMotor.FrictionTimescale = m_linearFrictionTimescale; break; case Vehicle.LINEAR_MOTOR_DIRECTION: m_linearMotorDirection = new Vector3(pValue, pValue, pValue); m_linearMotorDirectionLASTSET = new Vector3(pValue, pValue, pValue); + m_linearMotor.SetTarget(m_linearMotorDirection); break; case Vehicle.LINEAR_MOTOR_OFFSET: m_linearMotorOffset = new Vector3(pValue, pValue, pValue); @@ -227,6 +238,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin { case Vehicle.ANGULAR_FRICTION_TIMESCALE: m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); + m_angularMotor.FrictionTimescale = m_angularFrictionTimescale; break; case Vehicle.ANGULAR_MOTOR_DIRECTION: // Limit requested angular speed to 2 rps= 4 pi rads/sec @@ -234,14 +246,16 @@ namespace OpenSim.Region.Physics.BulletSPlugin pValue.Y = Math.Max(-12.56f, Math.Min(pValue.Y, 12.56f)); pValue.Z = Math.Max(-12.56f, Math.Min(pValue.Z, 12.56f)); m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); - // m_angularMotorApply = 100; + m_angularMotor.SetTarget(m_angularMotorDirection); break; case Vehicle.LINEAR_FRICTION_TIMESCALE: m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); + m_linearMotor.FrictionTimescale = m_linearFrictionTimescale; break; case Vehicle.LINEAR_MOTOR_DIRECTION: m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z); + m_linearMotor.SetTarget(m_linearMotorDirection); break; case Vehicle.LINEAR_MOTOR_OFFSET: m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z); @@ -303,7 +317,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_VhoverEfficiency = 0; m_VhoverTimescale = 0; m_VehicleBuoyancy = 0; - + m_linearDeflectionEfficiency = 1; m_linearDeflectionTimescale = 1; @@ -319,6 +333,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_referenceFrame = Quaternion.Identity; m_flags = (VehicleFlag)0; + break; case Vehicle.TYPE_SLED: @@ -351,10 +366,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_bankingMix = 1; m_referenceFrame = Quaternion.Identity; - 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 | VehicleFlag.HOVER_UP_ONLY); + m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY + | VehicleFlag.HOVER_TERRAIN_ONLY + | VehicleFlag.HOVER_GLOBAL_HEIGHT + | VehicleFlag.HOVER_UP_ONLY); + m_flags |= (VehicleFlag.NO_DEFLECTION_UP + | VehicleFlag.LIMIT_ROLL_ONLY + | VehicleFlag.LIMIT_MOTOR_UP); break; case Vehicle.TYPE_CAR: m_linearMotorDirection = Vector3.Zero; @@ -427,9 +445,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.LIMIT_ROLL_ONLY + | VehicleFlag.LIMIT_MOTOR_UP | VehicleFlag.HOVER_UP_ONLY); m_flags |= (VehicleFlag.NO_DEFLECTION_UP - | VehicleFlag.LIMIT_MOTOR_UP | VehicleFlag.HOVER_WATER_ONLY); break; case Vehicle.TYPE_AIRPLANE: @@ -510,6 +528,28 @@ namespace OpenSim.Region.Physics.BulletSPlugin | VehicleFlag.HOVER_GLOBAL_HEIGHT); break; } + + // Update any physical parameters based on this type. + Refresh(); + + m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale, + m_linearMotorDecayTimescale, m_linearFrictionTimescale, + 1f); + m_linearMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging) + + m_angularMotor = new BSVMotor("AngularMotor", m_angularMotorTimescale, + m_angularMotorDecayTimescale, m_angularFrictionTimescale, + 1f); + m_angularMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging) + + m_verticalAttractionMotor = new BSVMotor("VerticalAttraction", m_verticalAttractionTimescale, + BSMotor.Infinite, BSMotor.InfiniteVector, + m_verticalAttractionEfficiency); + // Z goes away and we keep X and Y + m_verticalAttractionMotor.FrictionTimescale = new Vector3(BSMotor.Infinite, BSMotor.Infinite, 0.1f); + m_verticalAttractionMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging) + + // m_bankingMotor = new BSVMotor("BankingMotor", ...); } // Some of the properties of this prim may have changed. @@ -518,13 +558,25 @@ namespace OpenSim.Region.Physics.BulletSPlugin { if (IsActive) { + m_vehicleMass = Prim.Linkset.LinksetMass; + // Friction effects are handled by this vehicle code - BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, 0f); - BulletSimAPI.SetHitFraction2(Prim.PhysBody.ptr, 0f); + float friction = 0f; + BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, friction); - // BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, 0.8f); + // Moderate angular movement introduced by Bullet. + // TODO: possibly set AngularFactor and LinearFactor for the type of vehicle. + // Maybe compute linear and angular factor and damping from params. + float angularDamping = PhysicsScene.Params.vehicleAngularDamping; + BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping); - VDetailLog("{0},BSDynamics.Refresh,zeroingFriction and adding damping", Prim.LocalID); + // DEBUG DEBUG DEBUG: use uniform inertia to smooth movement added by Bullet + // Vector3 localInertia = new Vector3(1f, 1f, 1f); + Vector3 localInertia = new Vector3(m_vehicleMass, m_vehicleMass, m_vehicleMass); + BulletSimAPI.SetMassProps2(Prim.PhysBody.ptr, m_vehicleMass, localInertia); + + VDetailLog("{0},BSDynamics.Refresh,frict={1},inert={2},aDamp={3}", + Prim.LocalID, friction, localInertia, angularDamping); } } @@ -551,111 +603,109 @@ namespace OpenSim.Region.Physics.BulletSPlugin { if (!IsActive) return; - // DEBUG - // Because Bullet does apply forces to the vehicle, our last computed - // linear and angular velocities are not what is happening now. - // Vector3 externalAngularVelocity = Prim.ForceRotationalVelocity - m_lastAngularVelocity; - // m_lastAngularVelocity += (externalAngularVelocity * 0.5f) * pTimestep; - // m_lastAngularVelocity = Prim.ForceRotationalVelocity; // DEBUG: account for what Bullet did last time - // m_lastLinearVelocityVector = Prim.ForceVelocity * Quaternion.Inverse(Prim.ForceOrientation); // DEBUG: - // END DEBUG - - m_vehicleMass = Prim.Linkset.LinksetMass; - MoveLinear(pTimestep); - // Commented out for debug MoveAngular(pTimestep); - // Prim.ApplyTorqueImpulse(-Prim.RotationalVelocity * m_vehicleMass, false); // DEBUG DEBUG - // Prim.ForceRotationalVelocity = -Prim.RotationalVelocity; // DEBUG DEBUG LimitRotation(pTimestep); // remember the position so next step we can limit absolute movement effects m_lastPositionVector = Prim.ForcePosition; - VDetailLog("{0},BSDynamics.Step,frict={1},grav={2},inertia={3},mass={4}", // DEBUG DEBUG - Prim.LocalID, - BulletSimAPI.GetFriction2(Prim.PhysBody.ptr), - BulletSimAPI.GetGravity2(Prim.PhysBody.ptr), - Prim.Inertia, - m_vehicleMass - ); VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}", Prim.LocalID, Prim.ForcePosition, Prim.Force, Prim.ForceVelocity, Prim.RotationalVelocity); - }// end Step + } // Apply the effect of the linear motor. // Also does hover and float. private void MoveLinear(float pTimestep) { - // m_linearMotorDirection is the target direction we are moving relative to the vehicle coordinates - // m_lastLinearVelocityVector is the current speed we are moving in that direction - if (m_linearMotorDirection.LengthSquared() > 0.001f) - { - Vector3 origDir = m_linearMotorDirection; // DEBUG - Vector3 origVel = m_lastLinearVelocityVector; // DEBUG - // DEBUG: the vehicle velocity rotated to be relative to vehicle coordinates for comparison - Vector3 vehicleVelocity = Prim.ForceVelocity * Quaternion.Inverse(Prim.ForceOrientation); // DEBUG + Vector3 linearMotorContribution = m_linearMotor.Step(pTimestep); - // Add (desiredVelocity - lastAppliedVelocity) / howLongItShouldTakeToComplete - Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/(m_linearMotorTimescale) * pTimestep; - m_lastLinearVelocityVector += addAmount; - - float decayFactor = (1.0f / m_linearMotorDecayTimescale) * pTimestep; - m_linearMotorDirection *= (1f - decayFactor); - - // Rotate new object velocity from vehicle relative to world coordinates - m_newVelocity = m_lastLinearVelocityVector * Prim.ForceOrientation; - - // Apply friction for next time - Vector3 frictionFactor = (Vector3.One / m_linearFrictionTimescale) * pTimestep; - m_lastLinearVelocityVector *= (Vector3.One - frictionFactor); - - VDetailLog("{0},MoveLinear,nonZero,origlmDir={1},origlvVel={2},vehVel={3},add={4},decay={5},frict={6},lmDir={7},lvVec={8},newVel={9}", - Prim.LocalID, origDir, origVel, vehicleVelocity, addAmount, decayFactor, frictionFactor, - m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity); - } - else - { - // if what remains of direction is very small, zero it. - m_linearMotorDirection = Vector3.Zero; - m_lastLinearVelocityVector = Vector3.Zero; - m_newVelocity = Vector3.Zero; - - VDetailLog("{0},MoveLinear,zeroed", Prim.LocalID); - } - - // m_newVelocity is velocity computed from linear motor in world coordinates + // Rotate new object velocity from vehicle relative to world coordinates + linearMotorContribution *= Prim.ForceOrientation; + // ================================================================== // Gravity and Buoyancy // There is some gravity, make a gravity force vector that is applied after object velocity. // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; Vector3 grav = Prim.PhysicsScene.DefaultGravity * (1f - m_VehicleBuoyancy); - /* - * RA: Not sure why one would do this unless we are hoping external forces are doing gravity, ... - // Preserve the current Z velocity - Vector3 vel_now = m_prim.Velocity; - m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity - */ - Vector3 pos = Prim.ForcePosition; -// Vector3 accel = new Vector3(-(m_dir.X - m_lastLinearVelocityVector.X / 0.1f), -(m_dir.Y - m_lastLinearVelocityVector.Y / 0.1f), m_dir.Z - m_lastLinearVelocityVector.Z / 0.1f); - - // If below the terrain, move us above the ground a little. float terrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos); + + Vector3 terrainHeightContribution = ComputeLinearTerrainHeightCorrection(pTimestep, ref pos, terrainHeight); + + Vector3 hoverContribution = ComputeLinearHover(pTimestep, ref pos, terrainHeight); + + ComputeLinearBlockingEndPoint(pTimestep, ref pos); + + Vector3 limitMotorUpContribution = ComputeLinearMotorUp(pTimestep, pos, terrainHeight); + + // ================================================================== + Vector3 newVelocity = linearMotorContribution + + terrainHeightContribution + + hoverContribution + + limitMotorUpContribution; + + // If not changing some axis, reduce out velocity + if ((m_flags & (VehicleFlag.NO_X)) != 0) + newVelocity.X = 0; + if ((m_flags & (VehicleFlag.NO_Y)) != 0) + newVelocity.Y = 0; + if ((m_flags & (VehicleFlag.NO_Z)) != 0) + newVelocity.Z = 0; + + // ================================================================== + // Clamp REALLY high or low velocities + float newVelocityLengthSq = newVelocity.LengthSquared(); + if (newVelocityLengthSq > 1e6f) + { + newVelocity /= newVelocity.Length(); + newVelocity *= 1000f; + } + else if (newVelocityLengthSq < 1e-6f) + newVelocity = Vector3.Zero; + + // ================================================================== + // Stuff new linear velocity into the vehicle + Prim.ForceVelocity = newVelocity; + // Prim.ApplyForceImpulse((m_newVelocity - Prim.Velocity) * m_vehicleMass, false); // DEBUG DEBUG + + // Other linear forces are applied as forces. + Vector3 totalDownForce = grav * m_vehicleMass; + if (totalDownForce != Vector3.Zero) + { + Prim.AddForce(totalDownForce, false); + } + + VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},primVel={4},totalDown={5}", + Prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector, + newVelocity, Prim.Velocity, totalDownForce); + + } // end MoveLinear() + + public Vector3 ComputeLinearTerrainHeightCorrection(float pTimestep, ref Vector3 pos, float terrainHeight) + { + Vector3 ret = Vector3.Zero; + // If below the terrain, move us above the ground a little. // Taking the rotated size doesn't work here because m_prim.Size is the size of the root prim and not the linkset. // TODO: Add a m_prim.LinkSet.Size similar to m_prim.LinkSet.Mass. // Vector3 rotatedSize = m_prim.Size * m_prim.ForceOrientation; // if (rotatedSize.Z < terrainHeight) if (pos.Z < terrainHeight) { + // TODO: correct position by applying force rather than forcing position. pos.Z = terrainHeight + 2; Prim.ForcePosition = pos; VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", Prim.LocalID, terrainHeight, pos); } + return ret; + } + + public Vector3 ComputeLinearHover(float pTimestep, ref Vector3 pos, float terrainHeight) + { + Vector3 ret = Vector3.Zero; - // Check if hovering // m_VhoverEfficiency: 0=bouncy, 1=totally damped // m_VhoverTimescale: time to achieve height if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0) @@ -663,7 +713,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // We should hover, get the target height if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0) { - m_VhoverTargetHeight = Prim.PhysicsScene.GetWaterLevelAtXYZ(pos) + m_VhoverHeight; + m_VhoverTargetHeight = Prim.PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(pos) + m_VhoverHeight; } if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0) { @@ -680,6 +730,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin if (pos.Z > m_VhoverTargetHeight) m_VhoverTargetHeight = pos.Z; } + if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0) { if (Math.Abs(pos.Z - m_VhoverTargetHeight) > 0.2f) @@ -694,28 +745,31 @@ namespace OpenSim.Region.Physics.BulletSPlugin // RA: where does the 50 come from? float verticalCorrectionVelocity = pTimestep * ((verticalError * 50.0f) / m_VhoverTimescale); // Replace Vertical speed with correction figure if significant - if (Math.Abs(verticalError) > 0.01f) + if (verticalError > 0.01f) { - m_newVelocity.Z += verticalCorrectionVelocity; + ret = new Vector3(0f, 0f, verticalCorrectionVelocity); //KF: m_VhoverEfficiency is not yet implemented } else if (verticalError < -0.01) { - m_newVelocity.Z -= verticalCorrectionVelocity; - } - else - { - m_newVelocity.Z = 0f; + ret = new Vector3(0f, 0f, -verticalCorrectionVelocity); } } - VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", Prim.LocalID, pos, m_newVelocity, m_VhoverHeight, m_VhoverTargetHeight); + VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", + Prim.LocalID, pos, ret, m_VhoverHeight, m_VhoverTargetHeight); } + return ret; + } + + public bool ComputeLinearBlockingEndPoint(float pTimestep, ref Vector3 pos) + { + bool changed = false; + Vector3 posChange = pos - m_lastPositionVector; if (m_BlockingEndPoint != Vector3.Zero) { - bool changed = false; if (pos.X >= (m_BlockingEndPoint.X - (float)1)) { pos.X -= posChange.X + 1; @@ -748,75 +802,53 @@ namespace OpenSim.Region.Physics.BulletSPlugin Prim.LocalID, m_BlockingEndPoint, posChange, pos); } } + return changed; + } - #region downForce - Vector3 downForce = Vector3.Zero; - + // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags : + // Prevent ground vehicles from motoring into the sky.This flag has a subtle effect when + // used with conjunction with banking: the strength of the banking will decay when the + // vehicle no longer experiences collisions. The decay timescale is the same as + // VEHICLE_BANKING_TIMESCALE. This is to help prevent ground vehicles from steering + // when they are in mid jump. + // TODO: this code is wrong. Also, what should it do for boats? + public Vector3 ComputeLinearMotorUp(float pTimestep, Vector3 pos, float terrainHeight) + { + Vector3 ret = Vector3.Zero; if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0) { // If the vehicle is motoring into the sky, get it going back down. - // Is this an angular force or both linear and angular?? float distanceAboveGround = pos.Z - terrainHeight; - if (distanceAboveGround > 2f) + if (distanceAboveGround > 1f) { // downForce = new Vector3(0, 0, (-distanceAboveGround / m_bankingTimescale) * pTimestep); // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale); - downForce = new Vector3(0, 0, -distanceAboveGround); + ret = new Vector3(0, 0, -distanceAboveGround); } - // TODO: this calculation is all wrong. From the description at + // TODO: this calculation is wrong. From the description at // (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce // has a decay factor. This says this force should // be computed with a motor. - VDetailLog("{0},MoveLinear,limitMotorUp,distAbove={1},downForce={2}", - Prim.LocalID, distanceAboveGround, downForce); + // TODO: add interaction with banking. + VDetailLog("{0},MoveLinear,limitMotorUp,distAbove={1},downForce={2}", + Prim.LocalID, distanceAboveGround, ret); } - #endregion // downForce - - // If not changing some axis, reduce out velocity - if ((m_flags & (VehicleFlag.NO_X)) != 0) - m_newVelocity.X = 0; - if ((m_flags & (VehicleFlag.NO_Y)) != 0) - m_newVelocity.Y = 0; - if ((m_flags & (VehicleFlag.NO_Z)) != 0) - m_newVelocity.Z = 0; - - // Clamp REALLY high or low velocities - if (m_newVelocity.LengthSquared() > 1e6f) - { - m_newVelocity /= m_newVelocity.Length(); - m_newVelocity *= 1000f; - } - else if (m_newVelocity.LengthSquared() < 1e-6f) - m_newVelocity = Vector3.Zero; - - // Stuff new linear velocity into the vehicle - Prim.ForceVelocity = m_newVelocity; - // Prim.ApplyForceImpulse((m_newVelocity - Prim.Velocity) * m_vehicleMass, false); // DEBUG DEBUG - - Vector3 totalDownForce = downForce + grav; - if (totalDownForce != Vector3.Zero) - { - Prim.AddForce(totalDownForce * m_vehicleMass, false); - // Prim.ApplyForceImpulse(totalDownForce * m_vehicleMass, false); - } - - VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},primVel={4},totalDown={5}", - Prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity, Prim.Velocity, totalDownForce); - - } // end MoveLinear() + return ret; + } + // ======================================================================= // ======================================================================= // Apply the effect of the angular motor. private void MoveAngular(float pTimestep) { // m_angularMotorDirection // angular velocity requested by LSL motor - // m_angularMotorApply // application frame counter // m_angularMotorVelocity // current angular motor velocity (ramps up and down) - // m_angularMotorTimescale // motor angular velocity ramp up rate + // m_angularMotorTimescale // motor angular velocity ramp up time // m_angularMotorDecayTimescale // motor angular velocity decay rate // m_angularFrictionTimescale // body angular velocity decay rate // m_lastAngularVelocity // what was last applied to body + /* if (m_angularMotorDirection.LengthSquared() > 0.0001) { Vector3 origVel = m_angularMotorVelocity; @@ -835,141 +867,39 @@ namespace OpenSim.Region.Physics.BulletSPlugin { m_angularMotorVelocity = Vector3.Zero; } + */ - #region Vertical attactor + Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep); - Vector3 vertattr = Vector3.Zero; - Vector3 deflection = Vector3.Zero; - Vector3 banking = Vector3.Zero; - - // If vertical attaction timescale is reasonable and we applied an angular force last time... - if (m_verticalAttractionTimescale < 300 && m_lastAngularVelocity != Vector3.Zero) + // ================================================================== + // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags : + // This flag prevents linear deflection parallel to world z-axis. This is useful + // for preventing ground vehicles with large linear deflection, like bumper cars, + // from climbing their linear deflection into the sky. + // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement + if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) { - float VAservo = pTimestep * 0.2f / m_verticalAttractionTimescale; - if (Prim.IsColliding) - VAservo = pTimestep * 0.05f / (m_verticalAttractionTimescale); - - VAservo *= (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency); - - // Create a vector of the vehicle "up" in world coordinates - Vector3 verticalError = Vector3.UnitZ * Prim.ForceOrientation; - // verticalError.X and .Y are the World error amounts. They are 0 when there is no - // error (Vehicle Body is 'vertical'), and .Z will be 1. As the body leans to its - // side |.X| will increase to 1 and .Z fall to 0. As body inverts |.X| will fall - // and .Z will go // negative. Similar for tilt and |.Y|. .X and .Y must be - // modulated to prevent a stable inverted body. - - // Error is 0 (no error) to +/- 2 (max error) - if (verticalError.Z < 0.0f) - { - verticalError.X = 2.0f - verticalError.X; - verticalError.Y = 2.0f - verticalError.Y; - } - // scale it by VAservo (timestep and timescale) - verticalError = verticalError * VAservo; - - // As the body rotates around the X axis, then verticalError.Y increases; Rotated around Y - // then .X increases, so change Body angular velocity X based on Y, and Y based on X. - // Z is not changed. - vertattr.X = verticalError.Y; - vertattr.Y = - verticalError.X; - vertattr.Z = 0f; - - // scaling appears better usingsquare-law - Vector3 angularVelocity = Prim.ForceRotationalVelocity; - float bounce = 1.0f - (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency); - vertattr.X += bounce * angularVelocity.X; - vertattr.Y += bounce * angularVelocity.Y; - - VDetailLog("{0},MoveAngular,verticalAttraction,VAservo={1},effic={2},verticalError={3},bounce={4},vertattr={5}", - Prim.LocalID, VAservo, m_verticalAttractionEfficiency, verticalError, bounce, vertattr); - - } - #endregion // Vertical attactor - - #region Deflection - - if (m_angularDeflectionEfficiency != 0) - { - // Compute a scaled vector that points in the preferred axis (X direction) - Vector3 scaledDefaultDirection = - new Vector3((pTimestep * 10 * (m_angularDeflectionEfficiency / m_angularDeflectionTimescale)), 0, 0); - // Adding the current vehicle orientation and reference frame displaces the orientation to the frame. - // Rotate the scaled default axix relative to the actual vehicle direction giving where it should point. - Vector3 preferredAxisOfMotion = scaledDefaultDirection * Quaternion.Add(Prim.ForceOrientation, m_referenceFrame); - - // Scale by efficiency and timescale - deflection = (preferredAxisOfMotion * (m_angularDeflectionEfficiency) / m_angularDeflectionTimescale) * pTimestep; - - VDetailLog("{0},MoveAngular,Deflection,perfAxis={1},deflection={2}", - Prim.LocalID, preferredAxisOfMotion, deflection); - // This deflection computation is not correct. - deflection = Vector3.Zero; + angularMotorContribution.X = 0f; + angularMotorContribution.Y = 0f; + VDetailLog("{0},MoveAngular,noDeflectionUp,angularMotorContrib={1}", Prim.LocalID, angularMotorContribution); } - #endregion + Vector3 verticalAttractionContribution = ComputeAngularVerticalAttraction(pTimestep); - #region Banking + Vector3 deflectionContribution = ComputeAngularDeflection(pTimestep); - if (m_bankingEfficiency != 0) - { - Vector3 dir = Vector3.One * Prim.ForceOrientation; - float mult = (m_bankingMix*m_bankingMix)*-1*(m_bankingMix < 0 ? -1 : 1); - //Changes which way it banks in and out of turns + Vector3 bankingContribution = ComputeAngularBanking(pTimestep); - //Use the square of the efficiency, as it looks much more how SL banking works - float effSquared = (m_bankingEfficiency*m_bankingEfficiency); - if (m_bankingEfficiency < 0) - effSquared *= -1; //Keep the negative! - - float mix = Math.Abs(m_bankingMix); - if (m_angularMotorVelocity.X == 0) - { - /*if (!parent.Orientation.ApproxEquals(this.m_referenceFrame, 0.25f)) - { - Vector3 axisAngle; - float angle; - parent.Orientation.GetAxisAngle(out axisAngle, out angle); - Vector3 rotatedVel = parent.Velocity * parent.Orientation; - if ((rotatedVel.X < 0 && axisAngle.Y > 0) || (rotatedVel.X > 0 && axisAngle.Y < 0)) - m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (1f) * 10; - else - m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (-1f) * 10; - }*/ - } - else - banking.Z += (effSquared*(mult*mix))*(m_angularMotorVelocity.X) * 4; - if (!Prim.IsColliding && Math.Abs(m_angularMotorVelocity.X) > mix) - //If they are colliding, we probably shouldn't shove the prim around... probably - { - float angVelZ = m_angularMotorVelocity.X*-1; - /*if(angVelZ > mix) - angVelZ = mix; - else if(angVelZ < -mix) - angVelZ = -mix;*/ - //This controls how fast and how far the banking occurs - Vector3 bankingRot = new Vector3(angVelZ*(effSquared*mult), 0, 0); - if (bankingRot.X > 3) - bankingRot.X = 3; - else if (bankingRot.X < -3) - bankingRot.X = -3; - bankingRot *= Prim.ForceOrientation; - banking += bankingRot; - } - m_angularMotorVelocity.X *= m_bankingEfficiency == 1 ? 0.0f : 1 - m_bankingEfficiency; - VDetailLog("{0},MoveAngular,Banking,bEff={1},angMotVel={2},banking={3}", - Prim.LocalID, m_bankingEfficiency, m_angularMotorVelocity, banking); - } - - #endregion - - m_lastVertAttractor = vertattr; + // ================================================================== + m_lastVertAttractor = verticalAttractionContribution; // Sum velocities - m_lastAngularVelocity = m_angularMotorVelocity + vertattr + banking + deflection; - - #region Linear Motor Offset + m_lastAngularVelocity = angularMotorContribution + + verticalAttractionContribution + + deflectionContribution + + bankingContribution; + // ================================================================== //Offset section if (m_linearMotorOffset != Vector3.Zero) { @@ -985,8 +915,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin // // The torque created is the linear velocity crossed with the offset - // NOTE: this computation does should be in the linear section - // because there we know the impulse being applied. + // TODO: this computation should be in the linear section + // because that is where we know the impulse being applied. Vector3 torqueFromOffset = Vector3.Zero; // torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse); if (float.IsNaN(torqueFromOffset.X)) @@ -1000,20 +930,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin VDetailLog("{0},BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset); } - #endregion - - if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) - { - m_lastAngularVelocity.X = 0; - m_lastAngularVelocity.Y = 0; - VDetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity); - } - + // ================================================================== if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f)) { m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero. - Prim.ZeroAngularMotion(true); + // TODO: zeroing is good but it also sets values in unmanaged code. Remove the stores when idle. VDetailLog("{0},MoveAngular,zeroAngularMotion,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity); + Prim.ZeroAngularMotion(true); } else { @@ -1021,18 +944,166 @@ namespace OpenSim.Region.Physics.BulletSPlugin // The above calculates the absolute angular velocity needed. Angular velocity is massless. // Since we are stuffing the angular velocity directly into the object, the computed // velocity needs to be scaled by the timestep. - Vector3 applyAngularForce = ((m_lastAngularVelocity * pTimestep) - Prim.ForceRotationalVelocity); + // Also remove any motion that is on the object so added motion is only from vehicle. + Vector3 applyAngularForce = ((m_lastAngularVelocity * pTimestep) + - Prim.ForceRotationalVelocity); + // Unscale the force by the angular factor so it overwhelmes the Bullet additions. Prim.ForceRotationalVelocity = applyAngularForce; - // Decay the angular movement for next time - Vector3 decayamount = (Vector3.One / m_angularFrictionTimescale) * pTimestep; - m_lastAngularVelocity *= Vector3.One - decayamount; - - VDetailLog("{0},MoveAngular,done,newRotVel={1},decay={2},lastAngular={3}", - Prim.LocalID, applyAngularForce, decayamount, m_lastAngularVelocity); + VDetailLog("{0},MoveAngular,done,angMotor={1},vertAttr={2},bank={3},deflect={4},newAngForce={5},lastAngular={6}", + Prim.LocalID, + angularMotorContribution, verticalAttractionContribution, + bankingContribution, deflectionContribution, + applyAngularForce, m_lastAngularVelocity + ); } - } //end MoveAngular + } + public Vector3 ComputeAngularVerticalAttraction(float pTimestep) + { + Vector3 ret = Vector3.Zero; + + // If vertical attaction timescale is reasonable and we applied an angular force last time... + if (m_verticalAttractionTimescale < 500) + { + Vector3 verticalError = Vector3.UnitZ * Prim.ForceOrientation; + verticalError.Normalize(); + m_verticalAttractionMotor.SetCurrent(verticalError); + m_verticalAttractionMotor.SetTarget(Vector3.UnitZ); + ret = m_verticalAttractionMotor.Step(pTimestep); + /* + // Take a vector pointing up and convert it from world to vehicle relative coords. + Vector3 verticalError = Vector3.UnitZ * Prim.ForceOrientation; + verticalError.Normalize(); + + // If vertical attraction correction is needed, the vector that was pointing up (UnitZ) + // is now leaning to one side (rotated around the X axis) and the Y value will + // go from zero (nearly straight up) to one (completely to the side) or leaning + // front-to-back (rotated around the Y axis) and the value of X will be between + // zero and one. + // The value of Z is how far the rotation is off with 1 meaning none and 0 being 90 degrees. + + // If verticalError.Z is negative, the vehicle is upside down. Add additional push. + if (verticalError.Z < 0f) + { + verticalError.X = 2f - verticalError.X; + verticalError.Y = 2f - verticalError.Y; + } + + // Y error means needed rotation around X axis and visa versa. + verticalAttractionContribution.X = verticalError.Y; + verticalAttractionContribution.Y = - verticalError.X; + verticalAttractionContribution.Z = 0f; + + // scale by the time scale and timestep + Vector3 unscaledContrib = verticalAttractionContribution; + verticalAttractionContribution /= m_verticalAttractionTimescale; + verticalAttractionContribution *= pTimestep; + + // apply efficiency + Vector3 preEfficiencyContrib = verticalAttractionContribution; + float efficencySquared = m_verticalAttractionEfficiency * m_verticalAttractionEfficiency; + verticalAttractionContribution *= (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency); + + VDetailLog("{0},MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},preEff={3},eff={4},effSq={5},vertAttr={6}", + Prim.LocalID, verticalError, unscaledContrib, preEfficiencyContrib, + m_verticalAttractionEfficiency, efficencySquared, + verticalAttractionContribution); + */ + + } + return ret; + } + + public Vector3 ComputeAngularDeflection(float pTimestep) + { + Vector3 ret = Vector3.Zero; + + if (m_angularDeflectionEfficiency != 0) + { + // Compute a scaled vector that points in the preferred axis (X direction) + Vector3 scaledDefaultDirection = + new Vector3((pTimestep * 10 * (m_angularDeflectionEfficiency / m_angularDeflectionTimescale)), 0, 0); + // Adding the current vehicle orientation and reference frame displaces the orientation to the frame. + // Rotate the scaled default axix relative to the actual vehicle direction giving where it should point. + Vector3 preferredAxisOfMotion = scaledDefaultDirection * Quaternion.Add(Prim.ForceOrientation, m_referenceFrame); + + // Scale by efficiency and timescale + ret = (preferredAxisOfMotion * (m_angularDeflectionEfficiency) / m_angularDeflectionTimescale) * pTimestep; + + VDetailLog("{0},MoveAngular,Deflection,perfAxis={1},deflection={2}", Prim.LocalID, preferredAxisOfMotion, ret); + + // This deflection computation is not correct. + ret = Vector3.Zero; + } + return ret; + } + + public Vector3 ComputeAngularBanking(float pTimestep) + { + Vector3 ret = Vector3.Zero; + + if (m_bankingEfficiency != 0) + { + Vector3 dir = Vector3.One * Prim.ForceOrientation; + float mult = (m_bankingMix * m_bankingMix) * -1 * (m_bankingMix < 0 ? -1 : 1); + //Changes which way it banks in and out of turns + + //Use the square of the efficiency, as it looks much more how SL banking works + float effSquared = (m_bankingEfficiency * m_bankingEfficiency); + if (m_bankingEfficiency < 0) + effSquared *= -1; //Keep the negative! + + float mix = Math.Abs(m_bankingMix); + if (m_angularMotorVelocity.X == 0) + { + // The vehicle is stopped + /*if (!parent.Orientation.ApproxEquals(this.m_referenceFrame, 0.25f)) + { + Vector3 axisAngle; + float angle; + parent.Orientation.GetAxisAngle(out axisAngle, out angle); + Vector3 rotatedVel = parent.Velocity * parent.Orientation; + if ((rotatedVel.X < 0 && axisAngle.Y > 0) || (rotatedVel.X > 0 && axisAngle.Y < 0)) + m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (1f) * 10; + else + m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (-1f) * 10; + }*/ + } + else + { + ret.Z += (effSquared * (mult * mix)) * (m_angularMotorVelocity.X) * 4; + } + + //If they are colliding, we probably shouldn't shove the prim around... probably + if (!Prim.IsColliding && Math.Abs(m_angularMotorVelocity.X) > mix) + { + float angVelZ = m_angularMotorVelocity.X * -1; + /*if(angVelZ > mix) + angVelZ = mix; + else if(angVelZ < -mix) + angVelZ = -mix;*/ + //This controls how fast and how far the banking occurs + Vector3 bankingRot = new Vector3(angVelZ * (effSquared * mult), 0, 0); + if (bankingRot.X > 3) + bankingRot.X = 3; + else if (bankingRot.X < -3) + bankingRot.X = -3; + bankingRot *= Prim.ForceOrientation; + ret += bankingRot; + } + m_angularMotorVelocity.X *= m_bankingEfficiency == 1 ? 0.0f : 1 - m_bankingEfficiency; + VDetailLog("{0},MoveAngular,Banking,bEff={1},angMotVel={2},effSq={3},mult={4},mix={5},banking={6}", + Prim.LocalID, m_bankingEfficiency, m_angularMotorVelocity, effSquared, mult, mix, ret); + } + return ret; + } + + + // This is from previous instantiations of XXXDynamics.cs. + // Applies roll reference frame. + // TODO: is this the right way to separate the code to do this operation? + // Should this be in MoveAngular()? internal void LimitRotation(float timestep) { Quaternion rotq = Prim.ForceOrientation; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs b/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs new file mode 100755 index 0000000000..663b6f44b7 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs @@ -0,0 +1,191 @@ +/* + * 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 System.Reflection; +using Nini.Config; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + +public struct MaterialAttributes +{ + // Material type values that correspond with definitions for LSL + public enum Material : int + { + Stone = 0, + Metal, + Glass, + Wood, + Flesh, + Plastic, + Rubber, + Light, + // Hereafter are BulletSim additions + Avatar, + NumberOfTypes // the count of types in the enum. + } + // Names must be in the order of the above enum. + public static string[] MaterialNames = { "Stone", "Metal", "Glass", "Wood", + "Flesh", "Plastic", "Rubber", "Light", "Avatar" }; + public static string[] MaterialAttribs = { "Density", "Friction", "Restitution", + "ccdMotionThreshold", "ccdSweptSphereRadius" }; + + public MaterialAttributes(string t, float d, float f, float r, float ccdM, float ccdS) + { + type = t; + density = d; + friction = f; + restitution = r; + ccdMotionThreshold = ccdM; + ccdSweptSphereRadius = ccdS; + } + public string type; + public float density; + public float friction; + public float restitution; + public float ccdMotionThreshold; + public float ccdSweptSphereRadius; +} + +public static class BSMaterials +{ + public static MaterialAttributes[] Attributes; + + static BSMaterials() + { + // Attribute sets for both the non-physical and physical instances of materials. + Attributes = new MaterialAttributes[(int)MaterialAttributes.Material.NumberOfTypes * 2]; + } + + // This is where all the default material attributes are defined. + public static void InitializeFromDefaults(ConfigurationParameters parms) + { + // public static string[] MaterialNames = { "Stone", "Metal", "Glass", "Wood", + // "Flesh", "Plastic", "Rubber", "Light", "Avatar" }; + float dFriction = parms.defaultFriction; + float dRestitution = parms.defaultRestitution; + float dDensity = parms.defaultDensity; + float dCcdM = parms.ccdMotionThreshold; + float dCcdS = parms.ccdSweptSphereRadius; + Attributes[(int)MaterialAttributes.Material.Stone] = + new MaterialAttributes("stone",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Metal] = + new MaterialAttributes("metal",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Glass] = + new MaterialAttributes("glass",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Wood] = + new MaterialAttributes("wood",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Flesh] = + new MaterialAttributes("flesh",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Plastic] = + new MaterialAttributes("plastic",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Rubber] = + new MaterialAttributes("rubber",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Light] = + new MaterialAttributes("light",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Avatar] = + new MaterialAttributes("avatar",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + + Attributes[(int)MaterialAttributes.Material.Stone + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("stonePhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Metal + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("metalPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Glass + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("glassPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Wood + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("woodPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Flesh + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("fleshPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Plastic + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("plasticPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Rubber + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("rubberPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Light + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("lightPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + Attributes[(int)MaterialAttributes.Material.Avatar + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("avatarPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS); + } + + // Under the [BulletSim] section, one can change the individual material + // attribute values. The format of the configuration parameter is: + // ["Physical"] = floatValue + // For instance: + // [BulletSim] + // StoneFriction = 0.2 + // FleshRestitutionPhysical = 0.8 + // Materials can have different parameters for their static and + // physical instantiations. When setting the non-physical value, + // both values are changed. Setting the physical value only changes + // the physical value. + public static void InitializefromParameters(IConfig pConfig) + { + int matType = 0; + foreach (string matName in MaterialAttributes.MaterialNames) + { + foreach (string attribName in MaterialAttributes.MaterialAttribs) + { + string paramName = matName + attribName; + if (pConfig.Contains(paramName)) + { + float paramValue = pConfig.GetFloat(paramName); + SetAttributeValue(matType, attribName, paramValue); + // set the physical value also + SetAttributeValue(matType + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue); + } + paramName += "Physical"; + if (pConfig.Contains(paramName)) + { + float paramValue = pConfig.GetFloat(paramName); + SetAttributeValue(matType + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue); + } + } + matType++; + } + } + + private static void SetAttributeValue(int matType, string attribName, float val) + { + MaterialAttributes thisAttrib = Attributes[matType]; + FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName); + if (fieldInfo != null) + { + fieldInfo.SetValue(thisAttrib, val); + Attributes[matType] = thisAttrib; + } + } + + public static MaterialAttributes GetAttributes(MaterialAttributes.Material type, bool isPhysical) + { + int ind = (int)type; + if (isPhysical) ind += (int)MaterialAttributes.Material.NumberOfTypes; + return Attributes[ind]; + } + +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs index bc6e4c452b..e91bfa8496 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs @@ -1,104 +1,169 @@ -using System; -using System.Collections.Generic; -using System.Text; -using OpenMetaverse; - -namespace OpenSim.Region.Physics.BulletSPlugin -{ -public abstract class BSMotor -{ - public virtual void Reset() { } - public virtual void Zero() { } -} -// Can all the incremental stepping be replaced with motor classes? -public class BSVMotor : BSMotor -{ - public Vector3 FrameOfReference { get; set; } - public Vector3 Offset { get; set; } - - public float TimeScale { get; set; } - public float TargetValueDecayTimeScale { get; set; } - public Vector3 CurrentValueReductionTimescale { get; set; } - public float Efficiency { get; set; } - - public Vector3 TargetValue { get; private set; } - public Vector3 CurrentValue { get; private set; } - - - - BSVMotor(float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency) - { - TimeScale = timeScale; - TargetValueDecayTimeScale = decayTimeScale; - CurrentValueReductionTimescale = frictionTimeScale; - Efficiency = efficiency; - } - public void SetCurrent(Vector3 current) - { - CurrentValue = current; - } - public void SetTarget(Vector3 target) - { - TargetValue = target; - } - public Vector3 Step(float timeStep) - { - if (CurrentValue.LengthSquared() > 0.001f) - { - // Vector3 origDir = Target; // DEBUG - // Vector3 origVel = CurrentValue; // DEBUG - - // Add (desiredVelocity - currentAppliedVelocity) / howLongItShouldTakeToComplete - Vector3 addAmount = (TargetValue - CurrentValue)/(TargetValue) * timeStep; - CurrentValue += addAmount; - - float decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep; - TargetValue *= (1f - decayFactor); - - Vector3 frictionFactor = (Vector3.One / CurrentValueReductionTimescale) * timeStep; - CurrentValue *= (Vector3.One - frictionFactor); - } - else - { - // if what remains of direction is very small, zero it. - TargetValue = Vector3.Zero; - CurrentValue = Vector3.Zero; - - // VDetailLog("{0},MoveLinear,zeroed", Prim.LocalID); - } - return CurrentValue; - } -} - -public class BSFMotor : BSMotor -{ - public float TimeScale { get; set; } - public float DecayTimeScale { get; set; } - public float Friction { get; set; } - public float Efficiency { get; set; } - - public float Target { get; private set; } - public float CurrentValue { get; private set; } - - BSFMotor(float timeScale, float decayTimescale, float friction, float efficiency) - { - } - public void SetCurrent(float target) - { - } - public void SetTarget(float target) - { - } - public float Step(float timeStep) - { - return 0f; - } -} -public class BSPIDMotor : BSMotor -{ - // TODO: write and use this one - BSPIDMotor() - { - } -} -} +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public abstract class BSMotor +{ + // Timescales and other things can be turned off by setting them to 'infinite'. + public const float Infinite = 10000f; + public readonly static Vector3 InfiniteVector = new Vector3(BSMotor.Infinite, BSMotor.Infinite, BSMotor.Infinite); + + public BSMotor(string useName) + { + UseName = useName; + PhysicsScene = null; + } + public virtual void Reset() { } + public virtual void Zero() { } + + public string UseName { get; private set; } + // Used only for outputting debug information. Might not be set so check for null. + public BSScene PhysicsScene { get; set; } + protected void MDetailLog(string msg, params Object[] parms) + { + if (PhysicsScene != null) + { + if (PhysicsScene.VehicleLoggingEnabled) + { + PhysicsScene.DetailLog(msg, parms); + } + } + } +} +// Can all the incremental stepping be replaced with motor classes? +public class BSVMotor : BSMotor +{ + public Vector3 FrameOfReference { get; set; } + public Vector3 Offset { get; set; } + + public float TimeScale { get; set; } + public float TargetValueDecayTimeScale { get; set; } + public Vector3 FrictionTimescale { get; set; } + public float Efficiency { get; set; } + + public Vector3 TargetValue { get; private set; } + public Vector3 CurrentValue { get; private set; } + + public BSVMotor(string useName) + : base(useName) + { + TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite; + Efficiency = 1f; + FrictionTimescale = BSMotor.InfiniteVector; + CurrentValue = TargetValue = Vector3.Zero; + } + public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency) + : this(useName) + { + TimeScale = timeScale; + TargetValueDecayTimeScale = decayTimeScale; + FrictionTimescale = frictionTimeScale; + Efficiency = efficiency; + CurrentValue = TargetValue = Vector3.Zero; + } + public void SetCurrent(Vector3 current) + { + CurrentValue = current; + } + public void SetTarget(Vector3 target) + { + TargetValue = target; + } + public Vector3 Step(float timeStep) + { + Vector3 returnCurrent = Vector3.Zero; + if (!CurrentValue.ApproxEquals(TargetValue, 0.01f)) + { + Vector3 origTarget = TargetValue; // DEBUG + Vector3 origCurrVal = CurrentValue; // DEBUG + + // Addition = (desiredVector - currentAppliedVector) / secondsItShouldTakeToComplete + Vector3 addAmount = (TargetValue - CurrentValue)/TimeScale * timeStep; + CurrentValue += addAmount; + + returnCurrent = CurrentValue; + + // The desired value reduces to zero which also reduces the difference with current. + // If the decay time is infinite, don't decay at all. + float decayFactor = 0f; + if (TargetValueDecayTimeScale != BSMotor.Infinite) + { + decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep; + TargetValue *= (1f - decayFactor); + } + + Vector3 frictionFactor = Vector3.Zero; + if (FrictionTimescale != BSMotor.InfiniteVector) + { + // frictionFactor = (Vector3.One / FrictionTimescale) * timeStep; + frictionFactor.X = FrictionTimescale.X == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.X) * timeStep; + frictionFactor.Y = FrictionTimescale.Y == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.Y) * timeStep; + frictionFactor.Z = FrictionTimescale.Z == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.Z) * timeStep; + CurrentValue *= (Vector3.One - frictionFactor); + } + + MDetailLog("{0},BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},timeScale={5},addAmnt={6},targetDecay={7},decayFact={8},fricTS={9},frictFact={10}", + BSScene.DetailLogZero, UseName, origCurrVal, origTarget, + timeStep, TimeScale, addAmount, + TargetValueDecayTimeScale, decayFactor, + FrictionTimescale, frictionFactor); + MDetailLog("{0},BSVMotor.Step,nonZero,{1},curr={2},target={3},add={4},decay={5},frict={6},ret={7}", + BSScene.DetailLogZero, UseName, CurrentValue, TargetValue, + addAmount, decayFactor, frictionFactor, returnCurrent); + } + else + { + // Difference between what we have and target is small. Motor is done. + CurrentValue = Vector3.Zero; + TargetValue = Vector3.Zero; + + MDetailLog("{0},BSVMotor.Step,zero,{1},curr={2},target={3},ret={4}", + BSScene.DetailLogZero, UseName, TargetValue, CurrentValue, returnCurrent); + + } + return returnCurrent; + } + public override string ToString() + { + return String.Format("<{0},curr={1},targ={2},decayTS={3},frictTS={4}>", + UseName, CurrentValue, TargetValue, TargetValueDecayTimeScale, FrictionTimescale); + } +} + +public class BSFMotor : BSMotor +{ + public float TimeScale { get; set; } + public float DecayTimeScale { get; set; } + public float Friction { get; set; } + public float Efficiency { get; set; } + + public float Target { get; private set; } + public float CurrentValue { get; private set; } + + public BSFMotor(string useName, float timeScale, float decayTimescale, float friction, float efficiency) + : base(useName) + { + } + public void SetCurrent(float target) + { + } + public void SetTarget(float target) + { + } + public float Step(float timeStep) + { + return 0f; + } +} +public class BSPIDMotor : BSMotor +{ + // TODO: write and use this one + public BSPIDMotor(string useName) + : base(useName) + { + } +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 2b3fa2580b..3fb0300cfc 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -253,8 +253,9 @@ public sealed class BSPrim : BSPhysObject // Zero some other properties in the physics engine PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() { - BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); - BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); + // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity); + BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, _rotationalVelocity); + BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity); }); } @@ -329,7 +330,7 @@ public sealed class BSPrim : BSPhysObject if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) { - float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); + float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position); // TODO: a floating motor so object will bob in the water if (Math.Abs(Position.Z - waterHeight) > 0.1f) { @@ -342,13 +343,12 @@ public sealed class BSPrim : BSPhysObject // TODO: check for out of bounds // The above code computes a force to apply to correct any out-of-bounds problems. Apply same. + // TODO: This should be intergrated with a geneal physics action mechanism. + // TODO: This should be moderated with PID'ness. if (ret) { - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.PositionSanityCheck:belowTerrain", delegate() - { - // Apply upforce and overcome gravity. - ForceVelocity = ForceVelocity + upForce - PhysicsScene.DefaultGravity; - }); + // Apply upforce and overcome gravity. + AddForce(upForce - PhysicsScene.DefaultGravity, false, inTaintTime); } return ret; } @@ -1381,54 +1381,16 @@ public sealed class BSPrim : BSPhysObject public override void UpdateProperties(EntityProperties entprop) { - /* - UpdatedProperties changed = 0; - // assign to the local variables so the normal set action does not happen - // if (_position != entprop.Position) - if (!_position.ApproxEquals(entprop.Position, POSITION_TOLERANCE)) - { - _position = entprop.Position; - changed |= UpdatedProperties.Position; - } - // if (_orientation != entprop.Rotation) - if (!_orientation.ApproxEquals(entprop.Rotation, ROTATION_TOLERANCE)) - { - _orientation = entprop.Rotation; - changed |= UpdatedProperties.Rotation; - } - // if (_velocity != entprop.Velocity) - if (!_velocity.ApproxEquals(entprop.Velocity, VELOCITY_TOLERANCE)) - { - _velocity = entprop.Velocity; - changed |= UpdatedProperties.Velocity; - } - // if (_acceleration != entprop.Acceleration) - if (!_acceleration.ApproxEquals(entprop.Acceleration, ACCELERATION_TOLERANCE)) - { - _acceleration = entprop.Acceleration; - changed |= UpdatedProperties.Acceleration; - } - // if (_rotationalVelocity != entprop.RotationalVelocity) - if (!_rotationalVelocity.ApproxEquals(entprop.RotationalVelocity, ROTATIONAL_VELOCITY_TOLERANCE)) - { - _rotationalVelocity = entprop.RotationalVelocity; - changed |= UpdatedProperties.RotationalVel; - } - if (changed != 0) - { - // Only update the position of single objects and linkset roots - if (Linkset.IsRoot(this)) - { - base.RequestPhysicsterseUpdate(); - } - } - */ - - // Don't check for damping here -- it's done in BulletSim and SceneObjectPart. - // Updates only for individual prims and for the root object of a linkset. if (Linkset.IsRoot(this)) { + // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet + // TODO: handle physics introduced by Bullet with computed vehicle physics. + if (_vehicle.IsActive) + { + entprop.RotationalVelocity = OMV.Vector3.Zero; + } + // Assign directly to the local variables so the normal set action does not happen _position = entprop.Position; _orientation = entprop.Rotation; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 27a78d1f2a..0c806118cc 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -39,23 +39,10 @@ using log4net; using OpenMetaverse; // TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) -// Test sculpties (verified that they don't work) -// Compute physics FPS reasonably // Based on material, set density and friction -// Don't use constraints in linksets of non-physical objects. Means having to move children manually. -// Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly? -// In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground) -// At the moment, physical and phantom causes object to drop through the terrain -// Physical phantom objects and related typing (collision options ) -// Check out llVolumeDetect. Must do something for that. -// Use collision masks for collision with terrain and phantom objects // More efficient memory usage when passing hull information from BSPrim to BulletSim -// Should prim.link() and prim.delink() membership checking happen at taint time? -// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once. // Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect // Implement LockAngularMotion -// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation) -// Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet. // Add PID movement operations. What does ScenePresence.MoveToTarget do? // Check terrain size. 128 or 127? // Raycast @@ -140,7 +127,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters public const uint GROUNDPLANE_ID = 1; public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here - private float m_waterLevel; + public float SimpleWaterLevel { get; set; } public BSTerrainManager TerrainManager { get; private set; } public ConfigurationParameters Params @@ -195,6 +182,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters private string m_physicsLoggingDir; private string m_physicsLoggingPrefix; private int m_physicsLoggingFileMinutes; + private bool m_physicsLoggingDoFlush; // 'true' of the vehicle code is to log lots of details public bool VehicleLoggingEnabled { get; private set; } @@ -234,6 +222,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters if (m_physicsLoggingEnabled) { PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); + PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output error messages. } else { @@ -302,12 +291,20 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); + m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false); // Very detailed logging for vehicle debugging VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); // Do any replacements in the parameters m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); } + + // The material characteristics. + BSMaterials.InitializeFromDefaults(Params); + if (pConfig != null) + { + BSMaterials.InitializefromParameters(pConfig); + } } } @@ -499,7 +496,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters try { - if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG + // if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep, @@ -508,7 +505,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime); DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}", DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount); - if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG + // if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG } catch (Exception e) { @@ -520,9 +517,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters collidersCount = 0; } - // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in + // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in. - // Get a value for 'now' so all the collision and update routines don't have to get their own + // Get a value for 'now' so all the collision and update routines don't have to get their own. SimulationNowTime = Util.EnvironmentTickCount(); // If there were collisions, process them by sending the event to the prim. @@ -568,6 +565,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters ObjectsWithCollisions.Remove(po); ObjectsWithNoMoreCollisions.Clear(); } + // Done with collisions. // If any of the objects had updated properties, tell the object it has been changed by the physics engine if (updatedEntityCount > 0) @@ -591,9 +589,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters // The physics engine returns the number of milliseconds it simulated this call. // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. - // We multiply by 55 to give a recognizable running rate (55 or less). - return numSubSteps * m_fixedTimeStep * 1000 * 55; - // return timeStep * 1000 * 55; + // Multiply by 55 to give a nominal frame rate of 55. + return (float)numSubSteps * m_fixedTimeStep * 1000f * 55f; } // Something has collided @@ -639,12 +636,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters public override void SetWaterLevel(float baseheight) { - m_waterLevel = baseheight; - } - // Someday.... - public float GetWaterLevelAtXYZ(Vector3 loc) - { - return m_waterLevel; + SimpleWaterLevel = baseheight; } public override void DeleteTerrain() @@ -1069,7 +1061,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters (s,p,l,v) => { s.PID_P = v; } ), new ParameterDefn("DefaultFriction", "Friction factor used on new objects", - 0.5f, + 0.2f, (s,cf,p,v) => { s.m_params[0].defaultFriction = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].defaultFriction; }, (s,p,l,v) => { s.m_params[0].defaultFriction = v; } ), @@ -1084,7 +1076,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters (s) => { return s.m_params[0].defaultRestitution; }, (s,p,l,v) => { s.m_params[0].defaultRestitution = v; } ), new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)", - 0f, + 0.04f, (s,cf,p,v) => { s.m_params[0].collisionMargin = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].collisionMargin; }, (s,p,l,v) => { s.m_params[0].collisionMargin = v; } ), @@ -1151,7 +1143,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters (s) => { return s.m_params[0].terrainImplementation; }, (s,p,l,v) => { s.m_params[0].terrainImplementation = v; } ), new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , - 0.5f, + 0.3f, (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].terrainFriction; }, (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ), @@ -1165,13 +1157,19 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].terrainRestitution; }, (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ), + new ParameterDefn("TerrainCollisionMargin", "Margin where collision checking starts" , + 0.04f, + (s,cf,p,v) => { s.m_params[0].terrainCollisionMargin = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].terrainCollisionMargin; }, + (s,p,l,v) => { s.m_params[0].terrainCollisionMargin = v; /* TODO: set on real terrain */ } ), + new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", 0.2f, (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].avatarFriction; }, (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ), new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.", - 10f, + 10.0f, (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].avatarStandingFriction; }, (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ), @@ -1206,6 +1204,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters (s) => { return s.m_params[0].avatarContactProcessingThreshold; }, (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ), + new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)", + 0.95f, + (s,cf,p,v) => { s.m_params[0].vehicleAngularDamping = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].vehicleAngularDamping; }, + (s,p,l,v) => { s.m_params[0].vehicleAngularDamping = v; } ), new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", 0f, @@ -1487,7 +1490,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters { PhysicsLogging.Write(msg, args); // Add the Flush() if debugging crashes. Gets all the messages written out. - // PhysicsLogging.Flush(); + if (m_physicsLoggingDoFlush) PhysicsLogging.Flush(); } // 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/BSTerrainHeightmap.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs index 3ca756cba9..0cb151e285 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs @@ -93,7 +93,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys { m_mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, m_mapInfo.ID, m_mapInfo.minCoords, m_mapInfo.maxCoords, - m_mapInfo.heightMap, BSTerrainManager.TERRAIN_COLLISION_MARGIN); + m_mapInfo.heightMap, PhysicsScene.Params.terrainCollisionMargin); // Create the terrain shape from the mapInfo m_mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(m_mapInfo.Ptr), @@ -148,7 +148,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys } // The passed position is relative to the base of the region. - public override float GetHeightAtXYZ(Vector3 pos) + public override float GetTerrainHeightAtXYZ(Vector3 pos) { float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; @@ -166,5 +166,11 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys } return ret; } + + // The passed position is relative to the base of the region. + public override float GetWaterLevelAtXYZ(Vector3 pos) + { + return PhysicsScene.SimpleWaterLevel; + } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index 23fcfd33aa..17d9536c42 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -62,7 +62,8 @@ public abstract class BSTerrainPhys : IDisposable ID = id; } public abstract void Dispose(); - public abstract float GetHeightAtXYZ(Vector3 pos); + public abstract float GetTerrainHeightAtXYZ(Vector3 pos); + public abstract float GetWaterLevelAtXYZ(Vector3 pos); } // ========================================================================================== @@ -75,13 +76,12 @@ public sealed class BSTerrainManager public const float HEIGHT_INITIALIZATION = 24.987f; public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f; public const float HEIGHT_GETHEIGHT_RET = 24.765f; + public const float WATER_HEIGHT_GETHEIGHT_RET = 19.998f; // If the min and max height are equal, we reduce the min by this // amount to make sure that a bounding box is built for the terrain. public const float HEIGHT_EQUAL_FUDGE = 0.2f; - public const float TERRAIN_COLLISION_MARGIN = 0.0f; - // Until the whole simulator is changed to pass us the region size, we rely on constants. public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); @@ -129,7 +129,8 @@ public sealed class BSTerrainManager { // The ground plane is here to catch things that are trying to drop to negative infinity BulletShape groundPlaneShape = new BulletShape( - BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN), + BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, + PhysicsScene.Params.terrainCollisionMargin), BSPhysicsShapeType.SHAPE_GROUNDPLANE); m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID, @@ -165,17 +166,22 @@ public sealed class BSTerrainManager // Release all the terrain we have allocated public void ReleaseTerrain() { - foreach (KeyValuePair kvp in m_terrains) + lock (m_terrains) { - kvp.Value.Dispose(); + foreach (KeyValuePair kvp in m_terrains) + { + kvp.Value.Dispose(); + } + m_terrains.Clear(); } - m_terrains.Clear(); } // The simulator wants to set a new heightmap for the terrain. public void SetTerrain(float[] heightMap) { float[] localHeightMap = heightMap; - PhysicsScene.TaintedObject("TerrainManager.SetTerrain", delegate() + // If there are multiple requests for changes to the same terrain between ticks, + // only do that last one. + PhysicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate() { if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null) { @@ -211,6 +217,7 @@ public sealed class BSTerrainManager // 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 above does suggest that some simplification/refactoring is in order.) + // Called during taint-time. private void UpdateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool inTaintTime) { @@ -220,7 +227,7 @@ public sealed class BSTerrainManager // Find high and low points of passed heightmap. // The min and max passed in is usually the area objects can be in (maximum // object height, for instance). The terrain wants the bounding box for the - // terrain so we replace passed min and max Z with the actual terrain min/max Z. + // terrain so replace passed min and max Z with the actual terrain min/max Z. float minZ = float.MaxValue; float maxZ = float.MinValue; foreach (float height in heightMap) @@ -238,15 +245,15 @@ public sealed class BSTerrainManager Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f); - BSTerrainPhys terrainPhys; - if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys)) + lock (m_terrains) { - // There is already a terrain in this spot. Free the old and build the new. - DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}", - BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords); - - PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateTerrain:UpdateExisting", delegate() + BSTerrainPhys terrainPhys; + if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys)) { + // There is already a terrain in this spot. Free the old and build the new. + DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}", + BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords); + // Remove old terrain from the collection m_terrains.Remove(terrainRegionBase); // Release any physical memory it may be using. @@ -271,35 +278,24 @@ public sealed class BSTerrainManager // I hate doing this, but just bail return; } - }); - } - else - { - // We don't know about this terrain so either we are creating a new terrain or - // our mega-prim child is giving us a new terrain to add to the phys world - - // if this is a child terrain, calculate a unique terrain id - uint newTerrainID = id; - if (newTerrainID >= BSScene.CHILDTERRAIN_ID) - newTerrainID = ++m_terrainCount; - - float[] heightMapX = heightMap; - Vector3 minCoordsX = minCoords; - Vector3 maxCoordsX = maxCoords; - - DetailLog("{0},UpdateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}", - BSScene.DetailLogZero, newTerrainID, minCoords, minCoords); - - // Code that must happen at taint-time - PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateTerrain:NewTerrain", delegate() + } + else { - DetailLog("{0},UpdateTerrain:NewTerrain,taint,baseX={1},baseY={2}", - BSScene.DetailLogZero, minCoordsX.X, minCoordsX.Y); + // We don't know about this terrain so either we are creating a new terrain or + // our mega-prim child is giving us a new terrain to add to the phys world + + // if this is a child terrain, calculate a unique terrain id + uint newTerrainID = id; + if (newTerrainID >= BSScene.CHILDTERRAIN_ID) + newTerrainID = ++m_terrainCount; + + DetailLog("{0},UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}", + BSScene.DetailLogZero, newTerrainID, minCoords, minCoords); BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords); m_terrains.Add(terrainRegionBase, newTerrainPhys); m_terrainModified = true; - }); + } } } @@ -349,6 +345,7 @@ public sealed class BSTerrainManager // with the same parameters as last time. if (!m_terrainModified && lastHeightTX == tX && lastHeightTY == tY) return lastHeight; + m_terrainModified = false; lastHeightTX = tX; lastHeightTY = tY; @@ -358,23 +355,50 @@ public sealed class BSTerrainManager int offsetY = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y; Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f); - BSTerrainPhys physTerrain; - if (m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain)) + lock (m_terrains) { - ret = physTerrain.GetHeightAtXYZ(loc - terrainBaseXYZ); - DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,loc={1},base={2},height={3}", - BSScene.DetailLogZero, loc, terrainBaseXYZ, ret); + BSTerrainPhys physTerrain; + if (m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain)) + { + ret = physTerrain.GetTerrainHeightAtXYZ(loc - terrainBaseXYZ); + } + else + { + PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", + LogHeader, PhysicsScene.RegionName, tX, tY); + } } - else - { - PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", - LogHeader, PhysicsScene.RegionName, tX, tY); - } - m_terrainModified = false; lastHeight = ret; return ret; } + public float GetWaterLevelAtXYZ(Vector3 pos) + { + float ret = WATER_HEIGHT_GETHEIGHT_RET; + + float tX = pos.X; + float tY = pos.Y; + + Vector3 terrainBaseXYZ = Vector3.Zero; + terrainBaseXYZ.X = ((int)(tX / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X; + terrainBaseXYZ.Y = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y; + + lock (m_terrains) + { + BSTerrainPhys physTerrain; + if (m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain)) + { + ret = physTerrain.GetWaterLevelAtXYZ(pos); + } + else + { + PhysicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: region={1}, x={2}, y={3}", + LogHeader, PhysicsScene.RegionName, tX, tY); + } + } + return ret; + } + // Although no one seems to check this, I do support combining. public bool SupportsCombining() { diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs index dca7150931..7e93ab4623 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs @@ -88,9 +88,11 @@ public sealed class BSTerrainMesh : BSTerrainPhys // Something is very messed up and a crash is in our future. return; } + PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,indices={1},indSz={2},vertices={3},vertSz={4}", + ID, indicesCount, indices.Length, verticesCount, vertices.Length); m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr, - indicesCount, indices, verticesCount, vertices), + indicesCount, indices, verticesCount, vertices), BSPhysicsShapeType.SHAPE_MESH); if (m_terrainShape.ptr == IntPtr.Zero) { @@ -122,10 +124,10 @@ public sealed class BSTerrainMesh : BSTerrainPhys // Static objects are not very massive. BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero); - // Return the new terrain to the world of physical objects + // Put the new terrain to the world of physical objects BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr); - // redo its bounding box now that it is in the world + // Redo its bounding box now that it is in the world BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr); BulletSimAPI.SetCollisionFilterMask2(m_terrainBody.ptr, @@ -146,7 +148,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys } } - public override float GetHeightAtXYZ(Vector3 pos) + public override float GetTerrainHeightAtXYZ(Vector3 pos) { // For the moment use the saved heightmap to get the terrain height. // TODO: raycast downward to find the true terrain below the position. @@ -167,6 +169,12 @@ public sealed class BSTerrainMesh : BSTerrainPhys return ret; } + // The passed position is relative to the base of the region. + public override float GetWaterLevelAtXYZ(Vector3 pos) + { + return PhysicsScene.SimpleWaterLevel; + } + // Convert the passed heightmap to mesh information suitable for CreateMeshShape2(). // Return 'true' if successfully created. public static bool ConvertHeightmapToMesh( @@ -188,6 +196,11 @@ public sealed class BSTerrainMesh : BSTerrainPhys // Simple mesh creation which assumes magnification == 1. // TODO: do a more general solution that scales, adds new vertices and smoothes the result. + // Create an array of vertices that is sizeX+1 by sizeY+1 (note the loop + // from zero to <= sizeX). The triangle indices are then generated as two triangles + // per heightmap point. There are sizeX by sizeY of these squares. The extra row and + // column of vertices are used to complete the triangles of the last row and column + // of the heightmap. try { // One vertice per heightmap value plus the vertices off the top and bottom edge. @@ -200,16 +213,18 @@ public sealed class BSTerrainMesh : BSTerrainPhys float magY = (float)sizeY / extentY; physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}", BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY); + float minHeight = float.MaxValue; // Note that sizeX+1 vertices are created since there is land between this and the next region. for (int yy = 0; yy <= sizeY; yy++) { - for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we got through sizeX + 1 times + for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we go around sizeX + 1 times { int offset = yy * sizeX + xx; - // Extend the height from the height from the last row or column + // Extend the height with the height from the last row or column if (yy == sizeY) offset -= sizeX; if (xx == sizeX) offset -= 1; float height = heightMap[offset]; + minHeight = Math.Min(minHeight, height); vertices[verticesCount + 0] = (float)xx * magX + extentBase.X; vertices[verticesCount + 1] = (float)yy * magY + extentBase.Y; vertices[verticesCount + 2] = height + extentBase.Z; @@ -217,14 +232,12 @@ public sealed class BSTerrainMesh : BSTerrainPhys } } verticesCount = verticesCount / 3; - physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,completeVerts,verCount={1}", - BSScene.DetailLogZero, verticesCount); for (int yy = 0; yy < sizeY; yy++) { for (int xx = 0; xx < sizeX; xx++) { - int offset = yy * sizeX + xx; + int offset = yy * (sizeX + 1) + xx; // Each vertices is presumed to be the upper left corner of a box of two triangles indices[indicesCount + 0] = offset; indices[indicesCount + 1] = offset + 1; @@ -235,8 +248,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys indicesCount += 6; } } - physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,completeIndices,indCount={1}", // DEEBUG DEBUG DEBUG - LogHeader, indicesCount); // DEBUG + ret = true; } catch (Exception e) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs index e60a7604a8..1e003e6e8c 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs @@ -287,6 +287,8 @@ public struct ConfigurationParameters public float terrainFriction; public float terrainHitFraction; public float terrainRestitution; + public float terrainCollisionMargin; + public float avatarFriction; public float avatarStandingFriction; public float avatarDensity; @@ -296,6 +298,8 @@ public struct ConfigurationParameters public float avatarCapsuleHeight; public float avatarContactProcessingThreshold; + public float vehicleAngularDamping; + public float maxPersistantManifoldPoolSize; public float maxCollisionAlgorithmPoolSize; public float shouldDisableContactPoolDynamicAllocation; @@ -353,7 +357,7 @@ public enum CollisionFlags : uint CF_CHARACTER_OBJECT = 1 << 4, CF_DISABLE_VISUALIZE_OBJECT = 1 << 5, CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6, - // Following used by BulletSim to control collisions + // Following used by BulletSim to control collisions and updates BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10, BS_FLOATS_ON_WATER = 1 << 11, BS_NONE = 0, @@ -481,6 +485,9 @@ public static extern IntPtr BuildNativeShape2(IntPtr world, ShapeData shapeData) [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern bool IsNativeShape2(IntPtr shape); +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetShapeCollisionMargin(IntPtr shape, float margin); + [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale); diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs index f629c4d60b..9262a9e936 100644 --- a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs +++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs @@ -321,6 +321,9 @@ namespace OpenSim.Region.Physics.Meshing if (primShape.SculptData.Length <= 0) { + // XXX: At the moment we can not log here since ODEPrim, for instance, ends up triggering this + // method twice - once before it has loaded sculpt data from the asset service and once afterwards. + // The first time will always call with unloaded SculptData if this needs to be uploaded. // m_log.ErrorFormat("[MESH]: asset data for {0} is zero length", primName); return false; } diff --git a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs index a59f63fcdd..d09aa628c3 100644 --- a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs +++ b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs @@ -3367,6 +3367,11 @@ Console.WriteLine(" JointCreateFixed"); _pbs.SculptData = new byte[asset.Data.Length]; asset.Data.CopyTo(_pbs.SculptData, 0); // m_assetFailed = false; + +// m_log.DebugFormat( +// "[ODE PRIM]: Received mesh/sculpt data asset {0} with {1} bytes for {2} at {3} in {4}", +// _pbs.SculptTexture, _pbs.SculptData.Length, Name, _position, _parent_scene.Name); + m_taintshape = true; _parent_scene.AddPhysicsActorTaint(this); } diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index 3c4110b78c..d83896e1b8 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -217,10 +217,10 @@ ; to false if you have compatibility problems. ;CacheSculptMaps = true - ; Choose one of the physics engines below - ; OpenDynamicsEngine is by some distance the most developed physics engine - ; basicphysics effectively does not model physics at all, making all objects phantom - + ; Choose one of the physics engines below. + ; OpenDynamicsEngine is by some distance the most developed physics engine. + ; BulletSim is a high performance, up-and-coming physics engine. + ; basicphysics effectively does not model physics at all, making all objects phantom. physics = OpenDynamicsEngine ;physics = basicphysics ;physics = POS @@ -904,15 +904,18 @@ [BulletSim] ; World parameters - DefaultFriction = 0.50 + DefaultFriction = 0.20 DefaultDensity = 10.000006836 DefaultRestitution = 0.0 Gravity = -9.80665 - TerrainFriction = 0.50 - TerrainHitFriction = 0.8 + TerrainFriction = 0.30 + TerrainHitFraction = 0.8 TerrainRestitution = 0 + TerrainCollisionMargin = 0.04 + AvatarFriction = 0.2 + AvatarStandingFriction = 0.99 AvatarRestitution = 0.0 AvatarDensity = 60.0 AvatarCapsuleWidth = 0.6 @@ -926,27 +929,15 @@ LinearDamping = 0.0 AngularDamping = 0.0 DeactivationTime = 0.2 - LinearSleepingThreshold = 0.8 - AngularSleepingThreshold = 1.0 - CcdMotionThreshold = 0.0 - CcdSweptSphereRadius = 0.0 - ContactProcessingThreshold = 0.1 - ; If setting a pool size, also disable dynamic allocation (default pool size is 4096 with dynamic alloc) - MaxPersistantManifoldPoolSize = 0 - ShouldDisableContactPoolDynamicAllocation = False - ShouldForceUpdateAllAabbs = False - ShouldRandomizeSolverOrder = True - ShouldSplitSimulationIslands = True - ShouldEnableFrictionCaching = False - NumberOfSolverIterations = 0 + CollisionMargin = 0.04 ; Linkset constraint parameters + LinkImplementation = 1 ; 0=constraint, 1=compound LinkConstraintUseFrameOffset = False LinkConstraintEnableTransMotor = True LinkConstraintTransMotorMaxVel = 5.0 LinkConstraintTransMotorMaxForce = 0.1 - ; Whether to mesh sculpties MeshSculptedPrim = true diff --git a/bin/lib32/BulletSim.dll b/bin/lib32/BulletSim.dll index 2ae1c75e3d..3f5011a928 100755 Binary files a/bin/lib32/BulletSim.dll and b/bin/lib32/BulletSim.dll differ diff --git a/bin/lib32/libBulletSim.so b/bin/lib32/libBulletSim.so index d4852a55bd..46d40feb89 100755 Binary files a/bin/lib32/libBulletSim.so and b/bin/lib32/libBulletSim.so differ diff --git a/bin/lib64/BulletSim.dll b/bin/lib64/BulletSim.dll index 77cf7e3f28..27a807cd50 100755 Binary files a/bin/lib64/BulletSim.dll and b/bin/lib64/BulletSim.dll differ diff --git a/bin/lib64/libBulletSim.so b/bin/lib64/libBulletSim.so index 4ec62b2a66..1e82b3d37e 100755 Binary files a/bin/lib64/libBulletSim.so and b/bin/lib64/libBulletSim.so differ