Merge branch 'master' into careminster

Conflicts:
	OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
avinationmerge
Melanie 2013-03-26 03:26:02 +00:00
commit 95d0a7d4fa
6 changed files with 267 additions and 42 deletions

View File

@ -33,6 +33,7 @@ using System.Threading;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Framework.Capabilities; using OpenSim.Framework.Capabilities;
using OpenSim.Framework.Client; using OpenSim.Framework.Client;
using OpenSim.Framework.Monitoring;
using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Physics.Manager; using OpenSim.Region.Physics.Manager;
@ -77,6 +78,31 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
/// </remarks> /// </remarks>
public bool DisableInterRegionTeleportCancellation { get; set; } public bool DisableInterRegionTeleportCancellation { get; set; }
/// <summary>
/// Number of times inter-region teleport was attempted.
/// </summary>
private Stat m_interRegionTeleportAttempts;
/// <summary>
/// Number of times inter-region teleport was aborted (due to simultaneous client logout).
/// </summary>
private Stat m_interRegionTeleportAborts;
/// <summary>
/// Number of times inter-region teleport was successfully cancelled by the client.
/// </summary>
private Stat m_interRegionTeleportCancels;
/// <summary>
/// Number of times inter-region teleport failed due to server/client/network problems (e.g. viewer failed to
/// connect with destination region).
/// </summary>
/// <remarks>
/// This is not necessarily a problem for this simulator - in open-grid/hg conditions, viewer connectivity to
/// destination simulator is unknown.
/// </remarks>
private Stat m_interRegionTeleportFailures;
protected bool m_Enabled = false; protected bool m_Enabled = false;
public Scene Scene { get; private set; } public Scene Scene { get; private set; }
@ -156,6 +182,60 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
Scene = scene; 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<IEntityTransferModule>(this); scene.RegisterModuleInterface<IEntityTransferModule>(this);
scene.EventManager.OnNewClient += OnNewClient; scene.EventManager.OnNewClient += OnNewClient;
} }
@ -173,7 +253,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
public virtual void Close() {} 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) public virtual void RegionLoaded(Scene scene)
{ {
@ -548,6 +634,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return; 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); 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 // 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; bool logout = false;
if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout)) if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout))
{ {
m_interRegionTeleportFailures.Value++;
sp.ControllingClient.SendTeleportFailed(String.Format("Teleport refused: {0}", reason)); sp.ControllingClient.SendTeleportFailed(String.Format("Teleport refused: {0}", reason));
m_log.DebugFormat( m_log.DebugFormat(
@ -611,6 +704,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling) if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling)
{ {
m_interRegionTeleportCancels.Value++;
m_log.DebugFormat( m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request", "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request",
sp.Name, finalDestination.RegionName, sp.Scene.Name); 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) else if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
{ {
m_interRegionTeleportAborts.Value++;
m_log.DebugFormat( m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after CreateAgent due to previous client close.", "[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); 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. // establish th econnection to the destination which makes it return true.
if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
{ {
m_interRegionTeleportAborts.Value++;
m_log.DebugFormat( m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} before UpdateAgent", "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} before UpdateAgent",
sp.Name, finalDestination.RegionName, sp.Scene.Name); 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) if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
{ {
m_interRegionTeleportAborts.Value++;
m_log.DebugFormat( m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after UpdateAgent due to previous client close.", "[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); 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) if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling)
{ {
m_interRegionTeleportCancels.Value++;
m_log.DebugFormat( m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after UpdateAgent on client request", "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after UpdateAgent on client request",
sp.Name, finalDestination.RegionName, sp.Scene.Name); 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) if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
{ {
m_interRegionTeleportAborts.Value++;
m_log.DebugFormat( m_log.DebugFormat(
"[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after WaitForAgentArrivedAtDestination due to previous client close.", "[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); 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); sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName);
Fail(sp, finalDestination, logout, "Destination region did not signal teleport completion."); Fail(sp, finalDestination, logout, "Destination region did not signal teleport completion.");
return; return;
} }
@ -808,15 +914,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// now we have a child agent in this region. // now we have a child agent in this region.
sp.Reset(); 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);
// }
} }
/// <summary> /// <summary>
@ -852,6 +949,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
{ {
CleanupFailedInterRegionTeleport(sp, finalDestination); CleanupFailedInterRegionTeleport(sp, finalDestination);
m_interRegionTeleportFailures.Value++;
sp.ControllingClient.SendTeleportFailed( sp.ControllingClient.SendTeleportFailed(
string.Format( string.Format(
"Problems connecting to destination {0}, reason: {1}", finalDestination.RegionName, reason)); "Problems connecting to destination {0}, reason: {1}", finalDestination.RegionName, reason));

View File

@ -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); VDetailLog("{0},ProcessTypeChange,type={1}", Prim.LocalID, pType);
// Set Defaults For Type // 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. // efficiency of 1.0 will cause the spring to reach its equilibrium with exponential decay.
public void ComputeAngularVerticalAttraction() public void ComputeAngularVerticalAttraction()
{ {
// If vertical attaction timescale is reasonable // If vertical attaction timescale is reasonable
if (enableAngularVerticalAttraction && m_verticalAttractionTimescale < m_verticalAttractionCutoff) 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 vertContributionV = Vector3.Zero;
Vector3 origRotVelW = VehicleRotationalVelocity; // DEBUG DEBUG Vector3 origRotVelW = VehicleRotationalVelocity; // DEBUG DEBUG
// Take a vector pointing up and convert it from world to vehicle relative coords. // 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) // If vertical attraction correction is needed, the vector that was pointing up (UnitZ)
// is now: // is now:
@ -1334,13 +1372,17 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// 'vertContrbution' is now the necessary angular correction to correct tilt in one second. // 'vertContrbution' is now the necessary angular correction to correct tilt in one second.
// Correction happens over a number of seconds. // Correction happens over a number of seconds.
Vector3 unscaledContribVerticalErrorV = vertContributionV; // DEBUG DEBUG Vector3 unscaledContribVerticalErrorV = vertContributionV; // DEBUG DEBUG
// The correction happens over the user's time period
vertContributionV /= m_verticalAttractionTimescale; 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}", VDetailLog("{0}, MoveAngular,verticalAttraction,,origRotVW={1},vertError={2},unscaledV={3},eff={4},ts={5},vertContribV={6}",
Prim.LocalID, origRotVelW, verticalError, unscaledContribVerticalErrorV, Prim.LocalID, origRotVelW, verticalError, unscaledContribVerticalErrorV,
m_verticalAttractionEfficiency, m_verticalAttractionTimescale, vertContributionV); m_verticalAttractionEfficiency, m_verticalAttractionTimescale, vertContributionV);
*/
} }
} }

View File

@ -142,6 +142,14 @@ public static class BSParam
public static float VehicleAngularBankingTimescaleFudge { get; private set; } public static float VehicleAngularBankingTimescaleFudge { get; private set; }
public static bool VehicleDebuggingEnabled { 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 // Linkset implementation parameters
public static float LinksetImplementation { get; private set; } public static float LinksetImplementation { get; private set; }
public static bool LinkConstraintUseFrameOffset { get; private set; } public static bool LinkConstraintUseFrameOffset { get; private set; }
@ -195,10 +203,10 @@ public static class BSParam
public delegate void PSetOnObject<T>(BSScene scene, BSPhysObject obj); public delegate void PSetOnObject<T>(BSScene scene, BSPhysObject obj);
public sealed class ParameterDefn<T> : ParameterDefnBase public sealed class ParameterDefn<T> : ParameterDefnBase
{ {
T defaultValue; private T defaultValue;
PSetValue<T> setter; private PSetValue<T> setter;
PGetValue<T> getter; private PGetValue<T> getter;
PSetOnObject<T> objectSet; private PSetOnObject<T> objectSet;
public ParameterDefn(string pName, string pDesc, T pDefault, PGetValue<T> pGetter, PSetValue<T> pSetter) public ParameterDefn(string pName, string pDesc, T pDefault, PGetValue<T> pGetter, PSetValue<T> pSetter)
: base(pName, pDesc) : base(pName, pDesc)
{ {
@ -215,13 +223,23 @@ public static class BSParam
getter = pGetter; getter = pGetter;
objectSet = pObjSetter; 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) public override void AssignDefault(BSScene s)
{ {
setter(s, defaultValue); setter(s, defaultValue);
} }
public override string GetValue(BSScene s) public override string GetValue(BSScene s)
{ {
return String.Format("{0}", getter(s)); return getter(s).ToString();
} }
public override void SetValue(BSScene s, string valAsString) public override void SetValue(BSScene s, string valAsString)
{ {
@ -244,6 +262,7 @@ public static class BSParam
try try
{ {
T setValue = (T)parser.Invoke(genericType, new Object[] { valAsString }); T setValue = (T)parser.Invoke(genericType, new Object[] { valAsString });
// Store the parsed value
setter(s, setValue); setter(s, setValue);
// s.Logger.DebugFormat("{0} Parameter {1} = {2}", LogHeader, name, setValue); // s.Logger.DebugFormat("{0} Parameter {1} = {2}", LogHeader, name, setValue);
} }
@ -623,6 +642,31 @@ public static class BSParam
(s) => { return GlobalContactBreakingThreshold; }, (s) => { return GlobalContactBreakingThreshold; },
(s,v) => { GlobalContactBreakingThreshold = v; s.UnmanagedParams[0].globalContactBreakingThreshold = v; } ), (s,v) => { GlobalContactBreakingThreshold = v; s.UnmanagedParams[0].globalContactBreakingThreshold = v; } ),
new ParameterDefn<int>("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<int>("CSHullMaxDepthSplitForSimpleShapes", "CS impl: max depth setting for simple prim shapes",
2,
(s) => { return CSHullMaxDepthSplitForSimpleShapes; },
(s,v) => { CSHullMaxDepthSplitForSimpleShapes = v; } ),
new ParameterDefn<float>("CSHullConcavityThresholdPercent", "CS impl: concavity threshold percent (0-20)",
5f,
(s) => { return CSHullConcavityThresholdPercent; },
(s,v) => { CSHullConcavityThresholdPercent = v; } ),
new ParameterDefn<float>("CSHullVolumeConservationThresholdPercent", "percent volume conservation to collapse hulls (0-30)",
5f,
(s) => { return CSHullVolumeConservationThresholdPercent; },
(s,v) => { CSHullVolumeConservationThresholdPercent = v; } ),
new ParameterDefn<int>("CSHullMaxVertices", "CS impl: maximum number of vertices in output hulls. Keep < 50.",
32,
(s) => { return CSHullMaxVertices; },
(s,v) => { CSHullMaxVertices = v; } ),
new ParameterDefn<float>("CSHullMaxSkinWidth", "CS impl: skin width to apply to output hulls.",
0,
(s) => { return CSHullMaxSkinWidth; },
(s,v) => { CSHullMaxSkinWidth = v; } ),
new ParameterDefn<float>("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)", new ParameterDefn<float>("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)",
(float)BSLinkset.LinksetImplementation.Compound, (float)BSLinkset.LinksetImplementation.Compound,
(s) => { return LinksetImplementation; }, (s) => { return LinksetImplementation; },

View File

@ -86,7 +86,7 @@ public abstract class BSPhysObject : PhysicsActor
PhysBody = new BulletBody(localID); PhysBody = new BulletBody(localID);
PhysShape = new BulletShape(); PhysShape = new BulletShape();
LastAssetBuildFailed = false; PrimAssetState = PrimAssetCondition.Unknown;
// Default material type. Also sets Friction, Restitution and Density. // Default material type. Also sets Friction, Restitution and Density.
SetMaterial((int)MaterialAttributes.Material.Wood); SetMaterial((int)MaterialAttributes.Material.Wood);
@ -133,9 +133,13 @@ public abstract class BSPhysObject : PhysicsActor
// Reference to the physical shape (btCollisionShape) of this object // Reference to the physical shape (btCollisionShape) of this object
public BulletShape PhysShape; public BulletShape PhysShape;
// 'true' if the mesh's underlying asset failed to build. // The physical representation of the prim might require an asset fetch.
// This will keep us from looping after the first time the build failed. // The asset state is first 'Unknown' then 'Waiting' then either 'Failed' or 'Fetched'.
public bool LastAssetBuildFailed { get; set; } public enum PrimAssetCondition
{
Unknown, Waiting, Failed, Fetched
}
public PrimAssetCondition PrimAssetState { get; set; }
// The objects base shape information. Null if not a prim type shape. // The objects base shape information. Null if not a prim type shape.
public PrimitiveBaseShape BaseShape { get; protected set; } public PrimitiveBaseShape BaseShape { get; protected set; }

View File

@ -155,7 +155,7 @@ public class BSPrim : BSPhysObject
public override PrimitiveBaseShape Shape { public override PrimitiveBaseShape Shape {
set { set {
BaseShape = value; BaseShape = value;
LastAssetBuildFailed = false; PrimAssetState = PrimAssetCondition.Unknown;
ForceBodyShapeRebuild(false); ForceBodyShapeRebuild(false);
} }
} }

View File

@ -447,17 +447,10 @@ public sealed class BSShapeCollection : IDisposable
// If the prim attributes are simple, this could be a simple Bullet native shape // If the prim attributes are simple, this could be a simple Bullet native shape
if (!haveShape if (!haveShape
&& nativeShapePossible
&& pbs != null && pbs != null
&& !pbs.SculptEntry && !pbs.SculptEntry
&& nativeShapePossible && ((pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim) || PrimHasNoCuts(pbs)) )
&& ((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) ) )
{ {
// Get the scale of any existing shape so we can see if the new shape is same native type and same size. // 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; OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero;
@ -508,6 +501,18 @@ public sealed class BSShapeCollection : IDisposable
return ret; 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. // return 'true' if the prim's shape was changed.
public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback) public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
{ {
@ -518,7 +523,7 @@ public sealed class BSShapeCollection : IDisposable
if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects) if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects)
{ {
// Update prim.BSShape to reference a hull of this shape. // 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}", if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}",
prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X")); 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. // See that hull shape exists in the physical world and update prim.BSShape.
// We could be creating the hull because scale changed or whatever. // 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) private bool GetReferenceToHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
{ {
BulletShape newShape; BulletShape newShape;
@ -717,6 +723,7 @@ public sealed class BSShapeCollection : IDisposable
DereferenceShape(prim.PhysShape, shapeCallback); DereferenceShape(prim.PhysShape, shapeCallback);
newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, prim.BaseShape, prim.Size, lod); 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); newShape = VerifyMeshCreated(newShape, prim);
ReferenceShape(newShape); ReferenceShape(newShape);
@ -735,13 +742,13 @@ public sealed class BSShapeCollection : IDisposable
HullDesc hullDesc; HullDesc hullDesc;
if (Hulls.TryGetValue(newHullKey, out 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(); newShape = hullDesc.shape.Clone();
} }
else else
{ {
// Build a new hull in the physical world // 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 // 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); IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false, false, false);
if (meshData != null) if (meshData != null)
{ {
@ -761,15 +768,35 @@ public sealed class BSShapeCollection : IDisposable
convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); 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 // setup and do convex hull conversion
m_hulls = new List<ConvexResult>(); m_hulls = new List<ConvexResult>();
DecompDesc dcomp = new DecompDesc(); DecompDesc dcomp = new DecompDesc();
dcomp.mIndices = convIndices; dcomp.mIndices = convIndices;
dcomp.mVertices = convVertices; 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); ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
// create the hull into the _hulls variable // create the hull into the _hulls variable
convexBuilder.process(dcomp); 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. // Convert the vertices and indices for passing to unmanaged.
// The hull information is passed as a large floating point array. // The hull information is passed as a large floating point array.
// The format is: // The format is:
@ -905,11 +932,15 @@ public sealed class BSShapeCollection : IDisposable
return newShape; return newShape;
// If this mesh has an underlying asset and we have not failed getting it before, fetch the asset // 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); DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset", prim.LocalID);
// This will prevent looping through this code as we keep trying to get the failed shape // Multiple requestors will know we're waiting for this asset
prim.LastAssetBuildFailed = true; prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting;
BSPhysObject xprim = prim; BSPhysObject xprim = prim;
Util.FireAndForget(delegate Util.FireAndForget(delegate
@ -920,7 +951,7 @@ public sealed class BSShapeCollection : IDisposable
BSPhysObject yprim = xprim; // probably not necessary, but, just in case. BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
{ {
bool assetFound = false; // DEBUG DEBUG bool assetFound = false;
string mismatchIDs = String.Empty; // DEBUG DEBUG string mismatchIDs = String.Empty; // DEBUG DEBUG
if (asset != null && yprim.BaseShape.SculptEntry) if (asset != null && yprim.BaseShape.SculptEntry)
{ {
@ -938,6 +969,10 @@ public sealed class BSShapeCollection : IDisposable
mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID; 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}", DetailLog("{0},BSShapeCollection,fetchAssetCallback,found={1},isSculpt={2},ids={3}",
yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs ); yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs );
@ -945,6 +980,7 @@ public sealed class BSShapeCollection : IDisposable
} }
else else
{ {
xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed;
PhysicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}", PhysicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}",
LogHeader, PhysicsScene.Name); LogHeader, PhysicsScene.Name);
} }
@ -952,7 +988,7 @@ public sealed class BSShapeCollection : IDisposable
} }
else else
{ {
if (prim.LastAssetBuildFailed) if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Failed)
{ {
PhysicsScene.Logger.ErrorFormat("{0} Mesh failed to fetch asset. lID={1}, texture={2}", PhysicsScene.Logger.ErrorFormat("{0} Mesh failed to fetch asset. lID={1}, texture={2}",
LogHeader, prim.LocalID, prim.BaseShape.SculptTexture); LogHeader, prim.LocalID, prim.BaseShape.SculptTexture);