From f783b9169fbc0544ec6c634900cb34bf48c6b2a9 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Fri, 22 Mar 2013 16:50:56 -0700 Subject: BulletSim: parameterize C# HACD hull creation. Add feature of reducing max hull count for simple (non-cut prims) meshes. --- OpenSim/Region/Physics/BulletSPlugin/BSParam.cs | 33 +++++++++++++ .../Physics/BulletSPlugin/BSShapeCollection.cs | 55 ++++++++++++++++------ 2 files changed, 74 insertions(+), 14 deletions(-) (limited to 'OpenSim/Region/Physics') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs index 4d89a88..26d2d60 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs @@ -142,6 +142,14 @@ public static class BSParam public static float VehicleAngularBankingTimescaleFudge { get; private set; } public static bool VehicleDebuggingEnabled { get; private set; } + // Convex Hulls + public static int CSHullMaxDepthSplit { get; private set; } + public static int CSHullMaxDepthSplitForSimpleShapes { get; private set; } + public static float CSHullConcavityThresholdPercent { get; private set; } + public static float CSHullVolumeConservationThresholdPercent { get; private set; } + public static int CSHullMaxVertices { get; private set; } + public static float CSHullMaxSkinWidth { get; private set; } + // Linkset implementation parameters public static float LinksetImplementation { get; private set; } public static bool LinkConstraintUseFrameOffset { get; private set; } @@ -623,6 +631,31 @@ public static class BSParam (s) => { return GlobalContactBreakingThreshold; }, (s,v) => { GlobalContactBreakingThreshold = v; s.UnmanagedParams[0].globalContactBreakingThreshold = v; } ), + new ParameterDefn("CSHullMaxDepthSplit", "CS impl: max depth to split for hull. 1-10 but > 7 is iffy", + 7, + (s) => { return CSHullMaxDepthSplit; }, + (s,v) => { CSHullMaxDepthSplit = v; } ), + new ParameterDefn("CSHullMaxDepthSplitForSimpleShapes", "CS impl: max depth setting for simple prim shapes", + 2, + (s) => { return CSHullMaxDepthSplitForSimpleShapes; }, + (s,v) => { CSHullMaxDepthSplitForSimpleShapes = v; } ), + new ParameterDefn("CSHullConcavityThresholdPercent", "CS impl: concavity threshold percent (0-20)", + 5f, + (s) => { return CSHullConcavityThresholdPercent; }, + (s,v) => { CSHullConcavityThresholdPercent = v; } ), + new ParameterDefn("CSHullVolumeConservationThresholdPercent", "percent volume conservation to collapse hulls (0-30)", + 5f, + (s) => { return CSHullVolumeConservationThresholdPercent; }, + (s,v) => { CSHullVolumeConservationThresholdPercent = v; } ), + new ParameterDefn("CSHullMaxVertices", "CS impl: maximum number of vertices in output hulls. Keep < 50.", + 32, + (s) => { return CSHullMaxVertices; }, + (s,v) => { CSHullMaxVertices = v; } ), + new ParameterDefn("CSHullMaxSkinWidth", "CS impl: skin width to apply to output hulls.", + 0, + (s) => { return CSHullMaxSkinWidth; }, + (s,v) => { CSHullMaxSkinWidth = v; } ), + new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)", (float)BSLinkset.LinksetImplementation.Compound, (s) => { return LinksetImplementation; }, diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index b16bc10..457f204 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -447,17 +447,10 @@ public sealed class BSShapeCollection : IDisposable // If the prim attributes are simple, this could be a simple Bullet native shape if (!haveShape + && nativeShapePossible && pbs != null && !pbs.SculptEntry - && nativeShapePossible - && ((pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim) - || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 - && pbs.ProfileHollow == 0 - && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0 - && pbs.PathBegin == 0 && pbs.PathEnd == 0 - && pbs.PathTaperX == 0 && pbs.PathTaperY == 0 - && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 - && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) ) + && ((pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim) || PrimHasNoCuts(pbs)) ) { // Get the scale of any existing shape so we can see if the new shape is same native type and same size. OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero; @@ -508,6 +501,18 @@ public sealed class BSShapeCollection : IDisposable return ret; } + // return 'true' if this shape description does not include any cutting or twisting. + private bool PrimHasNoCuts(PrimitiveBaseShape pbs) + { + return pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 + && pbs.ProfileHollow == 0 + && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0 + && pbs.PathBegin == 0 && pbs.PathEnd == 0 + && pbs.PathTaperX == 0 && pbs.PathTaperY == 0 + && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 + && pbs.PathShearX == 0 && pbs.PathShearY == 0; + } + // return 'true' if the prim's shape was changed. public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback) { @@ -518,7 +523,7 @@ public sealed class BSShapeCollection : IDisposable if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects) { // Update prim.BSShape to reference a hull of this shape. - ret = GetReferenceToHull(prim,shapeCallback); + ret = GetReferenceToHull(prim, shapeCallback); if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}", prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X")); } @@ -697,6 +702,7 @@ public sealed class BSShapeCollection : IDisposable // See that hull shape exists in the physical world and update prim.BSShape. // We could be creating the hull because scale changed or whatever. + // Return 'true' if a new hull was built. Otherwise, returning a shared hull instance. private bool GetReferenceToHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback) { BulletShape newShape; @@ -715,6 +721,7 @@ public sealed class BSShapeCollection : IDisposable DereferenceShape(prim.PhysShape, shapeCallback); newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, prim.BaseShape, prim.Size, lod); + // It might not have been created if we're waiting for an asset. newShape = VerifyMeshCreated(newShape, prim); ReferenceShape(newShape); @@ -733,14 +740,14 @@ public sealed class BSShapeCollection : IDisposable HullDesc hullDesc; if (Hulls.TryGetValue(newHullKey, out hullDesc)) { - // If the hull shape already is created, just use it. + // If the hull shape already has been created, just use the one shared instance. newShape = hullDesc.shape.Clone(); } else { - // Build a new hull in the physical world - // Pass true for physicalness as this creates some sort of bounding box which we don't need - IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false); + // Build a new hull in the physical world. + // Pass true for physicalness as this prevents the creation of bounding box which is not needed + IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true /* isPhysical */, false /* shouldCache */); if (meshData != null) { @@ -759,15 +766,35 @@ public sealed class BSShapeCollection : IDisposable convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); } + uint maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplit; + if (BSParam.CSHullMaxDepthSplit != BSParam.CSHullMaxDepthSplitForSimpleShapes) + { + // Simple primitive shapes we know are convex so they are better implemented with + // fewer hulls. + // Check for simple shape (prim without cuts) and reduce split parameter if so. + if (PrimHasNoCuts(pbs)) + { + maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplitForSimpleShapes; + } + } + // setup and do convex hull conversion m_hulls = new List(); DecompDesc dcomp = new DecompDesc(); dcomp.mIndices = convIndices; dcomp.mVertices = convVertices; + dcomp.mDepth = maxDepthSplit; + dcomp.mCpercent = BSParam.CSHullConcavityThresholdPercent; + dcomp.mPpercent = BSParam.CSHullVolumeConservationThresholdPercent; + dcomp.mMaxVertices = (uint)BSParam.CSHullMaxVertices; + dcomp.mSkinWidth = BSParam.CSHullMaxSkinWidth; ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); // create the hull into the _hulls variable convexBuilder.process(dcomp); + DetailLog("{0},BSShapeCollection.CreatePhysicalHull,key={1},inVert={2},inInd={3},split={4},hulls={5}", + BSScene.DetailLogZero, newHullKey, indices.GetLength(0), vertices.Count, maxDepthSplit, m_hulls.Count); + // Convert the vertices and indices for passing to unmanaged. // The hull information is passed as a large floating point array. // The format is: -- cgit v1.1 From 953090fd62c2f8647d0e04bc3890a04a7076dbad Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Sat, 23 Mar 2013 11:00:52 -0700 Subject: BulletSim: fix possible race condition where an prim's asset can be requested quicker than the asset fetcher returns and thus falsely reporting that an asset was not fetched and defaulting the assset to a bounding box. --- .../Region/Physics/BulletSPlugin/BSPhysObject.cs | 12 ++++++++---- OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | 2 +- .../Physics/BulletSPlugin/BSShapeCollection.cs | 21 +++++++++++++++------ 3 files changed, 24 insertions(+), 11 deletions(-) (limited to 'OpenSim/Region/Physics') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index f953c1e..6bb88c7 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -86,7 +86,7 @@ public abstract class BSPhysObject : PhysicsActor PhysBody = new BulletBody(localID); PhysShape = new BulletShape(); - LastAssetBuildFailed = false; + PrimAssetState = PrimAssetCondition.Unknown; // Default material type. Also sets Friction, Restitution and Density. SetMaterial((int)MaterialAttributes.Material.Wood); @@ -133,9 +133,13 @@ public abstract class BSPhysObject : PhysicsActor // Reference to the physical shape (btCollisionShape) of this object public BulletShape PhysShape; - // 'true' if the mesh's underlying asset failed to build. - // This will keep us from looping after the first time the build failed. - public bool LastAssetBuildFailed { get; set; } + // The physical representation of the prim might require an asset fetch. + // The asset state is first 'Unknown' then 'Waiting' then either 'Failed' or 'Fetched'. + public enum PrimAssetCondition + { + Unknown, Waiting, Failed, Fetched + } + public PrimAssetCondition PrimAssetState { get; set; } // The objects base shape information. Null if not a prim type shape. public PrimitiveBaseShape BaseShape { get; protected set; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 2cbbe9a..6a5461a 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -155,7 +155,7 @@ public class BSPrim : BSPhysObject public override PrimitiveBaseShape Shape { set { BaseShape = value; - LastAssetBuildFailed = false; + PrimAssetState = PrimAssetCondition.Unknown; ForceBodyShapeRebuild(false); } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index 457f204..a6e20a8 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -930,11 +930,15 @@ public sealed class BSShapeCollection : IDisposable return newShape; // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset - if (prim.BaseShape.SculptEntry && !prim.LastAssetBuildFailed && prim.BaseShape.SculptTexture != OMV.UUID.Zero) + if (prim.BaseShape.SculptEntry + && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Failed + && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting + && prim.BaseShape.SculptTexture != OMV.UUID.Zero + ) { - DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lastFailed={1}", prim.LocalID, prim.LastAssetBuildFailed); - // This will prevent looping through this code as we keep trying to get the failed shape - prim.LastAssetBuildFailed = true; + DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset", prim.LocalID); + // Multiple requestors will know we're waiting for this asset + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting; BSPhysObject xprim = prim; Util.FireAndForget(delegate @@ -945,7 +949,7 @@ public sealed class BSShapeCollection : IDisposable BSPhysObject yprim = xprim; // probably not necessary, but, just in case. assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) { - bool assetFound = false; // DEBUG DEBUG + bool assetFound = false; string mismatchIDs = String.Empty; // DEBUG DEBUG if (asset != null && yprim.BaseShape.SculptEntry) { @@ -963,6 +967,10 @@ public sealed class BSShapeCollection : IDisposable mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID; } } + if (assetFound) + yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched; + else + yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; DetailLog("{0},BSShapeCollection,fetchAssetCallback,found={1},isSculpt={2},ids={3}", yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs ); @@ -970,6 +978,7 @@ public sealed class BSShapeCollection : IDisposable } else { + xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; PhysicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}", LogHeader, PhysicsScene.Name); } @@ -977,7 +986,7 @@ public sealed class BSShapeCollection : IDisposable } else { - if (prim.LastAssetBuildFailed) + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Failed) { PhysicsScene.Logger.ErrorFormat("{0} Mesh failed to fetch asset. lID={1}, texture={2}", LogHeader, prim.LocalID, prim.BaseShape.SculptTexture); -- cgit v1.1 From c96a6f1de6d5e66dd2055365c26144d7a92f2fc5 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Sat, 23 Mar 2013 11:03:59 -0700 Subject: BulletSim: small tweaks and formatting in the parameter fetching code. --- OpenSim/Region/Physics/BulletSPlugin/BSParam.cs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'OpenSim/Region/Physics') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs index 26d2d60..f3454c8 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs @@ -203,10 +203,10 @@ public static class BSParam public delegate void PSetOnObject(BSScene scene, BSPhysObject obj); public sealed class ParameterDefn : ParameterDefnBase { - T defaultValue; - PSetValue setter; - PGetValue getter; - PSetOnObject objectSet; + private T defaultValue; + private PSetValue setter; + private PGetValue getter; + private PSetOnObject objectSet; public ParameterDefn(string pName, string pDesc, T pDefault, PGetValue pGetter, PSetValue pSetter) : base(pName, pDesc) { @@ -223,13 +223,23 @@ public static class BSParam getter = pGetter; objectSet = pObjSetter; } + /* Wish I could simplify using this definition but CLR doesn't store references so closure around delegates of references won't work + public ParameterDefn(string pName, string pDesc, T pDefault, ref T loc) + : base(pName, pDesc) + { + defaultValue = pDefault; + setter = (s, v) => { loc = v; }; + getter = (s) => { return loc; }; + objectSet = null; + } + */ public override void AssignDefault(BSScene s) { setter(s, defaultValue); } public override string GetValue(BSScene s) { - return String.Format("{0}", getter(s)); + return getter(s).ToString(); } public override void SetValue(BSScene s, string valAsString) { @@ -252,6 +262,7 @@ public static class BSParam try { T setValue = (T)parser.Invoke(genericType, new Object[] { valAsString }); + // Store the parsed value setter(s, setValue); // s.Logger.DebugFormat("{0} Parameter {1} = {2}", LogHeader, name, setValue); } -- cgit v1.1 From 285dc554ece0b504cb549193096f84c9c0cfe89f Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Mon, 25 Mar 2013 15:19:55 -0700 Subject: BulletSim: new algorithm for vertical attraction which uses quaternion arithmetic to compute the shortest path between the current tilt and vertical. --- OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | 48 ++++++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) (limited to 'OpenSim/Region/Physics') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index 5549984..65df741 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -321,7 +321,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin } } - internal void ProcessTypeChange(Vehicle pType) + public void ProcessTypeChange(Vehicle pType) { VDetailLog("{0},ProcessTypeChange,type={1}", Prim.LocalID, pType); // Set Defaults For Type @@ -1301,14 +1301,52 @@ namespace OpenSim.Region.Physics.BulletSPlugin // efficiency of 1.0 will cause the spring to reach its equilibrium with exponential decay. public void ComputeAngularVerticalAttraction() { + // If vertical attaction timescale is reasonable if (enableAngularVerticalAttraction && m_verticalAttractionTimescale < m_verticalAttractionCutoff) { + // Possible solution derived from a discussion at: + // http://stackoverflow.com/questions/14939657/computing-vector-from-quaternion-works-computing-quaternion-from-vector-does-no + + // Create a rotation that is only the vehicle's rotation around Z + Vector3 currentEuler = Vector3.Zero; + VehicleOrientation.GetEulerAngles(out currentEuler.X, out currentEuler.Y, out currentEuler.Z); + Quaternion justZOrientation = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, currentEuler.Z); + + // Create the axis that is perpendicular to the up vector and the rotated up vector. + Vector3 differenceAxis = Vector3.Cross(Vector3.UnitZ * justZOrientation, Vector3.UnitZ * VehicleOrientation); + // Compute the angle between those to vectors. + double differenceAngle = Math.Acos((double)Vector3.Dot(Vector3.UnitZ, Vector3.Normalize(Vector3.UnitZ * VehicleOrientation))); + // 'differenceAngle' is the angle to rotate and 'differenceAxis' is the plane to rotate in to get the vehicle vertical + + // Reduce the change by the time period it is to change in. Timestep is handled when velocity is applied. + // TODO: add 'efficiency'. + differenceAngle /= m_verticalAttractionTimescale; + + // Create the quaterian representing the correction angle + Quaternion correctionRotation = Quaternion.CreateFromAxisAngle(differenceAxis, (float)differenceAngle); + + // Turn that quaternion into Euler values to make it into velocities to apply. + Vector3 vertContributionV = Vector3.Zero; + correctionRotation.GetEulerAngles(out vertContributionV.X, out vertContributionV.Y, out vertContributionV.Z); + vertContributionV *= -1f; + + VehicleRotationalVelocity += vertContributionV; + + VDetailLog("{0}, MoveAngular,verticalAttraction,diffAxis={1},diffAng={2},corrRot={3},contrib={4}", + Prim.LocalID, + differenceAxis, + differenceAngle, + correctionRotation, + vertContributionV); + + // =================================================================== + /* Vector3 vertContributionV = Vector3.Zero; Vector3 origRotVelW = VehicleRotationalVelocity; // DEBUG DEBUG // Take a vector pointing up and convert it from world to vehicle relative coords. - Vector3 verticalError = Vector3.UnitZ * VehicleOrientation; + Vector3 verticalError = Vector3.Normalize(Vector3.UnitZ * VehicleOrientation); // If vertical attraction correction is needed, the vector that was pointing up (UnitZ) // is now: @@ -1334,13 +1372,17 @@ namespace OpenSim.Region.Physics.BulletSPlugin // 'vertContrbution' is now the necessary angular correction to correct tilt in one second. // Correction happens over a number of seconds. Vector3 unscaledContribVerticalErrorV = vertContributionV; // DEBUG DEBUG + + // The correction happens over the user's time period vertContributionV /= m_verticalAttractionTimescale; - VehicleRotationalVelocity += vertContributionV; + // Rotate the vehicle rotation to the world coordinates. + VehicleRotationalVelocity += (vertContributionV * VehicleOrientation); VDetailLog("{0}, MoveAngular,verticalAttraction,,origRotVW={1},vertError={2},unscaledV={3},eff={4},ts={5},vertContribV={6}", Prim.LocalID, origRotVelW, verticalError, unscaledContribVerticalErrorV, m_verticalAttractionEfficiency, m_verticalAttractionTimescale, vertContributionV); + */ } } -- cgit v1.1 From abde0d4efb897581df2a6a7be591de2224611b90 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Tue, 26 Mar 2013 15:02:10 -0700 Subject: BulletSim: prevent asset fetching loop when the fetched asset fails to mesh. Check for the case where the fetched mesh asset fails meshing (degenerate triangles or no physical mesh). In this case, the asset is marked 'failed' and BulletSim doesn't keep trying to fetch over-and-over trying to get a good asset. --- .../Physics/BulletSPlugin/BSShapeCollection.cs | 116 ++++++++++++--------- 1 file changed, 64 insertions(+), 52 deletions(-) (limited to 'OpenSim/Region/Physics') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index a6e20a8..b6ac23d 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -929,67 +929,79 @@ public sealed class BSShapeCollection : IDisposable if (newShape.HasPhysicalShape) return newShape; - // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset - if (prim.BaseShape.SculptEntry - && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Failed - && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting - && prim.BaseShape.SculptTexture != OMV.UUID.Zero - ) + // VerifyMeshCreated is called after trying to create the mesh. If we think the asset had been + // fetched but we end up here again, the meshing of the asset must have failed. + // Prevent trying to keep fetching the mesh by declaring failure. + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) { - DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset", prim.LocalID); - // Multiple requestors will know we're waiting for this asset - prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting; + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; + PhysicsScene.Logger.WarnFormat("{0} Fetched asset would not mesh. {1}, texture={2}", + LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); + } + else + { + // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset + if (prim.BaseShape.SculptEntry + && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Failed + && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting + && prim.BaseShape.SculptTexture != OMV.UUID.Zero + ) + { + DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset", prim.LocalID); + // Multiple requestors will know we're waiting for this asset + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting; - BSPhysObject xprim = prim; - Util.FireAndForget(delegate - { - RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod; - if (assetProvider != null) + BSPhysObject xprim = prim; + Util.FireAndForget(delegate { - BSPhysObject yprim = xprim; // probably not necessary, but, just in case. - assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) + RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod; + if (assetProvider != null) { - bool assetFound = false; - string mismatchIDs = String.Empty; // DEBUG DEBUG - if (asset != null && yprim.BaseShape.SculptEntry) + BSPhysObject yprim = xprim; // probably not necessary, but, just in case. + assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) { - if (yprim.BaseShape.SculptTexture.ToString() == asset.ID) + bool assetFound = false; + string mismatchIDs = String.Empty; // DEBUG DEBUG + if (asset != null && yprim.BaseShape.SculptEntry) { - yprim.BaseShape.SculptData = asset.Data; - // This will cause the prim to see that the filler shape is not the right - // one and try again to build the object. - // No race condition with the normal shape setting since the rebuild is at taint time. - yprim.ForceBodyShapeRebuild(false /* inTaintTime */); - assetFound = true; + if (yprim.BaseShape.SculptTexture.ToString() == asset.ID) + { + yprim.BaseShape.SculptData = asset.Data; + // This will cause the prim to see that the filler shape is not the right + // one and try again to build the object. + // No race condition with the normal shape setting since the rebuild is at taint time. + yprim.ForceBodyShapeRebuild(false /* inTaintTime */); + assetFound = true; + } + else + { + mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID; + } } + if (assetFound) + yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched; else - { - mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID; - } - } - if (assetFound) - yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched; - else - yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; - DetailLog("{0},BSShapeCollection,fetchAssetCallback,found={1},isSculpt={2},ids={3}", - yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs ); - - }); - } - else - { - xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; - PhysicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}", - LogHeader, PhysicsScene.Name); - } - }); - } - else - { - if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Failed) + yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; + DetailLog("{0},BSShapeCollection,fetchAssetCallback,found={1},isSculpt={2},ids={3}", + yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs ); + + }); + } + else + { + xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; + PhysicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}", + LogHeader, PhysicsScene.Name); + } + }); + } + else { - PhysicsScene.Logger.ErrorFormat("{0} Mesh failed to fetch asset. lID={1}, texture={2}", - LogHeader, prim.LocalID, prim.BaseShape.SculptTexture); + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Failed) + { + PhysicsScene.Logger.WarnFormat("{0} Mesh failed to fetch asset. obj={1}, texture={2}", + LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); + } } } -- cgit v1.1 From 3f9b274180acb4499b878e9acad461811f11eb1f Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Wed, 27 Mar 2013 16:59:13 -0700 Subject: BulletSim: tweaks to terrain boundry computation. No functional changes. --- .../Physics/BulletSPlugin/BSTerrainManager.cs | 50 +++++++++++++--------- 1 file changed, 29 insertions(+), 21 deletions(-) (limited to 'OpenSim/Region/Physics') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index a60946d..d4aecbc 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -343,37 +343,35 @@ public sealed class BSTerrainManager : IDisposable { Vector3 ret = pPos; + // First, base addresses are never negative so correct for that possible problem. + if (ret.X < 0f || ret.Y < 0f) + { + ret.X = Util.Clamp(ret.X, 0f, 1000000f); + ret.Y = Util.Clamp(ret.Y, 0f, 1000000f); + DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,zeroingNegXorY,oldPos={1},newPos={2}", + BSScene.DetailLogZero, pPos, ret); + } + // Can't do this function if we don't know about any terrain. if (m_terrains.Count == 0) return ret; - int loopPrevention = 5; + int loopPrevention = 10; Vector3 terrainBaseXYZ; BSTerrainPhys physTerrain; while (!GetTerrainPhysicalAtXYZ(ret, out physTerrain, out terrainBaseXYZ)) { // The passed position is not within a known terrain area. + // NOTE that GetTerrainPhysicalAtXYZ will set 'terrainBaseXYZ' to the base of the unfound region. - // First, base addresses are never negative so correct for that possible problem. - if (ret.X < 0f || ret.Y < 0f) - { - if (ret.X < 0f) - ret.X = 0f; - if (ret.Y < 0f) - ret.Y = 0f; - DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,zeroingNegXorY,oldPos={1},newPos={2}", - BSScene.DetailLogZero, pPos, ret); - } - else - { - // Must be off the top of a region. Find an adjacent region to move into. - Vector3 adjacentTerrainBase = FindAdjacentTerrainBase(terrainBaseXYZ); + // Must be off the top of a region. Find an adjacent region to move into. + Vector3 adjacentTerrainBase = FindAdjacentTerrainBase(terrainBaseXYZ); + + ret.X = Math.Min(ret.X, adjacentTerrainBase.X + (ret.X % DefaultRegionSize.X)); + ret.Y = Math.Min(ret.Y, adjacentTerrainBase.Y + (ret.X % DefaultRegionSize.Y)); + DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,findingAdjacentRegion,adjacentRegBase={1},oldPos={2},newPos={3}", + BSScene.DetailLogZero, adjacentTerrainBase, pPos, ret); - ret.X = Math.Min(ret.X, adjacentTerrainBase.X + DefaultRegionSize.X); - ret.Y = Math.Min(ret.Y, adjacentTerrainBase.Y + DefaultRegionSize.Y); - DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,findingAdjacentRegion,adjacentRegBase={1},oldPos={2},newPos={3}", - BSScene.DetailLogZero, adjacentTerrainBase, pPos, ret); - } if (loopPrevention-- < 0f) { // The 'while' is a little dangerous so this prevents looping forever if the @@ -383,6 +381,7 @@ public sealed class BSTerrainManager : IDisposable break; } } + return ret; } @@ -479,11 +478,20 @@ public sealed class BSTerrainManager : IDisposable private Vector3 FindAdjacentTerrainBase(Vector3 pTerrainBase) { Vector3 ret = pTerrainBase; + + // Can't do this function if we don't know about any terrain. + if (m_terrains.Count == 0) + return ret; + + // Just some sanity + ret.X = Util.Clamp(ret.X, 0f, 1000000f); + ret.Y = Util.Clamp(ret.Y, 0f, 1000000f); ret.Z = 0f; + lock (m_terrains) { // Once down to the <0,0> region, we have to be done. - while (ret.X > 0f && ret.Y > 0f) + while (ret.X > 0f || ret.Y > 0f) { if (ret.X > 0f) { -- cgit v1.1 From 6a9630d2bdc27ed702936f4c44e6978f728a9ef0 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Thu, 28 Mar 2013 10:56:21 -0700 Subject: BulletSim: fix race condition when creating very large mega-regions. The symptom was exceptions while creating physical terrain. Reduce default terrain mesh magnification to 2 from 3 because the higher resolution uses a lot of memory and doesn't solve the terrain smoothness for vehicles. Added comments here and there and improved some debugging log messages. --- .../Region/Physics/BulletSPlugin/BSCharacter.cs | 4 +-- .../Region/Physics/BulletSPlugin/BSMaterials.cs | 3 ++ OpenSim/Region/Physics/BulletSPlugin/BSParam.cs | 2 +- .../Physics/BulletSPlugin/BSTerrainManager.cs | 38 ++++++++++++++-------- .../Region/Physics/BulletSPlugin/BSTerrainMesh.cs | 10 +++--- 5 files changed, 35 insertions(+), 22 deletions(-) (limited to 'OpenSim/Region/Physics') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index e208d3a..90c2d9c 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -479,7 +479,7 @@ public sealed class BSCharacter : BSPhysObject // The character is out of the known/simulated area. // Force the avatar position to be within known. ScenePresence will use the position // plus the velocity to decide if the avatar is moving out of the region. - RawPosition = PhysicsScene.TerrainManager.ClampPositionIntoKnownTerrain(RawPosition); + RawPosition = PhysicsScene.TerrainManager.ClampPositionIntoKnownTerrain(RawPosition); DetailLog("{0},BSCharacter.PositionSanityCheck,notWithinKnownTerrain,clampedPos={1}", LocalID, RawPosition); return true; } @@ -898,7 +898,7 @@ public sealed class BSCharacter : BSPhysObject // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. if (PositionSanityCheck(true)) { - DetailLog("{0},BSCharacter.UpdateProperties,updatePosForSanity,pos={1}", LocalID, _position); + DetailLog("{0},BSCharacter.UpdateProperties,updatePosForSanity,pos={1}", LocalID, _position); entprop.Position = _position; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs b/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs index 92d62ff..ee77d6e 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs @@ -180,11 +180,14 @@ public static class BSMaterials // Use reflection to set the value in the attribute structure. private static void SetAttributeValue(int matType, string attribName, float val) { + // Get the current attribute values for this material MaterialAttributes thisAttrib = Attributes[matType]; + // Find the field for the passed attribute name (eg, find field named 'friction') FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName.ToLower()); if (fieldInfo != null) { fieldInfo.SetValue(thisAttrib, val); + // Copy new attributes back to array -- since MaterialAttributes is 'struct', passed by value, not reference. Attributes[matType] = thisAttrib; } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs index f3454c8..385ed9e 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs @@ -482,7 +482,7 @@ public static class BSParam (s) => { return TerrainImplementation; }, (s,v) => { TerrainImplementation = v; } ), new ParameterDefn("TerrainMeshMagnification", "Number of times the 256x256 heightmap is multiplied to create the terrain mesh" , - 3, + 2, (s) => { return TerrainMeshMagnification; }, (s,v) => { TerrainMeshMagnification = v; } ), new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index d4aecbc..b2fb835 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -132,6 +132,7 @@ public sealed class BSTerrainManager : IDisposable // safe to call Bullet in real time. We hope no one is moving prims around yet. public void CreateInitialGroundPlaneAndTerrain() { + DetailLog("{0},BSTerrainManager.CreateInitialGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, PhysicsScene.RegionName); // The ground plane is here to catch things that are trying to drop to negative infinity BulletShape groundPlaneShape = PhysicsScene.PE.CreateGroundPlaneShape(BSScene.GROUNDPLANE_ID, 1f, BSParam.TerrainCollisionMargin); m_groundPlane = PhysicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape, @@ -145,14 +146,18 @@ public sealed class BSTerrainManager : IDisposable m_groundPlane.collisionType = CollisionType.Groundplane; m_groundPlane.ApplyCollisionMask(PhysicsScene); - // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain. BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize); - m_terrains.Add(Vector3.Zero, initialTerrain); + lock (m_terrains) + { + // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain. + m_terrains.Add(Vector3.Zero, initialTerrain); + } } // Release all the terrain structures we might have allocated public void ReleaseGroundPlaneAndTerrain() { + DetailLog("{0},BSTerrainManager.ReleaseGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, PhysicsScene.RegionName); if (m_groundPlane.HasPhysicalBody) { if (PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, m_groundPlane)) @@ -193,11 +198,16 @@ public sealed class BSTerrainManager : IDisposable // the terrain is added to our parent if (MegaRegionParentPhysicsScene is BSScene) { - DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", - BSScene.DetailLogZero, m_worldOffset, m_worldMax); - ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateTerrain( - BSScene.CHILDTERRAIN_ID, localHeightMap, - m_worldOffset, m_worldOffset + DefaultRegionSize, true); + DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", BSScene.DetailLogZero, m_worldOffset, m_worldMax); + // This looks really odd but this region is passing its terrain to its mega-region root region + // and the creation of the terrain must happen on the root region's taint thread and not + // my taint thread. + ((BSScene)MegaRegionParentPhysicsScene).PostTaintObject("TerrainManager.SetTerrain.Mega-" + m_worldOffset.ToString(), 0, delegate() + { + ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateTerrain( + BSScene.CHILDTERRAIN_ID, localHeightMap, + m_worldOffset, m_worldOffset + DefaultRegionSize, true /* inTaintTime */); + }); } } else @@ -206,16 +216,16 @@ public sealed class BSTerrainManager : IDisposable DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero); UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap, - m_worldOffset, m_worldOffset + DefaultRegionSize, true); + m_worldOffset, m_worldOffset + DefaultRegionSize, true /* inTaintTime */); } }); } - // If called with no mapInfo for the terrain, this will create a new mapInfo and terrain + // If called for terrain has has not been previously allocated, a new terrain will be built // based on the passed information. The 'id' should be either the terrain id or // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used. // The latter feature is for creating child terrains for mega-regions. - // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new + // If there is an existing terrain body, a new // terrain shape is created and added to the body. // This call is most often used to update the heightMap and parameters of the terrain. // (The above does suggest that some simplification/refactoring is in order.) @@ -223,8 +233,8 @@ public sealed class BSTerrainManager : IDisposable private void UpdateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool inTaintTime) { - DetailLog("{0},BSTerrainManager.UpdateTerrain,call,minC={1},maxC={2},inTaintTime={3}", - BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime); + DetailLog("{0},BSTerrainManager.UpdateTerrain,call,id={1},minC={2},maxC={3},inTaintTime={4}", + BSScene.DetailLogZero, id, minCoords, maxCoords, inTaintTime); // Find high and low points of passed heightmap. // The min and max passed in is usually the area objects can be in (maximum @@ -253,7 +263,7 @@ public sealed class BSTerrainManager : IDisposable 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}", + DetailLog("{0},BSTErrainManager.UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}", BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords); // Remove old terrain from the collection @@ -292,7 +302,7 @@ public sealed class BSTerrainManager : IDisposable if (newTerrainID >= BSScene.CHILDTERRAIN_ID) newTerrainID = ++m_terrainCount; - DetailLog("{0},UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}", + DetailLog("{0},BSTerrainManager.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); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs index a9cd8a1..2ce1513 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs @@ -98,20 +98,20 @@ public sealed class BSTerrainMesh : BSTerrainPhys if (!meshCreationSuccess) { // DISASTER!! - PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap", ID); + PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap,id={1}", BSScene.DetailLogZero, ID); PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase); // 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); + PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,id={1},indices={2},indSz={3},vertices={4},vertSz={5}", + BSScene.DetailLogZero, ID, indicesCount, indices.Length, verticesCount, vertices.Length); m_terrainShape = PhysicsScene.PE.CreateMeshShape(PhysicsScene.World, indicesCount, indices, verticesCount, vertices); if (!m_terrainShape.HasPhysicalShape) { // DISASTER!! - PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID); + PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape,id={1}", BSScene.DetailLogZero, ID); PhysicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase); // Something is very messed up and a crash is in our future. return; @@ -151,7 +151,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys if (BSParam.UseSingleSidedMeshes) { - PhysicsScene.DetailLog("{0},BSTerrainMesh.settingCustomMaterial", id); + PhysicsScene.DetailLog("{0},BSTerrainMesh.settingCustomMaterial,id={1}", BSScene.DetailLogZero, id); PhysicsScene.PE.AddToCollisionFlags(m_terrainBody, CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK); } -- cgit v1.1