diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
index a30d8eb546..974fd577f9 100644
--- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
@@ -33,6 +33,7 @@ using System.Threading;
using OpenSim.Framework;
using OpenSim.Framework.Capabilities;
using OpenSim.Framework.Client;
+using OpenSim.Framework.Monitoring;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Physics.Manager;
@@ -77,6 +78,31 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
///
public bool DisableInterRegionTeleportCancellation { get; set; }
+ ///
+ /// Number of times inter-region teleport was attempted.
+ ///
+ private Stat m_interRegionTeleportAttempts;
+
+ ///
+ /// Number of times inter-region teleport was aborted (due to simultaneous client logout).
+ ///
+ private Stat m_interRegionTeleportAborts;
+
+ ///
+ /// Number of times inter-region teleport was successfully cancelled by the client.
+ ///
+ private Stat m_interRegionTeleportCancels;
+
+ ///
+ /// Number of times inter-region teleport failed due to server/client/network problems (e.g. viewer failed to
+ /// connect with destination region).
+ ///
+ ///
+ /// This is not necessarily a problem for this simulator - in open-grid/hg conditions, viewer connectivity to
+ /// destination simulator is unknown.
+ ///
+ private Stat m_interRegionTeleportFailures;
+
protected bool m_Enabled = false;
public Scene Scene { get; private set; }
@@ -156,6 +182,60 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
Scene = scene;
+ m_interRegionTeleportAttempts =
+ new Stat(
+ "InterRegionTeleportAttempts",
+ "Number of inter-region teleports attempted.",
+ "This does not count attempts which failed due to pre-conditions (e.g. target simulator refused access).\n"
+ + "You can get successfully teleports by subtracting aborts, cancels and teleport failures from this figure.",
+ "",
+ "entitytransfer",
+ Scene.Name,
+ StatType.Push,
+ null,
+ StatVerbosity.Debug);
+
+ m_interRegionTeleportAborts =
+ new Stat(
+ "InterRegionTeleportAborts",
+ "Number of inter-region teleports aborted due to client actions.",
+ "The chief action is simultaneous logout whilst teleporting.",
+ "",
+ "entitytransfer",
+ Scene.Name,
+ StatType.Push,
+ null,
+ StatVerbosity.Debug);
+
+ m_interRegionTeleportCancels =
+ new Stat(
+ "InterRegionTeleportCancels",
+ "Number of inter-region teleports cancelled by the client.",
+ null,
+ "",
+ "entitytransfer",
+ Scene.Name,
+ StatType.Push,
+ null,
+ StatVerbosity.Debug);
+
+ m_interRegionTeleportFailures =
+ new Stat(
+ "InterRegionTeleportFailures",
+ "Number of inter-region teleports that failed due to server/client/network issues.",
+ "This number may not be very helpful in open-grid/hg situations as the network connectivity/quality of destinations is uncontrollable.",
+ "",
+ "entitytransfer",
+ Scene.Name,
+ StatType.Push,
+ null,
+ StatVerbosity.Debug);
+
+ StatsManager.RegisterStat(m_interRegionTeleportAttempts);
+ StatsManager.RegisterStat(m_interRegionTeleportAborts);
+ StatsManager.RegisterStat(m_interRegionTeleportCancels);
+ StatsManager.RegisterStat(m_interRegionTeleportFailures);
+
scene.RegisterModuleInterface(this);
scene.EventManager.OnNewClient += OnNewClient;
}
@@ -173,7 +253,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
public virtual void Close() {}
- public virtual void RemoveRegion(Scene scene) {}
+ public virtual void RemoveRegion(Scene scene)
+ {
+ StatsManager.DeregisterStat(m_interRegionTeleportAttempts);
+ StatsManager.DeregisterStat(m_interRegionTeleportAborts);
+ StatsManager.DeregisterStat(m_interRegionTeleportCancels);
+ StatsManager.DeregisterStat(m_interRegionTeleportFailures);
+ }
public virtual void RegionLoaded(Scene scene)
{
@@ -548,6 +634,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return;
}
+ // Before this point, teleport 'failure' is due to checkable pre-conditions such as whether the target
+ // simulator can be found and is explicitly prepared to allow access. Therefore, we will not count these
+ // as server attempts.
+ m_interRegionTeleportAttempts.Value++;
+
m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Destination is running version {0}", version);
// Fixing a bug where teleporting while sitting results in the avatar ending up removed from
@@ -600,6 +691,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
bool logout = false;
if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout))
{
+ m_interRegionTeleportFailures.Value++;
+
sp.ControllingClient.SendTeleportFailed(String.Format("Teleport refused: {0}", reason));
m_log.DebugFormat(
@@ -611,6 +704,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling)
{
+ m_interRegionTeleportCancels.Value++;
+
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request",
sp.Name, finalDestination.RegionName, sp.Scene.Name);
@@ -619,6 +714,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
}
else if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
{
+ m_interRegionTeleportAborts.Value++;
+
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after CreateAgent due to previous client close.",
sp.Name, finalDestination.RegionName, sp.Scene.Name);
@@ -688,6 +785,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// establish th econnection to the destination which makes it return true.
if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
{
+ m_interRegionTeleportAborts.Value++;
+
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} before UpdateAgent",
sp.Name, finalDestination.RegionName, sp.Scene.Name);
@@ -703,6 +802,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
{
if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
{
+ m_interRegionTeleportAborts.Value++;
+
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after UpdateAgent due to previous client close.",
sp.Name, finalDestination.RegionName, sp.Scene.Name);
@@ -720,6 +821,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling)
{
+ m_interRegionTeleportCancels.Value++;
+
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after UpdateAgent on client request",
sp.Name, finalDestination.RegionName, sp.Scene.Name);
@@ -755,6 +858,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
{
if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
{
+ m_interRegionTeleportAborts.Value++;
+
m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after WaitForAgentArrivedAtDestination due to previous client close.",
sp.Name, finalDestination.RegionName, sp.Scene.Name);
@@ -767,6 +872,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName);
Fail(sp, finalDestination, logout, "Destination region did not signal teleport completion.");
+
return;
}
@@ -808,15 +914,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// now we have a child agent in this region.
sp.Reset();
}
-
- // Commented pending deletion since this method no longer appears to do anything at all
-// // REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE!
-// if (sp.Scene.NeedSceneCacheClear(sp.UUID))
-// {
-// m_log.DebugFormat(
-// "[ENTITY TRANSFER MODULE]: User {0} is going to another region, profile cache removed",
-// sp.UUID);
-// }
}
///
@@ -852,6 +949,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
{
CleanupFailedInterRegionTeleport(sp, finalDestination);
+ m_interRegionTeleportFailures.Value++;
+
sp.ControllingClient.SendTeleportFailed(
string.Format(
"Problems connecting to destination {0}, reason: {1}", finalDestination.RegionName, reason));
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
index 5549984d65..65df741962 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);
+ */
}
}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs
index 4d89a88778..f3454c8daf 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; }
@@ -195,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)
{
@@ -215,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)
{
@@ -244,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);
}
@@ -623,6 +642,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/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
index f953c1e085..6bb88c77f3 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 2cbbe9a3fd..6a5461a4e8 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 05c147d93f..7609578aad 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"));
}
@@ -699,6 +704,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;
@@ -717,6 +723,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);
@@ -735,13 +742,13 @@ 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
+ // 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, false, false, false);
if (meshData != null)
{
@@ -761,15 +768,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:
@@ -905,11 +932,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
@@ -920,7 +951,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)
{
@@ -938,6 +969,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 );
@@ -945,6 +980,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);
}
@@ -952,7 +988,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);