Merge branch 'master' into cooptermination
commit
55c6753b13
|
@ -1026,6 +1026,7 @@ namespace OpenSim.Framework.Servers.HttpServer
|
|||
}
|
||||
|
||||
// JsonRpc (v2.0 only)
|
||||
// Batch requests not yet supported
|
||||
private byte[] HandleJsonRpcRequests(OSHttpRequest request, OSHttpResponse response)
|
||||
{
|
||||
Stream requestStream = request.InputStream;
|
||||
|
@ -1065,8 +1066,26 @@ namespace OpenSim.Framework.Servers.HttpServer
|
|||
{
|
||||
jsonRpcHandlers.TryGetValue(methodname, out method);
|
||||
}
|
||||
|
||||
method(jsonRpcRequest, ref jsonRpcResponse);
|
||||
bool res = false;
|
||||
try
|
||||
{
|
||||
res = method(jsonRpcRequest, ref jsonRpcResponse);
|
||||
if(!res)
|
||||
{
|
||||
// The handler sent back an unspecified error
|
||||
if(jsonRpcResponse.Error.Code == 0)
|
||||
{
|
||||
jsonRpcResponse.Error.Code = ErrorCode.InternalError;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string ErrorMessage = string.Format("[BASE HTTP SERVER]: Json-Rpc Handler Error method {0} - {1}", methodname, e.Message);
|
||||
m_log.Error(ErrorMessage);
|
||||
jsonRpcResponse.Error.Code = ErrorCode.InternalError;
|
||||
jsonRpcResponse.Error.Message = ErrorMessage;
|
||||
}
|
||||
}
|
||||
else // Error no hanlder defined for requested method
|
||||
{
|
||||
|
@ -1731,6 +1750,7 @@ namespace OpenSim.Framework.Servers.HttpServer
|
|||
|
||||
// Long Poll Service Manager with 3 worker threads a 25 second timeout for no events
|
||||
m_PollServiceManager = new PollServiceRequestManager(this, 3, 25000);
|
||||
m_PollServiceManager.Start();
|
||||
HTTPDRunning = true;
|
||||
|
||||
//HttpListenerContext context;
|
||||
|
@ -1781,6 +1801,8 @@ namespace OpenSim.Framework.Servers.HttpServer
|
|||
HTTPDRunning = false;
|
||||
try
|
||||
{
|
||||
m_PollServiceManager.Stop();
|
||||
|
||||
m_httpListener2.ExceptionThrown -= httpServerException;
|
||||
//m_httpListener2.DisconnectHandler = null;
|
||||
|
||||
|
|
|
@ -30,5 +30,5 @@ using OpenMetaverse.StructuredData;
|
|||
|
||||
namespace OpenSim.Framework.Servers.HttpServer
|
||||
{
|
||||
public delegate void JsonRPCMethod(OSDMap jsonRpcRequest, ref JsonRpcResponse response);
|
||||
public delegate bool JsonRPCMethod(OSDMap jsonRpcRequest, ref JsonRpcResponse response);
|
||||
}
|
||||
|
|
|
@ -45,19 +45,26 @@ namespace OpenSim.Framework.Servers.HttpServer
|
|||
private uint m_WorkerThreadCount = 0;
|
||||
private Thread[] m_workerThreads;
|
||||
private PollServiceWorkerThread[] m_PollServiceWorkerThreads;
|
||||
private bool m_running = true;
|
||||
private volatile bool m_running = true;
|
||||
private int m_pollTimeout;
|
||||
|
||||
public PollServiceRequestManager(BaseHttpServer pSrv, uint pWorkerThreadCount, int pTimeout)
|
||||
{
|
||||
m_server = pSrv;
|
||||
m_WorkerThreadCount = pWorkerThreadCount;
|
||||
m_pollTimeout = pTimeout;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
m_running = true;
|
||||
m_workerThreads = new Thread[m_WorkerThreadCount];
|
||||
m_PollServiceWorkerThreads = new PollServiceWorkerThread[m_WorkerThreadCount];
|
||||
|
||||
//startup worker threads
|
||||
for (uint i = 0; i < m_WorkerThreadCount; i++)
|
||||
{
|
||||
m_PollServiceWorkerThreads[i] = new PollServiceWorkerThread(m_server, pTimeout);
|
||||
m_PollServiceWorkerThreads[i] = new PollServiceWorkerThread(m_server, m_pollTimeout);
|
||||
m_PollServiceWorkerThreads[i].ReQueue += ReQueueEvent;
|
||||
|
||||
m_workerThreads[i]
|
||||
|
@ -136,8 +143,10 @@ namespace OpenSim.Framework.Servers.HttpServer
|
|||
|
||||
}
|
||||
|
||||
~PollServiceRequestManager()
|
||||
public void Stop()
|
||||
{
|
||||
m_running = false;
|
||||
|
||||
foreach (object o in m_requests)
|
||||
{
|
||||
PollServiceHttpRequest req = (PollServiceHttpRequest) o;
|
||||
|
@ -151,8 +160,6 @@ namespace OpenSim.Framework.Servers.HttpServer
|
|||
{
|
||||
t.Abort();
|
||||
}
|
||||
|
||||
m_running = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -299,6 +299,18 @@ namespace OpenSim.Framework
|
|||
x;
|
||||
}
|
||||
|
||||
// Clamp the maximum magnitude of a vector
|
||||
public static Vector3 ClampV(Vector3 x, float max)
|
||||
{
|
||||
Vector3 ret = x;
|
||||
float lenSq = x.LengthSquared();
|
||||
if (lenSq > (max * max))
|
||||
{
|
||||
x = x / x.Length() * max;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
// Inclusive, within range test (true if equal to the endpoints)
|
||||
public static bool InRange<T>(T x, T min, T max)
|
||||
where T : IComparable<T>
|
||||
|
@ -1646,8 +1658,13 @@ namespace OpenSim.Framework
|
|||
if (m_ThreadPool != null)
|
||||
throw new InvalidOperationException("SmartThreadPool is already initialized");
|
||||
|
||||
m_ThreadPool = new SmartThreadPool(2000, maxThreads, 2);
|
||||
m_ThreadPool.Name = "Util";
|
||||
STPStartInfo startInfo = new STPStartInfo();
|
||||
startInfo.ThreadPoolName = "Util";
|
||||
startInfo.IdleTimeout = 2000;
|
||||
startInfo.MaxWorkerThreads = maxThreads;
|
||||
startInfo.MinWorkerThreads = 2;
|
||||
|
||||
m_ThreadPool = new SmartThreadPool(startInfo);
|
||||
}
|
||||
|
||||
public static int FireAndForgetCount()
|
||||
|
|
|
@ -87,13 +87,24 @@ namespace OpenSim.Region.Framework.Scenes.Animation
|
|||
return false;
|
||||
}
|
||||
|
||||
public bool Remove(UUID animID)
|
||||
/// <summary>
|
||||
/// Remove the specified animation
|
||||
/// </summary>
|
||||
/// <param name='animID'></param>
|
||||
/// <param name='allowNoDefault'>
|
||||
/// If true, then the default animation can be entirely removed.
|
||||
/// If false, then removing the default animation will reset it to the simulator default (currently STAND).
|
||||
/// </param>
|
||||
public bool Remove(UUID animID, bool allowNoDefault)
|
||||
{
|
||||
lock (m_animations)
|
||||
{
|
||||
if (m_defaultAnimation.AnimID == animID)
|
||||
{
|
||||
if (allowNoDefault)
|
||||
m_defaultAnimation = new OpenSim.Framework.Animation(UUID.Zero, 1, UUID.Zero);
|
||||
else
|
||||
ResetDefaultAnimation();
|
||||
}
|
||||
else if (HasAnimation(animID))
|
||||
{
|
||||
|
|
|
@ -86,7 +86,10 @@ namespace OpenSim.Region.Framework.Scenes.Animation
|
|||
if (m_scenePresence.IsChildAgent)
|
||||
return;
|
||||
|
||||
// m_log.DebugFormat("[SCENE PRESENCE ANIMATOR]: Adding animation {0} for {1}", animID, m_scenePresence.Name);
|
||||
if (m_scenePresence.Scene.DebugAnimations)
|
||||
m_log.DebugFormat(
|
||||
"[SCENE PRESENCE ANIMATOR]: Adding animation {0} {1} for {2}",
|
||||
GetAnimName(animID), animID, m_scenePresence.Name);
|
||||
|
||||
if (m_animations.Add(animID, m_scenePresence.ControllingClient.NextAnimationSequenceNumber, objectID))
|
||||
SendAnimPack();
|
||||
|
@ -109,14 +112,25 @@ namespace OpenSim.Region.Framework.Scenes.Animation
|
|||
AddAnimation(animID, objectID);
|
||||
}
|
||||
|
||||
public void RemoveAnimation(UUID animID)
|
||||
/// <summary>
|
||||
/// Remove the specified animation
|
||||
/// </summary>
|
||||
/// <param name='animID'></param>
|
||||
/// <param name='allowNoDefault'>
|
||||
/// If true, then the default animation can be entirely removed.
|
||||
/// If false, then removing the default animation will reset it to the simulator default (currently STAND).
|
||||
/// </param>
|
||||
public void RemoveAnimation(UUID animID, bool allowNoDefault)
|
||||
{
|
||||
if (m_scenePresence.IsChildAgent)
|
||||
return;
|
||||
|
||||
// m_log.DebugFormat("[SCENE PRESENCE ANIMATOR]: Removing animation {0} for {1}", animID, m_scenePresence.Name);
|
||||
if (m_scenePresence.Scene.DebugAnimations)
|
||||
m_log.DebugFormat(
|
||||
"[SCENE PRESENCE ANIMATOR]: Removing animation {0} {1} for {2}",
|
||||
GetAnimName(animID), animID, m_scenePresence.Name);
|
||||
|
||||
if (m_animations.Remove(animID))
|
||||
if (m_animations.Remove(animID, allowNoDefault))
|
||||
SendAnimPack();
|
||||
}
|
||||
|
||||
|
@ -132,14 +146,15 @@ namespace OpenSim.Region.Framework.Scenes.Animation
|
|||
if (animID == UUID.Zero)
|
||||
return;
|
||||
|
||||
RemoveAnimation(animID);
|
||||
RemoveAnimation(animID, true);
|
||||
}
|
||||
|
||||
public void ResetAnimations()
|
||||
{
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE PRESENCE ANIMATOR]: Resetting animations for {0} in {1}",
|
||||
// m_scenePresence.Name, m_scenePresence.Scene.RegionInfo.RegionName);
|
||||
if (m_scenePresence.Scene.DebugAnimations)
|
||||
m_log.DebugFormat(
|
||||
"[SCENE PRESENCE ANIMATOR]: Resetting animations for {0} in {1}",
|
||||
m_scenePresence.Name, m_scenePresence.Scene.RegionInfo.RegionName);
|
||||
|
||||
m_animations.Clear();
|
||||
}
|
||||
|
@ -550,5 +565,21 @@ namespace OpenSim.Region.Framework.Scenes.Animation
|
|||
|
||||
SendAnimPack(animIDs, sequenceNums, objectIDs);
|
||||
}
|
||||
|
||||
public string GetAnimName(UUID animId)
|
||||
{
|
||||
string animName;
|
||||
|
||||
if (!DefaultAvatarAnimations.AnimsNames.TryGetValue(animId, out animName))
|
||||
{
|
||||
AssetMetadata amd = m_scenePresence.Scene.AssetService.GetMetadata(animId.ToString());
|
||||
if (amd != null)
|
||||
animName = amd.Name;
|
||||
else
|
||||
animName = "Unknown";
|
||||
}
|
||||
|
||||
return animName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,11 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
public bool EmergencyMonitoring = false;
|
||||
|
||||
/// <summary>
|
||||
/// Show debug information about animations.
|
||||
/// </summary>
|
||||
public bool DebugAnimations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Show debug information about teleports.
|
||||
/// </summary>
|
||||
|
@ -1978,6 +1983,19 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
EventManager.TriggerPrimsLoaded(this);
|
||||
}
|
||||
|
||||
public bool SupportsRayCastFiltered()
|
||||
{
|
||||
if (PhysicsScene == null)
|
||||
return false;
|
||||
return PhysicsScene.SupportsRaycastWorldFiltered();
|
||||
}
|
||||
|
||||
public object RayCastFiltered(Vector3 position, Vector3 direction, float length, int Count, RayFilterFlags filter)
|
||||
{
|
||||
if (PhysicsScene == null)
|
||||
return null;
|
||||
return PhysicsScene.RaycastWorld(position, direction, length, Count,filter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a new rez location based on the raycast and the size of the object that is being rezzed.
|
||||
|
|
|
@ -1951,6 +1951,16 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
{
|
||||
PhysicsActor pa = PhysActor;
|
||||
|
||||
if (pa != null)
|
||||
return new Vector3(pa.GeometricCenter.X, pa.GeometricCenter.Y, pa.GeometricCenter.Z);
|
||||
else
|
||||
return new Vector3(0, 0, 0);
|
||||
}
|
||||
|
||||
public Vector3 GetCenterOfMass()
|
||||
{
|
||||
PhysicsActor pa = PhysActor;
|
||||
|
||||
if (pa != null)
|
||||
return new Vector3(pa.CenterOfMass.X, pa.CenterOfMass.Y, pa.CenterOfMass.Z);
|
||||
else
|
||||
|
|
|
@ -1954,8 +1954,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
{
|
||||
if (ParentID != 0)
|
||||
{
|
||||
var targetPart = m_scene.GetSceneObjectPart(targetID);
|
||||
if (targetPart != null && targetPart.LocalId == ParentID)
|
||||
if (ParentPart.UUID == targetID)
|
||||
return; // already sitting here, ignore
|
||||
|
||||
StandUp();
|
||||
|
@ -2260,7 +2259,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
public void HandleStopAnim(IClientAPI remoteClient, UUID animID)
|
||||
{
|
||||
Animator.RemoveAnimation(animID);
|
||||
Animator.RemoveAnimation(animID, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -161,12 +161,13 @@ namespace OpenSim.Region.OptionalModules.Avatar.Animations
|
|||
UUID defaultAnimId = anims.DefaultAnimation.AnimID;
|
||||
cdl.AddRow(
|
||||
"Default anim",
|
||||
string.Format("{0}, {1}", defaultAnimId, GetAnimName(sp.Scene.AssetService, defaultAnimId)));
|
||||
string.Format("{0}, {1}", defaultAnimId, sp.Animator.GetAnimName(defaultAnimId)));
|
||||
|
||||
UUID implicitDefaultAnimId = anims.ImplicitDefaultAnimation.AnimID;
|
||||
cdl.AddRow(
|
||||
"Implicit default anim",
|
||||
string.Format("{0}, {1}", implicitDefaultAnimId, GetAnimName(sp.Scene.AssetService, implicitDefaultAnimId)));
|
||||
string.Format("{0}, {1}",
|
||||
implicitDefaultAnimId, sp.Animator.GetAnimName(implicitDefaultAnimId)));
|
||||
|
||||
cdl.AddToStringBuilder(sb);
|
||||
|
||||
|
@ -185,7 +186,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Animations
|
|||
for (int i = 0; i < animIds.Length; i++)
|
||||
{
|
||||
UUID animId = animIds[i];
|
||||
string animName = GetAnimName(sp.Scene.AssetService, animId);
|
||||
string animName = sp.Animator.GetAnimName(animId);
|
||||
int seq = sequenceNumbers[i];
|
||||
UUID objectId = objectIds[i];
|
||||
|
||||
|
@ -195,21 +196,5 @@ namespace OpenSim.Region.OptionalModules.Avatar.Animations
|
|||
cdt.AddToStringBuilder(sb);
|
||||
sb.Append("\n");
|
||||
}
|
||||
|
||||
private string GetAnimName(IAssetService assetService, UUID animId)
|
||||
{
|
||||
string animName;
|
||||
|
||||
if (!DefaultAvatarAnimations.AnimsNames.TryGetValue(animId, out animName))
|
||||
{
|
||||
AssetMetadata amd = assetService.GetMetadata(animId.ToString());
|
||||
if (amd != null)
|
||||
animName = amd.Name;
|
||||
else
|
||||
animName = "Unknown";
|
||||
}
|
||||
|
||||
return animName;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -199,7 +199,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
m_log.DebugFormat("[IRC-Channel-{0}] PrivateMessageFormat : <{1}>", cs.idn, cs.PrivateMessageFormat);
|
||||
cs.NoticeMessageFormat = Substitute(rs, config.GetString("noticeformat", cs.NoticeMessageFormat));
|
||||
m_log.DebugFormat("[IRC-Channel-{0}] NoticeMessageFormat : <{1}>", cs.idn, cs.NoticeMessageFormat);
|
||||
cs.ClientReporting = Convert.ToInt32(Substitute(rs, config.GetString("verbosity", cs.ClientReporting?"1":"0"))) > 0;
|
||||
cs.ClientReporting = Convert.ToInt32(Substitute(rs, config.GetString("verbosity", cs.ClientReporting ? "1" : "0"))) > 0;
|
||||
m_log.DebugFormat("[IRC-Channel-{0}] ClientReporting : <{1}>", cs.idn, cs.ClientReporting);
|
||||
cs.ClientReporting = Convert.ToBoolean(Substitute(rs, config.GetString("report_clients", Convert.ToString(cs.ClientReporting))));
|
||||
m_log.DebugFormat("[IRC-Channel-{0}] ClientReporting : <{1}>", cs.idn, cs.ClientReporting);
|
||||
|
@ -473,27 +473,27 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
{
|
||||
|
||||
string vvar = arg.Match(result).ToString();
|
||||
string var = vvar.Substring(1,vvar.Length-2).Trim();
|
||||
string var = vvar.Substring(1, vvar.Length - 2).Trim();
|
||||
|
||||
switch (var.ToLower())
|
||||
{
|
||||
case "%region" :
|
||||
case "%region":
|
||||
result = result.Replace(vvar, rs.Region);
|
||||
break;
|
||||
case "%host" :
|
||||
case "%host":
|
||||
result = result.Replace(vvar, rs.Host);
|
||||
break;
|
||||
case "%locx" :
|
||||
case "%locx":
|
||||
result = result.Replace(vvar, rs.LocX);
|
||||
break;
|
||||
case "%locy" :
|
||||
case "%locy":
|
||||
result = result.Replace(vvar, rs.LocY);
|
||||
break;
|
||||
case "%k" :
|
||||
case "%k":
|
||||
result = result.Replace(vvar, rs.IDK);
|
||||
break;
|
||||
default :
|
||||
result = result.Replace(vvar, rs.config.GetString(var,var));
|
||||
default:
|
||||
result = result.Replace(vvar, rs.config.GetString(var, var));
|
||||
break;
|
||||
}
|
||||
// m_log.DebugFormat("[IRC-Channel] Parse[2]: {0}", result);
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
internal static bool m_pluginEnabled = false;
|
||||
internal static bool Enabled = false;
|
||||
internal static IConfig m_config = null;
|
||||
|
||||
internal static List<ChannelState> m_channels = new List<ChannelState>();
|
||||
|
@ -72,13 +72,13 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
m_config = config.Configs["IRC"];
|
||||
if (m_config == null)
|
||||
{
|
||||
// m_log.InfoFormat("[IRC-Bridge] module not configured");
|
||||
// m_log.InfoFormat("[IRC-Bridge] module not configured");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_config.GetBoolean("enabled", false))
|
||||
{
|
||||
// m_log.InfoFormat("[IRC-Bridge] module disabled in configuration");
|
||||
// m_log.InfoFormat("[IRC-Bridge] module disabled in configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -87,19 +87,22 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
m_password = config.Configs["RemoteAdmin"].GetString("access_password", m_password);
|
||||
}
|
||||
|
||||
m_pluginEnabled = true;
|
||||
m_log.InfoFormat("[IRC-Bridge]: Module enabled");
|
||||
Enabled = true;
|
||||
|
||||
m_log.InfoFormat("[IRC-Bridge]: Module is enabled");
|
||||
}
|
||||
|
||||
public void AddRegion(Scene scene)
|
||||
{
|
||||
if (m_pluginEnabled)
|
||||
if (Enabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_log.InfoFormat("[IRC-Bridge] Connecting region {0}", scene.RegionInfo.RegionName);
|
||||
|
||||
if (!String.IsNullOrEmpty(m_password))
|
||||
MainServer.Instance.AddXmlRPCHandler("irc_admin", XmlRpcAdminMethod, false);
|
||||
|
||||
m_region = new RegionState(scene, m_config);
|
||||
lock (m_regions) m_regions.Add(m_region);
|
||||
m_region.Open();
|
||||
|
@ -123,7 +126,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
|
||||
public void RemoveRegion(Scene scene)
|
||||
{
|
||||
if (!m_pluginEnabled)
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
if (m_region == null)
|
||||
|
|
|
@ -295,15 +295,19 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
m_nick, m_ircChannel, m_server));
|
||||
m_writer.Flush();
|
||||
}
|
||||
catch (Exception) {}
|
||||
catch (Exception) { }
|
||||
|
||||
|
||||
m_connected = false;
|
||||
|
||||
try { m_writer.Close(); } catch (Exception) {}
|
||||
try { m_reader.Close(); } catch (Exception) {}
|
||||
try { m_stream.Close(); } catch (Exception) {}
|
||||
try { m_tcp.Close(); } catch (Exception) {}
|
||||
try { m_writer.Close(); }
|
||||
catch (Exception) { }
|
||||
try { m_reader.Close(); }
|
||||
catch (Exception) { }
|
||||
try { m_stream.Close(); }
|
||||
catch (Exception) { }
|
||||
try { m_tcp.Close(); }
|
||||
catch (Exception) { }
|
||||
|
||||
}
|
||||
|
||||
|
@ -418,9 +422,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
// the socket and it will disappear of its own accord, once this
|
||||
// processing is completed.
|
||||
|
||||
try { m_writer.Close(); } catch (Exception) {}
|
||||
try { m_reader.Close(); } catch (Exception) {}
|
||||
try { m_tcp.Close(); } catch (Exception) {}
|
||||
try { m_writer.Close(); }
|
||||
catch (Exception) { }
|
||||
try { m_reader.Close(); }
|
||||
catch (Exception) { }
|
||||
try { m_tcp.Close(); }
|
||||
catch (Exception) { }
|
||||
|
||||
m_connected = false;
|
||||
m_pending = false;
|
||||
|
@ -627,12 +634,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
// ":" indicates that the remainder of the
|
||||
// line is a single parameter value.
|
||||
|
||||
commArgs = command.Split(CS_SPACE,2);
|
||||
commArgs = command.Split(CS_SPACE, 2);
|
||||
|
||||
if (commArgs[0].StartsWith(":"))
|
||||
{
|
||||
pfx = commArgs[0].Substring(1);
|
||||
commArgs = commArgs[1].Split(CS_SPACE,2);
|
||||
commArgs = commArgs[1].Split(CS_SPACE, 2);
|
||||
}
|
||||
|
||||
cmd = commArgs[0];
|
||||
|
@ -646,11 +653,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
// Messages 001-004 are always sent
|
||||
// following signon.
|
||||
|
||||
case "001" : // Welcome ...
|
||||
case "002" : // Server information
|
||||
case "003" : // Welcome ...
|
||||
case "001": // Welcome ...
|
||||
case "002": // Server information
|
||||
case "003": // Welcome ...
|
||||
break;
|
||||
case "004" : // Server information
|
||||
case "004": // Server information
|
||||
m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
|
||||
commArgs = parms.Split(CS_SPACE);
|
||||
c_server = commArgs[1];
|
||||
|
@ -659,31 +666,31 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
usermod = commArgs[3];
|
||||
chanmod = commArgs[4];
|
||||
break;
|
||||
case "005" : // Server information
|
||||
case "005": // Server information
|
||||
break;
|
||||
case "042" :
|
||||
case "250" :
|
||||
case "251" :
|
||||
case "252" :
|
||||
case "254" :
|
||||
case "255" :
|
||||
case "265" :
|
||||
case "266" :
|
||||
case "332" : // Subject
|
||||
case "333" : // Subject owner (?)
|
||||
case "353" : // Name list
|
||||
case "366" : // End-of-Name list marker
|
||||
case "372" : // MOTD body
|
||||
case "375" : // MOTD start
|
||||
case "042":
|
||||
case "250":
|
||||
case "251":
|
||||
case "252":
|
||||
case "254":
|
||||
case "255":
|
||||
case "265":
|
||||
case "266":
|
||||
case "332": // Subject
|
||||
case "333": // Subject owner (?)
|
||||
case "353": // Name list
|
||||
case "366": // End-of-Name list marker
|
||||
case "372": // MOTD body
|
||||
case "375": // MOTD start
|
||||
// m_log.InfoFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]);
|
||||
break;
|
||||
case "376" : // MOTD end
|
||||
case "376": // MOTD end
|
||||
// m_log.InfoFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]);
|
||||
motd = true;
|
||||
break;
|
||||
case "451" : // Not registered
|
||||
case "451": // Not registered
|
||||
break;
|
||||
case "433" : // Nickname in use
|
||||
case "433": // Nickname in use
|
||||
// Gen a new name
|
||||
m_nick = m_baseNick + Util.RandomClass.Next(1, 99);
|
||||
m_log.ErrorFormat("[IRC-Connector-{0}]: [{1}] IRC SERVER reports NicknameInUse, trying {2}", idn, cmd, m_nick);
|
||||
|
@ -695,29 +702,29 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
m_writer.WriteLine(String.Format("JOIN {0}", m_ircChannel));
|
||||
m_writer.Flush();
|
||||
break;
|
||||
case "479" : // Bad channel name, etc. This will never work, so disable the connection
|
||||
m_log.ErrorFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]);
|
||||
case "479": // Bad channel name, etc. This will never work, so disable the connection
|
||||
m_log.ErrorFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE, 2)[1]);
|
||||
m_log.ErrorFormat("[IRC-Connector-{0}] [{1}] Connector disabled", idn, cmd);
|
||||
m_enabled = false;
|
||||
m_connected = false;
|
||||
m_pending = false;
|
||||
break;
|
||||
case "NOTICE" :
|
||||
case "NOTICE":
|
||||
// m_log.WarnFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]);
|
||||
break;
|
||||
case "ERROR" :
|
||||
m_log.ErrorFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]);
|
||||
case "ERROR":
|
||||
m_log.ErrorFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE, 2)[1]);
|
||||
if (parms.Contains("reconnect too fast"))
|
||||
ICCD_PERIOD++;
|
||||
m_pending = false;
|
||||
Reconnect();
|
||||
break;
|
||||
case "PING" :
|
||||
case "PING":
|
||||
m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
|
||||
m_writer.WriteLine(String.Format("PONG {0}", parms));
|
||||
m_writer.Flush();
|
||||
break;
|
||||
case "PONG" :
|
||||
case "PONG":
|
||||
break;
|
||||
case "JOIN":
|
||||
if (m_pending)
|
||||
|
@ -748,7 +755,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
|
||||
eventIrcQuit(pfx, cmd, parms);
|
||||
break;
|
||||
default :
|
||||
default:
|
||||
m_log.DebugFormat("[IRC-Connector-{0}] Command '{1}' ignored, parms = {2}", idn, cmd, parms);
|
||||
break;
|
||||
}
|
||||
|
@ -759,7 +766,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
|
||||
public void eventIrcJoin(string prefix, string command, string parms)
|
||||
{
|
||||
string[] args = parms.Split(CS_SPACE,2);
|
||||
string[] args = parms.Split(CS_SPACE, 2);
|
||||
string IrcUser = prefix.Split('!')[0];
|
||||
string IrcChannel = args[0];
|
||||
|
||||
|
@ -772,7 +779,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
|
||||
public void eventIrcPart(string prefix, string command, string parms)
|
||||
{
|
||||
string[] args = parms.Split(CS_SPACE,2);
|
||||
string[] args = parms.Split(CS_SPACE, 2);
|
||||
string IrcUser = prefix.Split('!')[0];
|
||||
string IrcChannel = args[0];
|
||||
|
||||
|
@ -782,7 +789,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
|
||||
public void eventIrcMode(string prefix, string command, string parms)
|
||||
{
|
||||
string[] args = parms.Split(CS_SPACE,2);
|
||||
string[] args = parms.Split(CS_SPACE, 2);
|
||||
string UserMode = args[1];
|
||||
|
||||
m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCMode {1}:{2}", idn, m_server, m_ircChannel);
|
||||
|
@ -794,7 +801,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
|
||||
public void eventIrcNickChange(string prefix, string command, string parms)
|
||||
{
|
||||
string[] args = parms.Split(CS_SPACE,2);
|
||||
string[] args = parms.Split(CS_SPACE, 2);
|
||||
string UserOldNick = prefix.Split('!')[0];
|
||||
string UserNewNick = args[0].Remove(0, 1);
|
||||
|
||||
|
@ -804,7 +811,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
|
||||
public void eventIrcKick(string prefix, string command, string parms)
|
||||
{
|
||||
string[] args = parms.Split(CS_SPACE,3);
|
||||
string[] args = parms.Split(CS_SPACE, 3);
|
||||
string UserKicker = prefix.Split('!')[0];
|
||||
string IrcChannel = args[0];
|
||||
string UserKicked = args[1];
|
||||
|
@ -842,7 +849,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
|
||||
// m_log.InfoFormat("[IRC-Watchdog] Status scan, pdk = {0}, icc = {1}", _pdk_, _icc_);
|
||||
|
||||
_pdk_ = (_pdk_+1)%PING_PERIOD; // cycle the ping trigger
|
||||
_pdk_ = (_pdk_ + 1) % PING_PERIOD; // cycle the ping trigger
|
||||
_icc_++; // increment the inter-consecutive-connect-delay counter
|
||||
|
||||
lock (m_connectors)
|
||||
|
|
|
@ -41,7 +41,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
|
||||
internal class RegionState
|
||||
{
|
||||
|
||||
private static readonly ILog m_log =
|
||||
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
|
@ -66,6 +65,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
internal IConfig config = null; // configuration file reference
|
||||
internal bool enabled = true;
|
||||
|
||||
//AgentAlert
|
||||
internal bool showAlert = false;
|
||||
internal string alertMessage = String.Empty;
|
||||
internal IDialogModule dialogModule = null;
|
||||
|
||||
// This list is used to keep track of who is here, and by
|
||||
// implication, who is not.
|
||||
|
||||
|
@ -75,7 +79,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
|
||||
public RegionState(Scene p_scene, IConfig p_config)
|
||||
{
|
||||
|
||||
scene = p_scene;
|
||||
config = p_config;
|
||||
|
||||
|
@ -85,6 +88,25 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
LocY = Convert.ToString(scene.RegionInfo.RegionLocY);
|
||||
IDK = Convert.ToString(_idk_++);
|
||||
|
||||
showAlert = config.GetBoolean("alert_show", false);
|
||||
string alertServerInfo = String.Empty;
|
||||
|
||||
if (showAlert)
|
||||
{
|
||||
bool showAlertServerInfo = config.GetBoolean("alert_show_serverinfo", true);
|
||||
|
||||
if (showAlertServerInfo)
|
||||
alertServerInfo = String.Format("\nServer: {0}\nPort: {1}\nChannel: {2}\n\n",
|
||||
config.GetString("server", ""), config.GetString("port", ""), config.GetString("channel", ""));
|
||||
|
||||
string alertPreMessage = config.GetString("alert_msg_pre", "This region is linked to Irc.");
|
||||
string alertPostMessage = config.GetString("alert_msg_post", "Everything you say in public chat can be listened.");
|
||||
|
||||
alertMessage = String.Format("{0}\n{1}{2}", alertPreMessage, alertServerInfo, alertPostMessage);
|
||||
|
||||
dialogModule = scene.RequestModuleInterface<IDialogModule>();
|
||||
}
|
||||
|
||||
// OpenChannel conditionally establishes a connection to the
|
||||
// IRC server. The request will either succeed, or it will
|
||||
// throw an exception.
|
||||
|
@ -195,7 +217,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
|
||||
private void OnMakeRootAgent(ScenePresence presence)
|
||||
{
|
||||
|
||||
IClientAPI client = presence.ControllingClient;
|
||||
|
||||
try
|
||||
|
@ -216,17 +237,18 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dialogModule != null && showAlert)
|
||||
dialogModule.SendAlertToUser(client, alertMessage, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
m_log.ErrorFormat("[IRC-Region {0}]: MakeRootAgent exception: {1}", Region, ex.Message);
|
||||
m_log.Debug(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// This handler detects chat events int he virtual world.
|
||||
|
||||
public void OnSimChat(Object sender, OSChatMessage msg)
|
||||
{
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments
|
|||
"debug scene get",
|
||||
"List current scene options.",
|
||||
"If active is false then main scene update and maintenance loops are suspended.\n"
|
||||
+ "If animations is true then extra animations debug information is logged.\n"
|
||||
+ "If collisions is false then collisions with other objects are turned off.\n"
|
||||
+ "If pbackup is false then periodic scene backup is turned off.\n"
|
||||
+ "If physics is false then all physics objects are non-physical.\n"
|
||||
|
@ -107,6 +108,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments
|
|||
"debug scene set active|collisions|pbackup|physics|scripting|teleport|updates true|false",
|
||||
"Turn on scene debugging options.",
|
||||
"If active is false then main scene update and maintenance loops are suspended.\n"
|
||||
+ "If animations is true then extra animations debug information is logged.\n"
|
||||
+ "If collisions is false then collisions with other objects are turned off.\n"
|
||||
+ "If pbackup is false then periodic scene backup is turned off.\n"
|
||||
+ "If physics is false then all physics objects are non-physical.\n"
|
||||
|
@ -120,9 +122,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments
|
|||
{
|
||||
if (args.Length == 3)
|
||||
{
|
||||
if (MainConsole.Instance.ConsoleScene == null)
|
||||
MainConsole.Instance.Output("Please use 'change region <regioname>' first");
|
||||
else
|
||||
if (MainConsole.Instance.ConsoleScene != m_scene && MainConsole.Instance.ConsoleScene != null)
|
||||
return;
|
||||
|
||||
OutputSceneDebugOptions();
|
||||
}
|
||||
else
|
||||
|
@ -135,12 +137,14 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments
|
|||
{
|
||||
ConsoleDisplayList cdl = new ConsoleDisplayList();
|
||||
cdl.AddRow("active", m_scene.Active);
|
||||
cdl.AddRow("animations", m_scene.DebugAnimations);
|
||||
cdl.AddRow("pbackup", m_scene.PeriodicBackup);
|
||||
cdl.AddRow("physics", m_scene.PhysicsEnabled);
|
||||
cdl.AddRow("scripting", m_scene.ScriptsEnabled);
|
||||
cdl.AddRow("teleport", m_scene.DebugTeleporting);
|
||||
cdl.AddRow("updates", m_scene.DebugUpdates);
|
||||
|
||||
MainConsole.Instance.OutputFormat("Scene {0} options:", m_scene.Name);
|
||||
MainConsole.Instance.Output(cdl.ToString());
|
||||
}
|
||||
|
||||
|
@ -148,18 +152,14 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments
|
|||
{
|
||||
if (args.Length == 5)
|
||||
{
|
||||
if (MainConsole.Instance.ConsoleScene == null)
|
||||
{
|
||||
MainConsole.Instance.Output("Please use 'change region <regioname>' first");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MainConsole.Instance.ConsoleScene != m_scene && MainConsole.Instance.ConsoleScene != null)
|
||||
return;
|
||||
|
||||
string key = args[3];
|
||||
string value = args[4];
|
||||
SetSceneDebugOptions(new Dictionary<string, string>() { { key, value } });
|
||||
|
||||
MainConsole.Instance.OutputFormat("Set debug scene {0} = {1}", key, value);
|
||||
}
|
||||
MainConsole.Instance.OutputFormat("Set {0} debug scene {1} = {2}", m_scene.Name, key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -178,6 +178,14 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments
|
|||
m_scene.Active = active;
|
||||
}
|
||||
|
||||
if (options.ContainsKey("animations"))
|
||||
{
|
||||
bool active;
|
||||
|
||||
if (bool.TryParse(options["animations"], out active))
|
||||
m_scene.DebugAnimations = active;
|
||||
}
|
||||
|
||||
if (options.ContainsKey("pbackup"))
|
||||
{
|
||||
bool active;
|
||||
|
|
|
@ -1,814 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using log4net;
|
||||
using OMV = OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public sealed class BSCharacter : BSPhysObject
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
private static readonly string LogHeader = "[BULLETS CHAR]";
|
||||
|
||||
// private bool _stopped;
|
||||
private OMV.Vector3 _size;
|
||||
private bool _grabbed;
|
||||
private bool _selected;
|
||||
private OMV.Vector3 _position;
|
||||
private float _mass;
|
||||
private float _avatarDensity;
|
||||
private float _avatarVolume;
|
||||
private OMV.Vector3 _force;
|
||||
private OMV.Vector3 _velocity;
|
||||
private OMV.Vector3 _torque;
|
||||
private float _collisionScore;
|
||||
private OMV.Vector3 _acceleration;
|
||||
private OMV.Quaternion _orientation;
|
||||
private int _physicsActorType;
|
||||
private bool _isPhysical;
|
||||
private bool _flying;
|
||||
private bool _setAlwaysRun;
|
||||
private bool _throttleUpdates;
|
||||
private bool _isColliding;
|
||||
private bool _collidingObj;
|
||||
private bool _floatOnWater;
|
||||
private OMV.Vector3 _rotationalVelocity;
|
||||
private bool _kinematic;
|
||||
private float _buoyancy;
|
||||
|
||||
// The friction and velocity of the avatar is modified depending on whether walking or not.
|
||||
private OMV.Vector3 _appliedVelocity; // the last velocity applied to the avatar
|
||||
private float _currentFriction; // the friction currently being used (changed by setVelocity).
|
||||
|
||||
private BSVMotor _velocityMotor;
|
||||
|
||||
private OMV.Vector3 _PIDTarget;
|
||||
private bool _usePID;
|
||||
private float _PIDTau;
|
||||
private bool _useHoverPID;
|
||||
private float _PIDHoverHeight;
|
||||
private PIDHoverType _PIDHoverType;
|
||||
private float _PIDHoverTao;
|
||||
|
||||
public BSCharacter(uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, bool isFlying)
|
||||
: base(parent_scene, localID, avName, "BSCharacter")
|
||||
{
|
||||
_physicsActorType = (int)ActorTypes.Agent;
|
||||
_position = pos;
|
||||
|
||||
// Old versions of ScenePresence passed only the height. If width and/or depth are zero,
|
||||
// replace with the default values.
|
||||
_size = size;
|
||||
if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth;
|
||||
if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth;
|
||||
|
||||
// A motor to control the acceleration and deceleration of the avatar movement.
|
||||
// _velocityMotor = new BSVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f);
|
||||
// _velocityMotor = new BSPIDVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f);
|
||||
// Infinite decay and timescale values so motor only changes current to target values.
|
||||
_velocityMotor = new BSVMotor("BSCharacter.Velocity",
|
||||
0.2f, // time scale
|
||||
BSMotor.Infinite, // decay time scale
|
||||
BSMotor.InfiniteVector, // friction timescale
|
||||
1f // efficiency
|
||||
);
|
||||
_velocityMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages.
|
||||
|
||||
_flying = isFlying;
|
||||
_orientation = OMV.Quaternion.Identity;
|
||||
_velocity = OMV.Vector3.Zero;
|
||||
_appliedVelocity = OMV.Vector3.Zero;
|
||||
_buoyancy = ComputeBuoyancyFromFlying(isFlying);
|
||||
_currentFriction = BSParam.AvatarStandingFriction;
|
||||
_avatarDensity = BSParam.AvatarDensity;
|
||||
|
||||
// The dimensions of the avatar capsule are kept in the scale.
|
||||
// Physics creates a unit capsule which is scaled by the physics engine.
|
||||
ComputeAvatarScale(_size);
|
||||
// set _avatarVolume and _mass based on capsule size, _density and Scale
|
||||
ComputeAvatarVolumeAndMass();
|
||||
DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}",
|
||||
LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
|
||||
|
||||
// do actual creation in taint time
|
||||
PhysicsScene.TaintedObject("BSCharacter.create", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.create,taint", LocalID);
|
||||
// New body and shape into PhysBody and PhysShape
|
||||
PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this);
|
||||
|
||||
SetPhysicalProperties();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// called when this character is being destroyed and the resources should be released
|
||||
public override void Destroy()
|
||||
{
|
||||
base.Destroy();
|
||||
|
||||
DetailLog("{0},BSCharacter.Destroy", LocalID);
|
||||
PhysicsScene.TaintedObject("BSCharacter.destroy", delegate()
|
||||
{
|
||||
PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
|
||||
PhysBody.Clear();
|
||||
PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
|
||||
PhysShape.Clear();
|
||||
});
|
||||
}
|
||||
|
||||
private void SetPhysicalProperties()
|
||||
{
|
||||
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
|
||||
|
||||
ZeroMotion(true);
|
||||
ForcePosition = _position;
|
||||
// Set the velocity and compute the proper friction
|
||||
ForceVelocity = _velocity;
|
||||
// Setting the current and target in the motor will cause it to start computing any deceleration.
|
||||
_velocityMotor.Reset();
|
||||
_velocityMotor.SetCurrent(_velocity);
|
||||
_velocityMotor.SetTarget(_velocity);
|
||||
_velocityMotor.Enabled = false;
|
||||
|
||||
// This will enable or disable the flying buoyancy of the avatar.
|
||||
// Needs to be reset especially when an avatar is recreated after crossing a region boundry.
|
||||
Flying = _flying;
|
||||
|
||||
BulletSimAPI.SetRestitution2(PhysBody.ptr, BSParam.AvatarRestitution);
|
||||
BulletSimAPI.SetMargin2(PhysShape.ptr, PhysicsScene.Params.collisionMargin);
|
||||
BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
|
||||
BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, BSParam.ContactProcessingThreshold);
|
||||
if (BSParam.CcdMotionThreshold > 0f)
|
||||
{
|
||||
BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold);
|
||||
BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius);
|
||||
}
|
||||
|
||||
UpdatePhysicalMassProperties(RawMass, false);
|
||||
|
||||
// Make so capsule does not fall over
|
||||
BulletSimAPI.SetAngularFactorV2(PhysBody.ptr, OMV.Vector3.Zero);
|
||||
|
||||
BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_CHARACTER_OBJECT);
|
||||
|
||||
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr, _position, _orientation);
|
||||
|
||||
// BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG);
|
||||
BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_DEACTIVATION);
|
||||
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
|
||||
|
||||
// Do this after the object has been added to the world
|
||||
PhysBody.collisionType = CollisionType.Avatar;
|
||||
PhysBody.ApplyCollisionMask();
|
||||
}
|
||||
|
||||
public override void RequestPhysicsterseUpdate()
|
||||
{
|
||||
base.RequestPhysicsterseUpdate();
|
||||
}
|
||||
// No one calls this method so I don't know what it could possibly mean
|
||||
public override bool Stopped { get { return false; } }
|
||||
|
||||
public override OMV.Vector3 Size {
|
||||
get
|
||||
{
|
||||
// Avatar capsule size is kept in the scale parameter.
|
||||
return _size;
|
||||
}
|
||||
|
||||
set {
|
||||
// When an avatar's size is set, only the height is changed.
|
||||
_size = value;
|
||||
// Old versions of ScenePresence passed only the height. If width and/or depth are zero,
|
||||
// replace with the default values.
|
||||
if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth;
|
||||
if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth;
|
||||
|
||||
ComputeAvatarScale(_size);
|
||||
ComputeAvatarVolumeAndMass();
|
||||
DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}",
|
||||
LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
|
||||
|
||||
PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
|
||||
{
|
||||
if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape)
|
||||
{
|
||||
BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
|
||||
UpdatePhysicalMassProperties(RawMass, true);
|
||||
// Make sure this change appears as a property update event
|
||||
BulletSimAPI.PushUpdate2(PhysBody.ptr);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public override PrimitiveBaseShape Shape
|
||||
{
|
||||
set { BaseShape = value; }
|
||||
}
|
||||
// I want the physics engine to make an avatar capsule
|
||||
public override BSPhysicsShapeType PreferredPhysicalShape
|
||||
{
|
||||
get {return BSPhysicsShapeType.SHAPE_CAPSULE; }
|
||||
}
|
||||
|
||||
public override bool Grabbed {
|
||||
set { _grabbed = value; }
|
||||
}
|
||||
public override bool Selected {
|
||||
set { _selected = value; }
|
||||
}
|
||||
public override void CrossingFailure() { return; }
|
||||
public override void link(PhysicsActor obj) { return; }
|
||||
public override void delink() { return; }
|
||||
|
||||
// Set motion values to zero.
|
||||
// Do it to the properties so the values get set in the physics engine.
|
||||
// Push the setting of the values to the viewer.
|
||||
// Called at taint time!
|
||||
public override void ZeroMotion(bool inTaintTime)
|
||||
{
|
||||
_velocity = OMV.Vector3.Zero;
|
||||
_acceleration = OMV.Vector3.Zero;
|
||||
_rotationalVelocity = OMV.Vector3.Zero;
|
||||
|
||||
// Zero some other properties directly into the physics engine
|
||||
PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
|
||||
{
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.ClearAllForces2(PhysBody.ptr);
|
||||
});
|
||||
}
|
||||
public override void ZeroAngularMotion(bool inTaintTime)
|
||||
{
|
||||
_rotationalVelocity = OMV.Vector3.Zero;
|
||||
|
||||
PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
|
||||
{
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
{
|
||||
BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
|
||||
BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
|
||||
// The next also get rid of applied linear force but the linear velocity is untouched.
|
||||
BulletSimAPI.ClearForces2(PhysBody.ptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public override void LockAngularMotion(OMV.Vector3 axis) { return; }
|
||||
|
||||
public override OMV.Vector3 RawPosition
|
||||
{
|
||||
get { return _position; }
|
||||
set { _position = value; }
|
||||
}
|
||||
public override OMV.Vector3 Position {
|
||||
get {
|
||||
// Don't refetch the position because this function is called a zillion times
|
||||
// _position = BulletSimAPI.GetObjectPosition2(Scene.World.ptr, LocalID);
|
||||
return _position;
|
||||
}
|
||||
set {
|
||||
_position = value;
|
||||
PositionSanityCheck();
|
||||
|
||||
PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
|
||||
});
|
||||
}
|
||||
}
|
||||
public override OMV.Vector3 ForcePosition {
|
||||
get {
|
||||
_position = BulletSimAPI.GetPosition2(PhysBody.ptr);
|
||||
return _position;
|
||||
}
|
||||
set {
|
||||
_position = value;
|
||||
PositionSanityCheck();
|
||||
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check that the current position is sane and, if not, modify the position to make it so.
|
||||
// Check for being below terrain or on water.
|
||||
// Returns 'true' of the position was made sane by some action.
|
||||
private bool PositionSanityCheck()
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
// TODO: check for out of bounds
|
||||
if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position))
|
||||
{
|
||||
// The character is out of the known/simulated area.
|
||||
// Upper levels of code will handle the transition to other areas so, for
|
||||
// the time, we just ignore the position.
|
||||
return ret;
|
||||
}
|
||||
|
||||
// If below the ground, move the avatar up
|
||||
float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
|
||||
if (Position.Z < terrainHeight)
|
||||
{
|
||||
DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
|
||||
_position.Z = terrainHeight + 2.0f;
|
||||
ret = true;
|
||||
}
|
||||
if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
|
||||
{
|
||||
float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
|
||||
if (Position.Z < waterHeight)
|
||||
{
|
||||
_position.Z = waterHeight;
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// A version of the sanity check that also makes sure a new position value is
|
||||
// pushed back to the physics engine. This routine would be used by anyone
|
||||
// who is not already pushing the value.
|
||||
private bool PositionSanityCheck(bool inTaintTime)
|
||||
{
|
||||
bool ret = false;
|
||||
if (PositionSanityCheck())
|
||||
{
|
||||
// The new position value must be pushed into the physics engine but we can't
|
||||
// just assign to "Position" because of potential call loops.
|
||||
PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
|
||||
});
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override float Mass { get { return _mass; } }
|
||||
|
||||
// used when we only want this prim's mass and not the linkset thing
|
||||
public override float RawMass {
|
||||
get {return _mass; }
|
||||
}
|
||||
public override void UpdatePhysicalMassProperties(float physMass, bool inWorld)
|
||||
{
|
||||
OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass);
|
||||
BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, localInertia);
|
||||
}
|
||||
|
||||
public override OMV.Vector3 Force {
|
||||
get { return _force; }
|
||||
set {
|
||||
_force = value;
|
||||
// m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force);
|
||||
PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public bool TouchingGround()
|
||||
{
|
||||
bool ret = BulletSimAPI.RayCastGround(PhysicsScene.World.ptr,_position,_size.Z * 0.55f, PhysBody.ptr);
|
||||
return ret;
|
||||
}
|
||||
// Avatars don't do vehicles
|
||||
public override int VehicleType { get { return (int)Vehicle.TYPE_NONE; } set { return; } }
|
||||
public override void VehicleFloatParam(int param, float value) { }
|
||||
public override void VehicleVectorParam(int param, OMV.Vector3 value) {}
|
||||
public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { }
|
||||
public override void VehicleFlags(int param, bool remove) { }
|
||||
|
||||
// Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more
|
||||
public override void SetVolumeDetect(int param) { return; }
|
||||
|
||||
public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } }
|
||||
public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } }
|
||||
|
||||
// Sets the target in the motor. This starts the changing of the avatar's velocity.
|
||||
public override OMV.Vector3 TargetVelocity
|
||||
{
|
||||
get
|
||||
{
|
||||
return _velocityMotor.TargetValue;
|
||||
}
|
||||
set
|
||||
{
|
||||
DetailLog("{0},BSCharacter.setTargetVelocity,call,vel={1}", LocalID, value);
|
||||
|
||||
if (!_flying)
|
||||
if ((value.Z >= 0.0001f) || (value.Z <= -0.0001f) || _velocity.Z < -0.0001f)
|
||||
if (!TouchingGround())
|
||||
value.Z = _velocity.Z;
|
||||
if (_setAlwaysRun)
|
||||
value *= 1.3f;
|
||||
|
||||
OMV.Vector3 targetVel = value;
|
||||
|
||||
PhysicsScene.TaintedObject("BSCharacter.setTargetVelocity", delegate()
|
||||
{
|
||||
|
||||
_velocityMotor.Reset();
|
||||
_velocityMotor.SetTarget(targetVel);
|
||||
_velocityMotor.SetCurrent(_velocity);
|
||||
_velocityMotor.Enabled = true;
|
||||
|
||||
// Make sure a property update happens next step so the motor gets incorporated.
|
||||
BulletSimAPI.PushUpdate2(PhysBody.ptr);
|
||||
});
|
||||
}
|
||||
}
|
||||
// Directly setting velocity means this is what the user really wants now.
|
||||
public override OMV.Vector3 Velocity {
|
||||
get { return _velocity; }
|
||||
set {
|
||||
_velocity = value;
|
||||
// m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity);
|
||||
PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate()
|
||||
{
|
||||
_velocityMotor.Reset();
|
||||
_velocityMotor.SetCurrent(_velocity);
|
||||
_velocityMotor.SetTarget(_velocity);
|
||||
// Even though the motor is initialized, it's not used and the velocity goes straight into the avatar.
|
||||
_velocityMotor.Enabled = false;
|
||||
|
||||
DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity);
|
||||
ForceVelocity = _velocity;
|
||||
});
|
||||
}
|
||||
}
|
||||
public override OMV.Vector3 ForceVelocity {
|
||||
get { return _velocity; }
|
||||
set {
|
||||
PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity");
|
||||
|
||||
_velocity = value;
|
||||
// Depending on whether the avatar is moving or not, change the friction
|
||||
// to keep the avatar from slipping around
|
||||
if (_velocity.Length() == 0)
|
||||
{
|
||||
if (_currentFriction != BSParam.AvatarStandingFriction)
|
||||
{
|
||||
_currentFriction = BSParam.AvatarStandingFriction;
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_currentFriction != BSParam.AvatarFriction)
|
||||
{
|
||||
_currentFriction = BSParam.AvatarFriction;
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
|
||||
}
|
||||
}
|
||||
// Remember the set velocity so we can suppress the reduction by friction, ...
|
||||
_appliedVelocity = value;
|
||||
|
||||
BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
|
||||
BulletSimAPI.Activate2(PhysBody.ptr, true);
|
||||
}
|
||||
}
|
||||
public override OMV.Vector3 Torque {
|
||||
get { return _torque; }
|
||||
set { _torque = value;
|
||||
}
|
||||
}
|
||||
public override float CollisionScore {
|
||||
get { return _collisionScore; }
|
||||
set { _collisionScore = value;
|
||||
}
|
||||
}
|
||||
public override OMV.Vector3 Acceleration {
|
||||
get { return _acceleration; }
|
||||
set { _acceleration = value; }
|
||||
}
|
||||
public override OMV.Quaternion RawOrientation
|
||||
{
|
||||
get { return _orientation; }
|
||||
set { _orientation = value; }
|
||||
}
|
||||
public override OMV.Quaternion Orientation {
|
||||
get { return _orientation; }
|
||||
set {
|
||||
_orientation = value;
|
||||
// m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation);
|
||||
PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
|
||||
{
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
{
|
||||
// _position = BulletSimAPI.GetPosition2(BSBody.ptr);
|
||||
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// Go directly to Bullet to get/set the value.
|
||||
public override OMV.Quaternion ForceOrientation
|
||||
{
|
||||
get
|
||||
{
|
||||
_orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr);
|
||||
return _orientation;
|
||||
}
|
||||
set
|
||||
{
|
||||
_orientation = value;
|
||||
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
|
||||
}
|
||||
}
|
||||
public override int PhysicsActorType {
|
||||
get { return _physicsActorType; }
|
||||
set { _physicsActorType = value;
|
||||
}
|
||||
}
|
||||
public override bool IsPhysical {
|
||||
get { return _isPhysical; }
|
||||
set { _isPhysical = value;
|
||||
}
|
||||
}
|
||||
public override bool IsSolid {
|
||||
get { return true; }
|
||||
}
|
||||
public override bool IsStatic {
|
||||
get { return false; }
|
||||
}
|
||||
public override bool Flying {
|
||||
get { return _flying; }
|
||||
set {
|
||||
_flying = value;
|
||||
|
||||
// simulate flying by changing the effect of gravity
|
||||
Buoyancy = ComputeBuoyancyFromFlying(_flying);
|
||||
}
|
||||
}
|
||||
// Flying is implimented by changing the avatar's buoyancy.
|
||||
// Would this be done better with a vehicle type?
|
||||
private float ComputeBuoyancyFromFlying(bool ifFlying) {
|
||||
return ifFlying ? 1f : 0f;
|
||||
}
|
||||
public override bool
|
||||
SetAlwaysRun {
|
||||
get { return _setAlwaysRun; }
|
||||
set { _setAlwaysRun = value; }
|
||||
}
|
||||
public override bool ThrottleUpdates {
|
||||
get { return _throttleUpdates; }
|
||||
set { _throttleUpdates = value; }
|
||||
}
|
||||
public override bool IsColliding {
|
||||
get { return (CollidingStep == PhysicsScene.SimulationStep); }
|
||||
set { _isColliding = value; }
|
||||
}
|
||||
public override bool CollidingGround {
|
||||
get { return (CollidingGroundStep == PhysicsScene.SimulationStep); }
|
||||
set { CollidingGround = value; }
|
||||
}
|
||||
public override bool CollidingObj {
|
||||
get { return _collidingObj; }
|
||||
set { _collidingObj = value; }
|
||||
}
|
||||
public override bool FloatOnWater {
|
||||
set {
|
||||
_floatOnWater = value;
|
||||
PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate()
|
||||
{
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
{
|
||||
if (_floatOnWater)
|
||||
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
|
||||
else
|
||||
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
public override OMV.Vector3 RotationalVelocity {
|
||||
get { return _rotationalVelocity; }
|
||||
set { _rotationalVelocity = value; }
|
||||
}
|
||||
public override OMV.Vector3 ForceRotationalVelocity {
|
||||
get { return _rotationalVelocity; }
|
||||
set { _rotationalVelocity = value; }
|
||||
}
|
||||
public override bool Kinematic {
|
||||
get { return _kinematic; }
|
||||
set { _kinematic = value; }
|
||||
}
|
||||
// neg=fall quickly, 0=1g, 1=0g, pos=float up
|
||||
public override float Buoyancy {
|
||||
get { return _buoyancy; }
|
||||
set { _buoyancy = value;
|
||||
PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
|
||||
ForceBuoyancy = _buoyancy;
|
||||
});
|
||||
}
|
||||
}
|
||||
public override float ForceBuoyancy {
|
||||
get { return _buoyancy; }
|
||||
set {
|
||||
PhysicsScene.AssertInTaintTime("BSCharacter.ForceBuoyancy");
|
||||
|
||||
_buoyancy = value;
|
||||
DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
|
||||
// Buoyancy is faked by changing the gravity applied to the object
|
||||
float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
|
||||
}
|
||||
}
|
||||
|
||||
// Used for MoveTo
|
||||
public override OMV.Vector3 PIDTarget {
|
||||
set { _PIDTarget = value; }
|
||||
}
|
||||
public override bool PIDActive {
|
||||
set { _usePID = value; }
|
||||
}
|
||||
public override float PIDTau {
|
||||
set { _PIDTau = value; }
|
||||
}
|
||||
|
||||
// Used for llSetHoverHeight and maybe vehicle height
|
||||
// Hover Height will override MoveTo target's Z
|
||||
public override bool PIDHoverActive {
|
||||
set { _useHoverPID = value; }
|
||||
}
|
||||
public override float PIDHoverHeight {
|
||||
set { _PIDHoverHeight = value; }
|
||||
}
|
||||
public override PIDHoverType PIDHoverType {
|
||||
set { _PIDHoverType = value; }
|
||||
}
|
||||
public override float PIDHoverTau {
|
||||
set { _PIDHoverTao = value; }
|
||||
}
|
||||
|
||||
// For RotLookAt
|
||||
public override OMV.Quaternion APIDTarget { set { return; } }
|
||||
public override bool APIDActive { set { return; } }
|
||||
public override float APIDStrength { set { return; } }
|
||||
public override float APIDDamping { set { return; } }
|
||||
|
||||
public override void AddForce(OMV.Vector3 force, bool pushforce) {
|
||||
if (force.IsFinite())
|
||||
{
|
||||
_force.X += force.X;
|
||||
_force.Y += force.Y;
|
||||
_force.Z += force.Z;
|
||||
// m_log.DebugFormat("{0}: AddForce. adding={1}, newForce={2}", LogHeader, force, _force);
|
||||
PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force);
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.ErrorFormat("{0}: Got a NaN force applied to a Character", LogHeader);
|
||||
}
|
||||
//m_lastUpdateSent = false;
|
||||
}
|
||||
|
||||
public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
|
||||
}
|
||||
public override void SetMomentum(OMV.Vector3 momentum) {
|
||||
}
|
||||
|
||||
private void ComputeAvatarScale(OMV.Vector3 size)
|
||||
{
|
||||
OMV.Vector3 newScale = size;
|
||||
// newScale.X = PhysicsScene.Params.avatarCapsuleWidth;
|
||||
// newScale.Y = PhysicsScene.Params.avatarCapsuleDepth;
|
||||
|
||||
// From the total height, remove the capsule half spheres that are at each end
|
||||
// The 1.15f came from ODE. Not sure what this factors in.
|
||||
// newScale.Z = (size.Z * 1.15f) - (newScale.X + newScale.Y);
|
||||
|
||||
// The total scale height is the central cylindar plus the caps on the two ends.
|
||||
newScale.Z = size.Z + (Math.Min(size.X, size.Y) * 2f);
|
||||
|
||||
// Convert diameters to radii and height to half height -- the way Bullet expects it.
|
||||
Scale = newScale / 2f;
|
||||
}
|
||||
|
||||
// set _avatarVolume and _mass based on capsule size, _density and Scale
|
||||
private void ComputeAvatarVolumeAndMass()
|
||||
{
|
||||
_avatarVolume = (float)(
|
||||
Math.PI
|
||||
* Scale.X
|
||||
* Scale.Y // the area of capsule cylinder
|
||||
* Scale.Z // times height of capsule cylinder
|
||||
+ 1.33333333f
|
||||
* Math.PI
|
||||
* Scale.X
|
||||
* Math.Min(Scale.X, Scale.Y)
|
||||
* Scale.Y // plus the volume of the capsule end caps
|
||||
);
|
||||
_mass = _avatarDensity * _avatarVolume;
|
||||
}
|
||||
|
||||
// The physics engine says that properties have updated. Update same and inform
|
||||
// the world that things have changed.
|
||||
public override void UpdateProperties(EntityProperties entprop)
|
||||
{
|
||||
_position = entprop.Position;
|
||||
_orientation = entprop.Rotation;
|
||||
_velocity = entprop.Velocity;
|
||||
_acceleration = entprop.Acceleration;
|
||||
_rotationalVelocity = entprop.RotationalVelocity;
|
||||
|
||||
// Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
|
||||
PositionSanityCheck(true);
|
||||
|
||||
if (_velocityMotor.Enabled)
|
||||
{
|
||||
// TODO: Decide if the step parameters should be changed depending on the avatar's
|
||||
// state (flying, colliding, ...).
|
||||
|
||||
OMV.Vector3 stepVelocity = _velocityMotor.Step(PhysicsScene.LastTimeStep);
|
||||
|
||||
// If falling, we keep the world's downward vector no matter what the other axis specify.
|
||||
if (!Flying && !IsColliding)
|
||||
{
|
||||
stepVelocity.Z = entprop.Velocity.Z;
|
||||
DetailLog("{0},BSCharacter.UpdateProperties,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity);
|
||||
}
|
||||
|
||||
// If the user has said stop and we've stopped applying velocity correction,
|
||||
// the motor can be turned off. Set the velocity to zero so the zero motion is sent to the viewer.
|
||||
if (_velocityMotor.TargetValue.ApproxEquals(OMV.Vector3.Zero, 0.01f) && _velocityMotor.ErrorIsZero)
|
||||
{
|
||||
ZeroMotion(true);
|
||||
stepVelocity = OMV.Vector3.Zero;
|
||||
_velocityMotor.Enabled = false;
|
||||
DetailLog("{0},BSCharacter.UpdateProperties,taint,disableVelocityMotor,m={1}", LocalID, _velocityMotor);
|
||||
}
|
||||
|
||||
_velocity = stepVelocity;
|
||||
entprop.Velocity = _velocity;
|
||||
BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
|
||||
}
|
||||
|
||||
// remember the current and last set values
|
||||
LastEntityProperties = CurrentEntityProperties;
|
||||
CurrentEntityProperties = entprop;
|
||||
|
||||
// Tell the linkset about value changes
|
||||
Linkset.UpdateProperties(this, true);
|
||||
|
||||
// Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
|
||||
// base.RequestPhysicsterseUpdate();
|
||||
|
||||
DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
|
||||
LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
public abstract class BSConstraint : IDisposable
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM CONSTRAINT]";
|
||||
|
||||
protected BulletWorld m_world;
|
||||
protected BulletBody m_body1;
|
||||
protected BulletBody m_body2;
|
||||
protected BulletConstraint m_constraint;
|
||||
protected bool m_enabled = false;
|
||||
|
||||
public BulletBody Body1 { get { return m_body1; } }
|
||||
public BulletBody Body2 { get { return m_body2; } }
|
||||
public BulletConstraint Constraint { get { return m_constraint; } }
|
||||
public abstract ConstraintType Type { get; }
|
||||
public bool IsEnabled { get { return m_enabled; } }
|
||||
|
||||
public BSConstraint()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (m_enabled)
|
||||
{
|
||||
m_enabled = false;
|
||||
if (m_constraint.HasPhysicalConstraint)
|
||||
{
|
||||
bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr);
|
||||
m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}",
|
||||
BSScene.DetailLogZero,
|
||||
m_body1.ID, m_body1.ptr.ToString(),
|
||||
m_body2.ID, m_body2.ptr.ToString(),
|
||||
success);
|
||||
m_constraint.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool SetLinearLimits(Vector3 low, Vector3 high)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
ret = BulletSimAPI.SetLinearLimits2(m_constraint.ptr, low, high);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public virtual bool SetAngularLimits(Vector3 low, Vector3 high)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
ret = BulletSimAPI.SetAngularLimits2(m_constraint.ptr, low, high);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public virtual bool SetSolverIterations(float cnt)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
{
|
||||
BulletSimAPI.SetConstraintNumSolverIterations2(m_constraint.ptr, cnt);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public virtual bool CalculateTransforms()
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
{
|
||||
// Recompute the internal transforms
|
||||
BulletSimAPI.CalculateTransforms2(m_constraint.ptr);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Reset this constraint making sure it has all its internal structures
|
||||
// recomputed and is enabled and ready to go.
|
||||
public virtual bool RecomputeConstraintVariables(float mass)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
{
|
||||
ret = CalculateTransforms();
|
||||
if (ret)
|
||||
{
|
||||
// Setting an object's mass to zero (making it static like when it's selected)
|
||||
// automatically disables the constraints.
|
||||
// If the link is enabled, be sure to set the constraint itself to enabled.
|
||||
BulletSimAPI.SetConstraintEnable2(m_constraint.ptr, BSParam.NumericBool(true));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_world.physicsScene.Logger.ErrorFormat("{0} CalculateTransforms failed. A={1}, B={2}", LogHeader, Body1.ID, Body2.ID);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
public sealed class BSConstraint6Dof : BSConstraint
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM 6DOF CONSTRAINT]";
|
||||
|
||||
public override ConstraintType Type { get { return ConstraintType.D6_CONSTRAINT_TYPE; } }
|
||||
|
||||
// Create a btGeneric6DofConstraint
|
||||
public BSConstraint6Dof(BulletWorld world, BulletBody obj1, BulletBody obj2,
|
||||
Vector3 frame1, Quaternion frame1rot,
|
||||
Vector3 frame2, Quaternion frame2rot,
|
||||
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
|
||||
{
|
||||
m_world = world;
|
||||
m_body1 = obj1;
|
||||
m_body2 = obj2;
|
||||
m_constraint = new BulletConstraint(
|
||||
BulletSimAPI.Create6DofConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
|
||||
frame1, frame1rot,
|
||||
frame2, frame2rot,
|
||||
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
|
||||
m_enabled = true;
|
||||
world.physicsScene.DetailLog("{0},BS6DofConstraint,createFrame,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
|
||||
BSScene.DetailLogZero, world.worldID,
|
||||
obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
|
||||
}
|
||||
|
||||
public BSConstraint6Dof(BulletWorld world, BulletBody obj1, BulletBody obj2,
|
||||
Vector3 joinPoint,
|
||||
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
|
||||
{
|
||||
m_world = world;
|
||||
m_body1 = obj1;
|
||||
m_body2 = obj2;
|
||||
if (!obj1.HasPhysicalBody || !obj2.HasPhysicalBody)
|
||||
{
|
||||
world.physicsScene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
|
||||
BSScene.DetailLogZero, world.worldID,
|
||||
obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
|
||||
world.physicsScene.Logger.ErrorFormat("{0} Attempt to build 6DOF constraint with missing bodies: wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
|
||||
LogHeader, world.worldID, obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
|
||||
m_enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_constraint = new BulletConstraint(
|
||||
BulletSimAPI.Create6DofConstraintToPoint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
|
||||
joinPoint,
|
||||
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
|
||||
world.physicsScene.DetailLog("{0},BS6DofConstraint,createMidPoint,wID={1}, csrt={2}, rID={3}, rBody={4}, cID={5}, cBody={6}",
|
||||
BSScene.DetailLogZero, world.worldID, m_constraint.ptr.ToString(),
|
||||
obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
|
||||
if (!m_constraint.HasPhysicalConstraint)
|
||||
{
|
||||
world.physicsScene.Logger.ErrorFormat("{0} Failed creation of 6Dof constraint. rootID={1}, childID={2}",
|
||||
LogHeader, obj1.ID, obj2.ID);
|
||||
m_enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetFrames(Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
{
|
||||
BulletSimAPI.SetFrames2(m_constraint.ptr, frameA, frameArot, frameB, frameBrot);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool SetCFMAndERP(float cfm, float erp)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
{
|
||||
BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
|
||||
BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL);
|
||||
BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool UseFrameOffset(bool useOffset)
|
||||
{
|
||||
bool ret = false;
|
||||
float onOff = useOffset ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
|
||||
if (m_enabled)
|
||||
ret = BulletSimAPI.UseFrameOffset2(m_constraint.ptr, onOff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool TranslationalLimitMotor(bool enable, float targetVelocity, float maxMotorForce)
|
||||
{
|
||||
bool ret = false;
|
||||
float onOff = enable ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
|
||||
if (m_enabled)
|
||||
{
|
||||
ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.ptr, onOff, targetVelocity, maxMotorForce);
|
||||
m_world.physicsScene.DetailLog("{0},BS6DOFConstraint,TransLimitMotor,enable={1},vel={2},maxForce={3}",
|
||||
BSScene.DetailLogZero, enable, targetVelocity, maxMotorForce);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool SetBreakingImpulseThreshold(float threshold)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
ret = BulletSimAPI.SetBreakingImpulseThreshold2(m_constraint.ptr, threshold);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,180 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using log4net;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
public sealed class BSConstraintCollection : IDisposable
|
||||
{
|
||||
// private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
// private static readonly string LogHeader = "[CONSTRAINT COLLECTION]";
|
||||
|
||||
delegate bool ConstraintAction(BSConstraint constrain);
|
||||
|
||||
private List<BSConstraint> m_constraints;
|
||||
private BulletWorld m_world;
|
||||
|
||||
public BSConstraintCollection(BulletWorld world)
|
||||
{
|
||||
m_world = world;
|
||||
m_constraints = new List<BSConstraint>();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.Clear();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (m_constraints)
|
||||
{
|
||||
foreach (BSConstraint cons in m_constraints)
|
||||
{
|
||||
cons.Dispose();
|
||||
}
|
||||
m_constraints.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public bool AddConstraint(BSConstraint cons)
|
||||
{
|
||||
lock (m_constraints)
|
||||
{
|
||||
// There is only one constraint between any bodies. Remove any old just to make sure.
|
||||
RemoveAndDestroyConstraint(cons.Body1, cons.Body2);
|
||||
|
||||
m_constraints.Add(cons);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the constraint between two bodies. There can be only one.
|
||||
// Return 'true' if a constraint was found.
|
||||
public bool TryGetConstraint(BulletBody body1, BulletBody body2, out BSConstraint returnConstraint)
|
||||
{
|
||||
bool found = false;
|
||||
BSConstraint foundConstraint = null;
|
||||
|
||||
uint lookingID1 = body1.ID;
|
||||
uint lookingID2 = body2.ID;
|
||||
lock (m_constraints)
|
||||
{
|
||||
foreach (BSConstraint constrain in m_constraints)
|
||||
{
|
||||
if ((constrain.Body1.ID == lookingID1 && constrain.Body2.ID == lookingID2)
|
||||
|| (constrain.Body1.ID == lookingID2 && constrain.Body2.ID == lookingID1))
|
||||
{
|
||||
foundConstraint = constrain;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
returnConstraint = foundConstraint;
|
||||
return found;
|
||||
}
|
||||
|
||||
// Remove any constraint between the passed bodies.
|
||||
// Presumed there is only one such constraint possible.
|
||||
// Return 'true' if a constraint was found and destroyed.
|
||||
public bool RemoveAndDestroyConstraint(BulletBody body1, BulletBody body2)
|
||||
{
|
||||
bool ret = false;
|
||||
lock (m_constraints)
|
||||
{
|
||||
BSConstraint constrain;
|
||||
if (this.TryGetConstraint(body1, body2, out constrain))
|
||||
{
|
||||
// remove the constraint from our collection
|
||||
RemoveAndDestroyConstraint(constrain);
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The constraint MUST exist in the collection
|
||||
public bool RemoveAndDestroyConstraint(BSConstraint constrain)
|
||||
{
|
||||
lock (m_constraints)
|
||||
{
|
||||
// remove the constraint from our collection
|
||||
m_constraints.Remove(constrain);
|
||||
}
|
||||
// tell the engine that all its structures need to be freed
|
||||
constrain.Dispose();
|
||||
// we destroyed something
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove all constraints that reference the passed body.
|
||||
// Return 'true' if any constraints were destroyed.
|
||||
public bool RemoveAndDestroyConstraint(BulletBody body1)
|
||||
{
|
||||
List<BSConstraint> toRemove = new List<BSConstraint>();
|
||||
uint lookingID = body1.ID;
|
||||
lock (m_constraints)
|
||||
{
|
||||
foreach (BSConstraint constrain in m_constraints)
|
||||
{
|
||||
if (constrain.Body1.ID == lookingID || constrain.Body2.ID == lookingID)
|
||||
{
|
||||
toRemove.Add(constrain);
|
||||
}
|
||||
}
|
||||
foreach (BSConstraint constrain in toRemove)
|
||||
{
|
||||
m_constraints.Remove(constrain);
|
||||
constrain.Dispose();
|
||||
}
|
||||
}
|
||||
return (toRemove.Count > 0);
|
||||
}
|
||||
|
||||
public bool RecalculateAllConstraints()
|
||||
{
|
||||
bool ret = false;
|
||||
lock (m_constraints)
|
||||
{
|
||||
foreach (BSConstraint constrain in m_constraints)
|
||||
{
|
||||
constrain.CalculateTransforms();
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
public sealed class BSConstraintHinge : BSConstraint
|
||||
{
|
||||
public override ConstraintType Type { get { return ConstraintType.HINGE_CONSTRAINT_TYPE; } }
|
||||
|
||||
public BSConstraintHinge(BulletWorld world, BulletBody obj1, BulletBody obj2,
|
||||
Vector3 pivotInA, Vector3 pivotInB,
|
||||
Vector3 axisInA, Vector3 axisInB,
|
||||
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
|
||||
{
|
||||
m_world = world;
|
||||
m_body1 = obj1;
|
||||
m_body2 = obj2;
|
||||
m_constraint = new BulletConstraint(
|
||||
BulletSimAPI.CreateHingeConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
|
||||
pivotInA, pivotInB,
|
||||
axisInA, axisInB,
|
||||
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
|
||||
m_enabled = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,329 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using OMV = OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
// A BSPrim can get individual information about its linkedness attached
|
||||
// to it through an instance of a subclass of LinksetInfo.
|
||||
// Each type of linkset will define the information needed for its type.
|
||||
public abstract class BSLinksetInfo
|
||||
{
|
||||
public virtual void Clear() { }
|
||||
}
|
||||
|
||||
public abstract class BSLinkset
|
||||
{
|
||||
// private static string LogHeader = "[BULLETSIM LINKSET]";
|
||||
|
||||
public enum LinksetImplementation
|
||||
{
|
||||
Constraint = 0, // linkset tied together with constraints
|
||||
Compound = 1, // linkset tied together as a compound object
|
||||
Manual = 2 // linkset tied together manually (code moves all the pieces)
|
||||
}
|
||||
// Create the correct type of linkset for this child
|
||||
public static BSLinkset Factory(BSScene physScene, BSPhysObject parent)
|
||||
{
|
||||
BSLinkset ret = null;
|
||||
|
||||
switch ((int)BSParam.LinksetImplementation)
|
||||
{
|
||||
case (int)LinksetImplementation.Constraint:
|
||||
ret = new BSLinksetConstraints(physScene, parent);
|
||||
break;
|
||||
case (int)LinksetImplementation.Compound:
|
||||
ret = new BSLinksetCompound(physScene, parent);
|
||||
break;
|
||||
case (int)LinksetImplementation.Manual:
|
||||
// ret = new BSLinksetManual(physScene, parent);
|
||||
break;
|
||||
default:
|
||||
ret = new BSLinksetCompound(physScene, parent);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public BSPhysObject LinksetRoot { get; protected set; }
|
||||
|
||||
public BSScene PhysicsScene { get; private set; }
|
||||
|
||||
static int m_nextLinksetID = 1;
|
||||
public int LinksetID { get; private set; }
|
||||
|
||||
// The children under the root in this linkset.
|
||||
protected HashSet<BSPhysObject> m_children;
|
||||
|
||||
// We lock the diddling of linkset classes to prevent any badness.
|
||||
// This locks the modification of the instances of this class. Changes
|
||||
// to the physical representation is done via the tainting mechenism.
|
||||
protected object m_linksetActivityLock = new Object();
|
||||
|
||||
// Some linksets have a preferred physical shape.
|
||||
// Returns SHAPE_UNKNOWN if there is no preference. Causes the correct shape to be selected.
|
||||
public virtual BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
|
||||
{
|
||||
return BSPhysicsShapeType.SHAPE_UNKNOWN;
|
||||
}
|
||||
|
||||
// We keep the prim's mass in the linkset structure since it could be dependent on other prims
|
||||
public float LinksetMass { get; protected set; }
|
||||
|
||||
public virtual bool LinksetIsColliding { get { return false; } }
|
||||
|
||||
public OMV.Vector3 CenterOfMass
|
||||
{
|
||||
get { return ComputeLinksetCenterOfMass(); }
|
||||
}
|
||||
|
||||
public OMV.Vector3 GeometricCenter
|
||||
{
|
||||
get { return ComputeLinksetGeometricCenter(); }
|
||||
}
|
||||
|
||||
protected BSLinkset(BSScene scene, BSPhysObject parent)
|
||||
{
|
||||
// A simple linkset of one (no children)
|
||||
LinksetID = m_nextLinksetID++;
|
||||
// We create LOTS of linksets.
|
||||
if (m_nextLinksetID <= 0)
|
||||
m_nextLinksetID = 1;
|
||||
PhysicsScene = scene;
|
||||
LinksetRoot = parent;
|
||||
m_children = new HashSet<BSPhysObject>();
|
||||
LinksetMass = parent.RawMass;
|
||||
Rebuilding = false;
|
||||
}
|
||||
|
||||
// Link to a linkset where the child knows the parent.
|
||||
// Parent changing should not happen so do some sanity checking.
|
||||
// We return the parent's linkset so the child can track its membership.
|
||||
// Called at runtime.
|
||||
public BSLinkset AddMeToLinkset(BSPhysObject child)
|
||||
{
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
// Don't add the root to its own linkset
|
||||
if (!IsRoot(child))
|
||||
AddChildToLinkset(child);
|
||||
LinksetMass = ComputeLinksetMass();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// Remove a child from a linkset.
|
||||
// Returns a new linkset for the child which is a linkset of one (just the
|
||||
// orphened child).
|
||||
// Called at runtime.
|
||||
public BSLinkset RemoveMeFromLinkset(BSPhysObject child)
|
||||
{
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
if (IsRoot(child))
|
||||
{
|
||||
// Cannot remove the root from a linkset.
|
||||
return this;
|
||||
}
|
||||
RemoveChildFromLinkset(child);
|
||||
LinksetMass = ComputeLinksetMass();
|
||||
}
|
||||
|
||||
// The child is down to a linkset of just itself
|
||||
return BSLinkset.Factory(PhysicsScene, child);
|
||||
}
|
||||
|
||||
// Return 'true' if the passed object is the root object of this linkset
|
||||
public bool IsRoot(BSPhysObject requestor)
|
||||
{
|
||||
return (requestor.LocalID == LinksetRoot.LocalID);
|
||||
}
|
||||
|
||||
public int NumberOfChildren { get { return m_children.Count; } }
|
||||
|
||||
// Return 'true' if this linkset has any children (more than the root member)
|
||||
public bool HasAnyChildren { get { return (m_children.Count > 0); } }
|
||||
|
||||
// Return 'true' if this child is in this linkset
|
||||
public bool HasChild(BSPhysObject child)
|
||||
{
|
||||
bool ret = false;
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
ret = m_children.Contains(child);
|
||||
/* Safer version but the above should work
|
||||
foreach (BSPhysObject bp in m_children)
|
||||
{
|
||||
if (child.LocalID == bp.LocalID)
|
||||
{
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Perform an action on each member of the linkset including root prim.
|
||||
// Depends on the action on whether this should be done at taint time.
|
||||
public delegate bool ForEachMemberAction(BSPhysObject obj);
|
||||
public virtual bool ForEachMember(ForEachMemberAction action)
|
||||
{
|
||||
bool ret = false;
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
action(LinksetRoot);
|
||||
foreach (BSPhysObject po in m_children)
|
||||
{
|
||||
if (action(po))
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// I am the root of a linkset and a new child is being added
|
||||
// Called while LinkActivity is locked.
|
||||
protected abstract void AddChildToLinkset(BSPhysObject child);
|
||||
|
||||
// I am the root of a linkset and one of my children is being removed.
|
||||
// Safe to call even if the child is not really in my linkset.
|
||||
protected abstract void RemoveChildFromLinkset(BSPhysObject child);
|
||||
|
||||
// When physical properties are changed the linkset needs to recalculate
|
||||
// its internal properties.
|
||||
// May be called at runtime or taint-time.
|
||||
public virtual void Refresh(BSPhysObject requestor)
|
||||
{
|
||||
LinksetMass = ComputeLinksetMass();
|
||||
}
|
||||
|
||||
// Flag denoting the linkset is in the process of being rebuilt.
|
||||
// Used to know not the schedule a rebuild in the middle of a rebuild.
|
||||
protected bool Rebuilding { get; set; }
|
||||
|
||||
// The object is going dynamic (physical). Do any setup necessary
|
||||
// for a dynamic linkset.
|
||||
// Only the state of the passed object can be modified. The rest of the linkset
|
||||
// has not yet been fully constructed.
|
||||
// Return 'true' if any properties updated on the passed object.
|
||||
// Called at taint-time!
|
||||
public abstract bool MakeDynamic(BSPhysObject child);
|
||||
|
||||
// The object is going static (non-physical). Do any setup necessary
|
||||
// for a static linkset.
|
||||
// Return 'true' if any properties updated on the passed object.
|
||||
// Called at taint-time!
|
||||
public abstract bool MakeStatic(BSPhysObject child);
|
||||
|
||||
// Called when a parameter update comes from the physics engine for any object
|
||||
// of the linkset is received.
|
||||
// Passed flag is update came from physics engine (true) or the user (false).
|
||||
// Called at taint-time!!
|
||||
public abstract void UpdateProperties(BSPhysObject physObject, bool physicalUpdate);
|
||||
|
||||
// Routine used when rebuilding the body of the root of the linkset
|
||||
// Destroy all the constraints have have been made to root.
|
||||
// This is called when the root body is changing.
|
||||
// Returns 'true' of something was actually removed and would need restoring
|
||||
// Called at taint-time!!
|
||||
public abstract bool RemoveBodyDependencies(BSPrim child);
|
||||
|
||||
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
|
||||
// this routine will restore the removed constraints.
|
||||
// Called at taint-time!!
|
||||
public abstract void RestoreBodyDependencies(BSPrim child);
|
||||
|
||||
// ================================================================
|
||||
protected virtual float ComputeLinksetMass()
|
||||
{
|
||||
float mass = LinksetRoot.RawMass;
|
||||
if (HasAnyChildren)
|
||||
{
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
foreach (BSPhysObject bp in m_children)
|
||||
{
|
||||
mass += bp.RawMass;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mass;
|
||||
}
|
||||
|
||||
protected virtual OMV.Vector3 ComputeLinksetCenterOfMass()
|
||||
{
|
||||
OMV.Vector3 com;
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
com = LinksetRoot.Position * LinksetRoot.RawMass;
|
||||
float totalMass = LinksetRoot.RawMass;
|
||||
|
||||
foreach (BSPhysObject bp in m_children)
|
||||
{
|
||||
com += bp.Position * bp.RawMass;
|
||||
totalMass += bp.RawMass;
|
||||
}
|
||||
if (totalMass != 0f)
|
||||
com /= totalMass;
|
||||
}
|
||||
|
||||
return com;
|
||||
}
|
||||
|
||||
protected virtual OMV.Vector3 ComputeLinksetGeometricCenter()
|
||||
{
|
||||
OMV.Vector3 com;
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
com = LinksetRoot.Position;
|
||||
|
||||
foreach (BSPhysObject bp in m_children)
|
||||
{
|
||||
com += bp.Position * bp.RawMass;
|
||||
}
|
||||
com /= (m_children.Count + 1);
|
||||
}
|
||||
|
||||
return com;
|
||||
}
|
||||
|
||||
// Invoke the detailed logger and output something if it's enabled.
|
||||
protected void DetailLog(string msg, params Object[] args)
|
||||
{
|
||||
if (PhysicsScene.PhysicsLogging.Enabled)
|
||||
PhysicsScene.DetailLog(msg, args);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,397 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using OpenSim.Framework;
|
||||
|
||||
using OMV = OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
// When a child is linked, the relationship position of the child to the parent
|
||||
// is remembered so the child's world position can be recomputed when it is
|
||||
// removed from the linkset.
|
||||
sealed class BSLinksetCompoundInfo : BSLinksetInfo
|
||||
{
|
||||
public OMV.Vector3 OffsetPos;
|
||||
public OMV.Quaternion OffsetRot;
|
||||
public BSLinksetCompoundInfo(OMV.Vector3 p, OMV.Quaternion r)
|
||||
{
|
||||
OffsetPos = p;
|
||||
OffsetRot = r;
|
||||
}
|
||||
public override void Clear()
|
||||
{
|
||||
OffsetPos = OMV.Vector3.Zero;
|
||||
OffsetRot = OMV.Quaternion.Identity;
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder buff = new StringBuilder();
|
||||
buff.Append("<p=");
|
||||
buff.Append(OffsetPos.ToString());
|
||||
buff.Append(",r=");
|
||||
buff.Append(OffsetRot.ToString());
|
||||
buff.Append(">");
|
||||
return buff.ToString();
|
||||
}
|
||||
};
|
||||
|
||||
public sealed class BSLinksetCompound : BSLinkset
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]";
|
||||
|
||||
public BSLinksetCompound(BSScene scene, BSPhysObject parent) : base(scene, parent)
|
||||
{
|
||||
}
|
||||
|
||||
// For compound implimented linksets, if there are children, use compound shape for the root.
|
||||
public override BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
|
||||
{
|
||||
// Returning 'unknown' means we don't have a preference.
|
||||
BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN;
|
||||
if (IsRoot(requestor) && HasAnyChildren)
|
||||
{
|
||||
ret = BSPhysicsShapeType.SHAPE_COMPOUND;
|
||||
}
|
||||
// DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// When physical properties are changed the linkset needs to recalculate
|
||||
// its internal properties.
|
||||
public override void Refresh(BSPhysObject requestor)
|
||||
{
|
||||
base.Refresh(requestor);
|
||||
|
||||
// Something changed so do the rebuilding thing
|
||||
// ScheduleRebuild();
|
||||
}
|
||||
|
||||
// Schedule a refresh to happen after all the other taint processing.
|
||||
private void ScheduleRebuild(BSPhysObject requestor)
|
||||
{
|
||||
DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1}",
|
||||
requestor.LocalID, Rebuilding);
|
||||
// When rebuilding, it is possible to set properties that would normally require a rebuild.
|
||||
// If already rebuilding, don't request another rebuild.
|
||||
if (!Rebuilding)
|
||||
{
|
||||
PhysicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate()
|
||||
{
|
||||
if (HasAnyChildren)
|
||||
RecomputeLinksetCompound();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// The object is going dynamic (physical). Do any setup necessary
|
||||
// for a dynamic linkset.
|
||||
// Only the state of the passed object can be modified. The rest of the linkset
|
||||
// has not yet been fully constructed.
|
||||
// Return 'true' if any properties updated on the passed object.
|
||||
// Called at taint-time!
|
||||
public override bool MakeDynamic(BSPhysObject child)
|
||||
{
|
||||
bool ret = false;
|
||||
DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child));
|
||||
if (IsRoot(child))
|
||||
{
|
||||
// The root is going dynamic. Make sure mass is properly set.
|
||||
ScheduleRebuild(LinksetRoot);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The origional prims are removed from the world as the shape of the root compound
|
||||
// shape takes over.
|
||||
BulletSimAPI.AddToCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
|
||||
BulletSimAPI.ForceActivationState2(child.PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
|
||||
// We don't want collisions from the old linkset children.
|
||||
BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||
|
||||
child.PhysBody.collisionType = CollisionType.LinksetChild;
|
||||
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The object is going static (non-physical). Do any setup necessary for a static linkset.
|
||||
// Return 'true' if any properties updated on the passed object.
|
||||
// This doesn't normally happen -- OpenSim removes the objects from the physical
|
||||
// world if it is a static linkset.
|
||||
// Called at taint-time!
|
||||
public override bool MakeStatic(BSPhysObject child)
|
||||
{
|
||||
bool ret = false;
|
||||
DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child));
|
||||
if (IsRoot(child))
|
||||
{
|
||||
ScheduleRebuild(LinksetRoot);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The non-physical children can come back to life.
|
||||
BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
|
||||
|
||||
child.PhysBody.collisionType = CollisionType.LinksetChild;
|
||||
|
||||
// Don't force activation so setting of DISABLE_SIMULATION can stay if used.
|
||||
BulletSimAPI.Activate2(child.PhysBody.ptr, false);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override void UpdateProperties(BSPhysObject updated, bool physicalUpdate)
|
||||
{
|
||||
// The user moving a child around requires the rebuilding of the linkset compound shape
|
||||
// One problem is this happens when a border is crossed -- the simulator implementation
|
||||
// is to store the position into the group which causes the move of the object
|
||||
// but it also means all the child positions get updated.
|
||||
// What would cause an unnecessary rebuild so we make sure the linkset is in a
|
||||
// region before bothering to do a rebuild.
|
||||
if (!IsRoot(updated)
|
||||
&& !physicalUpdate
|
||||
&& PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
|
||||
{
|
||||
updated.LinksetInfo = null;
|
||||
ScheduleRebuild(updated);
|
||||
}
|
||||
}
|
||||
|
||||
// Routine called when rebuilding the body of some member of the linkset.
|
||||
// Since we don't keep in world relationships, do nothing unless it's a child changing.
|
||||
// Returns 'true' of something was actually removed and would need restoring
|
||||
// Called at taint-time!!
|
||||
public override bool RemoveBodyDependencies(BSPrim child)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}",
|
||||
child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(), IsRoot(child));
|
||||
|
||||
if (!IsRoot(child))
|
||||
{
|
||||
// Because it is a convenient time, recompute child world position and rotation based on
|
||||
// its position in the linkset.
|
||||
RecomputeChildWorldPosition(child, true);
|
||||
}
|
||||
|
||||
// Cannot schedule a refresh/rebuild here because this routine is called when
|
||||
// the linkset is being rebuilt.
|
||||
// InternalRefresh(LinksetRoot);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
|
||||
// this routine will restore the removed constraints.
|
||||
// Called at taint-time!!
|
||||
public override void RestoreBodyDependencies(BSPrim child)
|
||||
{
|
||||
}
|
||||
|
||||
// When the linkset is built, the child shape is added to the compound shape relative to the
|
||||
// root shape. The linkset then moves around but this does not move the actual child
|
||||
// prim. The child prim's location must be recomputed based on the location of the root shape.
|
||||
private void RecomputeChildWorldPosition(BSPhysObject child, bool inTaintTime)
|
||||
{
|
||||
BSLinksetCompoundInfo lci = child.LinksetInfo as BSLinksetCompoundInfo;
|
||||
if (lci != null)
|
||||
{
|
||||
if (inTaintTime)
|
||||
{
|
||||
OMV.Vector3 oldPos = child.RawPosition;
|
||||
child.ForcePosition = LinksetRoot.RawPosition + lci.OffsetPos;
|
||||
child.ForceOrientation = LinksetRoot.RawOrientation * lci.OffsetRot;
|
||||
DetailLog("{0},BSLinksetCompound.RecomputeChildWorldPosition,oldPos={1},lci={2},newPos={3}",
|
||||
child.LocalID, oldPos, lci, child.RawPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TaintedObject is not used here so the raw position is set now and not at taint-time.
|
||||
child.Position = LinksetRoot.RawPosition + lci.OffsetPos;
|
||||
child.Orientation = LinksetRoot.RawOrientation * lci.OffsetRot;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This happens when children have been added to the linkset but the linkset
|
||||
// has not been constructed yet. So like, at taint time, adding children to a linkset
|
||||
// and then changing properties of the children (makePhysical, for instance)
|
||||
// but the post-print action of actually rebuilding the linkset has not yet happened.
|
||||
// PhysicsScene.Logger.WarnFormat("{0} Restoring linkset child position failed because of no relative position computed. ID={1}",
|
||||
// LogHeader, child.LocalID);
|
||||
DetailLog("{0},BSLinksetCompound.recomputeChildWorldPosition,noRelativePositonInfo", child.LocalID);
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
|
||||
// Add a new child to the linkset.
|
||||
// Called while LinkActivity is locked.
|
||||
protected override void AddChildToLinkset(BSPhysObject child)
|
||||
{
|
||||
if (!HasChild(child))
|
||||
{
|
||||
m_children.Add(child);
|
||||
|
||||
DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
|
||||
|
||||
// Rebuild the compound shape with the new child shape included
|
||||
ScheduleRebuild(child);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the specified child from the linkset.
|
||||
// Safe to call even if the child is not really in the linkset.
|
||||
protected override void RemoveChildFromLinkset(BSPhysObject child)
|
||||
{
|
||||
if (m_children.Remove(child))
|
||||
{
|
||||
DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
|
||||
child.LocalID,
|
||||
LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(),
|
||||
child.LocalID, child.PhysBody.ptr.ToString());
|
||||
|
||||
// Cause the child's body to be rebuilt and thus restored to normal operation
|
||||
RecomputeChildWorldPosition(child, false);
|
||||
child.ForceBodyShapeRebuild(false);
|
||||
|
||||
if (!HasAnyChildren)
|
||||
{
|
||||
// The linkset is now empty. The root needs rebuilding.
|
||||
LinksetRoot.ForceBodyShapeRebuild(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rebuild the compound shape with the child removed
|
||||
ScheduleRebuild(child);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Called before the simulation step to make sure the compound based linkset
|
||||
// is all initialized.
|
||||
// Constraint linksets are rebuilt every time.
|
||||
// Note that this works for rebuilding just the root after a linkset is taken apart.
|
||||
// Called at taint time!!
|
||||
private void RecomputeLinksetCompound()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Suppress rebuilding while rebuilding
|
||||
Rebuilding = true;
|
||||
|
||||
// Cause the root shape to be rebuilt as a compound object with just the root in it
|
||||
LinksetRoot.ForceBodyShapeRebuild(true);
|
||||
|
||||
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}",
|
||||
LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren);
|
||||
|
||||
// Add a shape for each of the other children in the linkset
|
||||
ForEachMember(delegate(BSPhysObject cPrim)
|
||||
{
|
||||
if (!IsRoot(cPrim))
|
||||
{
|
||||
// Compute the displacement of the child from the root of the linkset.
|
||||
// This info is saved in the child prim so the relationship does not
|
||||
// change over time and the new child position can be computed
|
||||
// when the linkset is being disassembled (the linkset may have moved).
|
||||
BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo;
|
||||
if (lci == null)
|
||||
{
|
||||
// Each child position and rotation is given relative to the root.
|
||||
OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
|
||||
OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation;
|
||||
OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation;
|
||||
|
||||
// Save relative position for recomputing child's world position after moving linkset.
|
||||
lci = new BSLinksetCompoundInfo(displacementPos, displacementRot);
|
||||
cPrim.LinksetInfo = lci;
|
||||
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci);
|
||||
}
|
||||
|
||||
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}",
|
||||
LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, lci.OffsetPos, lci.OffsetRot);
|
||||
|
||||
if (cPrim.PhysShape.isNativeShape)
|
||||
{
|
||||
// A native shape is turning into a hull collision shape because native
|
||||
// shapes are not shared so we have to hullify it so it will be tracked
|
||||
// and freed at the correct time. This also solves the scaling problem
|
||||
// (native shapes scaled but hull/meshes are assumed to not be).
|
||||
// TODO: decide of the native shape can just be used in the compound shape.
|
||||
// Use call to CreateGeomNonSpecial().
|
||||
BulletShape saveShape = cPrim.PhysShape;
|
||||
cPrim.PhysShape.Clear(); // Don't let the create free the child's shape
|
||||
// PhysicsScene.Shapes.CreateGeomNonSpecial(true, cPrim, null);
|
||||
PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null);
|
||||
BulletShape newShape = cPrim.PhysShape;
|
||||
cPrim.PhysShape = saveShape;
|
||||
BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, lci.OffsetPos, lci.OffsetRot);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For the shared shapes (meshes and hulls), just use the shape in the child.
|
||||
// The reference count added here will be decremented when the compound shape
|
||||
// is destroyed in BSShapeCollection (the child shapes are looped over and dereferenced).
|
||||
if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape))
|
||||
{
|
||||
PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}",
|
||||
LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape);
|
||||
}
|
||||
BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, lci.OffsetPos, lci.OffsetRot);
|
||||
}
|
||||
}
|
||||
return false; // 'false' says to move onto the next child in the list
|
||||
});
|
||||
|
||||
// With all of the linkset packed into the root prim, it has the mass of everyone.
|
||||
LinksetMass = LinksetMass;
|
||||
LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Rebuilding = false;
|
||||
}
|
||||
|
||||
BulletSimAPI.RecalculateCompoundShapeLocalAabb2(LinksetRoot.PhysShape.ptr);
|
||||
|
||||
// DEBUG: see of inter-linkset collisions are causing problems for constraint linksets.
|
||||
// BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
|
||||
// (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,316 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using OMV = OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public sealed class BSLinksetConstraints : BSLinkset
|
||||
{
|
||||
// private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]";
|
||||
|
||||
public BSLinksetConstraints(BSScene scene, BSPhysObject parent) : base(scene, parent)
|
||||
{
|
||||
}
|
||||
|
||||
// When physical properties are changed the linkset needs to recalculate
|
||||
// its internal properties.
|
||||
// This is queued in the 'post taint' queue so the
|
||||
// refresh will happen once after all the other taints are applied.
|
||||
public override void Refresh(BSPhysObject requestor)
|
||||
{
|
||||
base.Refresh(requestor);
|
||||
|
||||
// Queue to happen after all the other taint processing
|
||||
PhysicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate()
|
||||
{
|
||||
if (HasAnyChildren && IsRoot(requestor))
|
||||
RecomputeLinksetConstraints();
|
||||
});
|
||||
}
|
||||
|
||||
// The object is going dynamic (physical). Do any setup necessary
|
||||
// for a dynamic linkset.
|
||||
// Only the state of the passed object can be modified. The rest of the linkset
|
||||
// has not yet been fully constructed.
|
||||
// Return 'true' if any properties updated on the passed object.
|
||||
// Called at taint-time!
|
||||
public override bool MakeDynamic(BSPhysObject child)
|
||||
{
|
||||
// What is done for each object in BSPrim is what we want.
|
||||
return false;
|
||||
}
|
||||
|
||||
// The object is going static (non-physical). Do any setup necessary for a static linkset.
|
||||
// Return 'true' if any properties updated on the passed object.
|
||||
// This doesn't normally happen -- OpenSim removes the objects from the physical
|
||||
// world if it is a static linkset.
|
||||
// Called at taint-time!
|
||||
public override bool MakeStatic(BSPhysObject child)
|
||||
{
|
||||
// What is done for each object in BSPrim is what we want.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Called at taint-time!!
|
||||
public override void UpdateProperties(BSPhysObject updated, bool inTaintTime)
|
||||
{
|
||||
// Nothing to do for constraints on property updates
|
||||
}
|
||||
|
||||
// Routine called when rebuilding the body of some member of the linkset.
|
||||
// Destroy all the constraints have have been made to root and set
|
||||
// up to rebuild the constraints before the next simulation step.
|
||||
// Returns 'true' of something was actually removed and would need restoring
|
||||
// Called at taint-time!!
|
||||
public override bool RemoveBodyDependencies(BSPrim child)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
DetailLog("{0},BSLinksetConstraint.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}",
|
||||
child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString());
|
||||
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
// Just undo all the constraints for this linkset. Rebuild at the end of the step.
|
||||
ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot);
|
||||
// Cause the constraints, et al to be rebuilt before the next simulation step.
|
||||
Refresh(LinksetRoot);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
|
||||
// this routine will restore the removed constraints.
|
||||
// Called at taint-time!!
|
||||
public override void RestoreBodyDependencies(BSPrim child)
|
||||
{
|
||||
// The Refresh operation queued by RemoveBodyDependencies() will build any missing constraints.
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
|
||||
// Add a new child to the linkset.
|
||||
// Called while LinkActivity is locked.
|
||||
protected override void AddChildToLinkset(BSPhysObject child)
|
||||
{
|
||||
if (!HasChild(child))
|
||||
{
|
||||
m_children.Add(child);
|
||||
|
||||
DetailLog("{0},BSLinksetConstraints.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
|
||||
|
||||
// Cause constraints and assorted properties to be recomputed before the next simulation step.
|
||||
Refresh(LinksetRoot);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the specified child from the linkset.
|
||||
// Safe to call even if the child is not really in my linkset.
|
||||
protected override void RemoveChildFromLinkset(BSPhysObject child)
|
||||
{
|
||||
if (m_children.Remove(child))
|
||||
{
|
||||
BSPhysObject rootx = LinksetRoot; // capture the root and body as of now
|
||||
BSPhysObject childx = child;
|
||||
|
||||
DetailLog("{0},BSLinksetConstraints.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
|
||||
childx.LocalID,
|
||||
rootx.LocalID, rootx.PhysBody.ptr.ToString(),
|
||||
childx.LocalID, childx.PhysBody.ptr.ToString());
|
||||
|
||||
PhysicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate()
|
||||
{
|
||||
PhysicallyUnlinkAChildFromRoot(rootx, childx);
|
||||
});
|
||||
// See that the linkset parameters are recomputed at the end of the taint time.
|
||||
Refresh(LinksetRoot);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-fatal occurance.
|
||||
// PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a constraint between me (root of linkset) and the passed prim (the child).
|
||||
// Called at taint time!
|
||||
private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
|
||||
{
|
||||
// Don't build the constraint when asked. Put it off until just before the simulation step.
|
||||
Refresh(rootPrim);
|
||||
}
|
||||
|
||||
private BSConstraint BuildConstraint(BSPhysObject rootPrim, BSPhysObject childPrim)
|
||||
{
|
||||
// Zero motion for children so they don't interpolate
|
||||
childPrim.ZeroMotion(true);
|
||||
|
||||
// Relative position normalized to the root prim
|
||||
// Essentually a vector pointing from center of rootPrim to center of childPrim
|
||||
OMV.Vector3 childRelativePosition = childPrim.Position - rootPrim.Position;
|
||||
|
||||
// real world coordinate of midpoint between the two objects
|
||||
OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2);
|
||||
|
||||
DetailLog("{0},BSLinksetConstraint.BuildConstraint,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}",
|
||||
rootPrim.LocalID,
|
||||
rootPrim.LocalID, rootPrim.PhysBody.ptr.ToString(),
|
||||
childPrim.LocalID, childPrim.PhysBody.ptr.ToString(),
|
||||
rootPrim.Position, childPrim.Position, midPoint);
|
||||
|
||||
// create a constraint that allows no freedom of movement between the two objects
|
||||
// http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
|
||||
|
||||
BSConstraint6Dof constrain = new BSConstraint6Dof(
|
||||
PhysicsScene.World, rootPrim.PhysBody, childPrim.PhysBody, midPoint, true, true );
|
||||
// PhysicsScene.World, childPrim.BSBody, rootPrim.BSBody, midPoint, true, true );
|
||||
|
||||
/* NOTE: below is an attempt to build constraint with full frame computation, etc.
|
||||
* Using the midpoint is easier since it lets the Bullet code manipulate the transforms
|
||||
* of the objects.
|
||||
* Code left for future programmers.
|
||||
// ==================================================================================
|
||||
// relative position normalized to the root prim
|
||||
OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation);
|
||||
OMV.Vector3 childRelativePosition = (childPrim.Position - rootPrim.Position) * invThisOrientation;
|
||||
|
||||
// relative rotation of the child to the parent
|
||||
OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation;
|
||||
OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation);
|
||||
|
||||
DetailLog("{0},BSLinksetConstraint.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID);
|
||||
BS6DofConstraint constrain = new BS6DofConstraint(
|
||||
PhysicsScene.World, rootPrim.Body, childPrim.Body,
|
||||
OMV.Vector3.Zero,
|
||||
OMV.Quaternion.Inverse(rootPrim.Orientation),
|
||||
OMV.Vector3.Zero,
|
||||
OMV.Quaternion.Inverse(childPrim.Orientation),
|
||||
true,
|
||||
true
|
||||
);
|
||||
// ==================================================================================
|
||||
*/
|
||||
|
||||
PhysicsScene.Constraints.AddConstraint(constrain);
|
||||
|
||||
// zero linear and angular limits makes the objects unable to move in relation to each other
|
||||
constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
|
||||
constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
|
||||
|
||||
// tweek the constraint to increase stability
|
||||
constrain.UseFrameOffset(BSParam.BoolNumeric(BSParam.LinkConstraintUseFrameOffset));
|
||||
constrain.TranslationalLimitMotor(BSParam.BoolNumeric(BSParam.LinkConstraintEnableTransMotor),
|
||||
BSParam.LinkConstraintTransMotorMaxVel,
|
||||
BSParam.LinkConstraintTransMotorMaxForce);
|
||||
constrain.SetCFMAndERP(BSParam.LinkConstraintCFM, BSParam.LinkConstraintERP);
|
||||
if (BSParam.LinkConstraintSolverIterations != 0f)
|
||||
{
|
||||
constrain.SetSolverIterations(BSParam.LinkConstraintSolverIterations);
|
||||
}
|
||||
return constrain;
|
||||
}
|
||||
|
||||
// Remove linkage between the linkset root and a particular child
|
||||
// The root and child bodies are passed in because we need to remove the constraint between
|
||||
// the bodies that were present at unlink time.
|
||||
// Called at taint time!
|
||||
private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
|
||||
{
|
||||
bool ret = false;
|
||||
DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}",
|
||||
rootPrim.LocalID,
|
||||
rootPrim.LocalID, rootPrim.PhysBody.ptr.ToString(),
|
||||
childPrim.LocalID, childPrim.PhysBody.ptr.ToString());
|
||||
|
||||
// Find the constraint for this link and get rid of it from the overall collection and from my list
|
||||
if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody))
|
||||
{
|
||||
// Make the child refresh its location
|
||||
BulletSimAPI.PushUpdate2(childPrim.PhysBody.ptr);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Remove linkage between myself and any possible children I might have.
|
||||
// Returns 'true' of any constraints were destroyed.
|
||||
// Called at taint time!
|
||||
private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim)
|
||||
{
|
||||
DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID);
|
||||
|
||||
return PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody);
|
||||
}
|
||||
|
||||
// Call each of the constraints that make up this linkset and recompute the
|
||||
// various transforms and variables. Create constraints of not created yet.
|
||||
// Called before the simulation step to make sure the constraint based linkset
|
||||
// is all initialized.
|
||||
// Called at taint time!!
|
||||
private void RecomputeLinksetConstraints()
|
||||
{
|
||||
float linksetMass = LinksetMass;
|
||||
LinksetRoot.UpdatePhysicalMassProperties(linksetMass, true);
|
||||
|
||||
// DEBUG: see of inter-linkset collisions are causing problems
|
||||
// BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
|
||||
// (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
|
||||
DetailLog("{0},BSLinksetConstraint.RecomputeLinksetConstraints,set,rBody={1},linksetMass={2}",
|
||||
LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(), linksetMass);
|
||||
|
||||
foreach (BSPhysObject child in m_children)
|
||||
{
|
||||
// A child in the linkset physically shows the mass of the whole linkset.
|
||||
// This allows Bullet to apply enough force on the child to move the whole linkset.
|
||||
// (Also do the mass stuff before recomputing the constraint so mass is not zero.)
|
||||
child.UpdatePhysicalMassProperties(linksetMass, true);
|
||||
|
||||
BSConstraint constrain;
|
||||
if (!PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain))
|
||||
{
|
||||
// If constraint doesn't exist yet, create it.
|
||||
constrain = BuildConstraint(LinksetRoot, child);
|
||||
}
|
||||
constrain.RecomputeConstraintVariables(linksetMass);
|
||||
|
||||
// DEBUG: see of inter-linkset collisions are causing problems
|
||||
// BulletSimAPI.SetCollisionFilterMask2(child.BSBody.ptr,
|
||||
// (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
|
||||
|
||||
// BulletSimAPI.DumpConstraint2(PhysicsScene.World.ptr, constrain.Constraint.ptr); // DEBUG DEBUG
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,200 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using Nini.Config;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
public struct MaterialAttributes
|
||||
{
|
||||
// Material type values that correspond with definitions for LSL
|
||||
public enum Material : int
|
||||
{
|
||||
Stone = 0,
|
||||
Metal,
|
||||
Glass,
|
||||
Wood,
|
||||
Flesh,
|
||||
Plastic,
|
||||
Rubber,
|
||||
Light,
|
||||
// Hereafter are BulletSim additions
|
||||
Avatar,
|
||||
NumberOfTypes // the count of types in the enum.
|
||||
}
|
||||
|
||||
// Names must be in the order of the above enum.
|
||||
// These names must coorespond to the lower case field names in the MaterialAttributes
|
||||
// structure as reflection is used to select the field to put the value in.
|
||||
public static readonly string[] MaterialAttribs = { "Density", "Friction", "Restitution"};
|
||||
|
||||
public MaterialAttributes(string t, float d, float f, float r)
|
||||
{
|
||||
type = t;
|
||||
density = d;
|
||||
friction = f;
|
||||
restitution = r;
|
||||
}
|
||||
public string type;
|
||||
public float density;
|
||||
public float friction;
|
||||
public float restitution;
|
||||
}
|
||||
|
||||
public static class BSMaterials
|
||||
{
|
||||
// Attributes for each material type
|
||||
private static readonly MaterialAttributes[] Attributes;
|
||||
|
||||
// Map of material name to material type code
|
||||
public static readonly Dictionary<string, MaterialAttributes.Material> MaterialMap;
|
||||
|
||||
static BSMaterials()
|
||||
{
|
||||
// Attribute sets for both the non-physical and physical instances of materials.
|
||||
Attributes = new MaterialAttributes[(int)MaterialAttributes.Material.NumberOfTypes * 2];
|
||||
|
||||
// Map of name to type code.
|
||||
MaterialMap = new Dictionary<string, MaterialAttributes.Material>();
|
||||
MaterialMap.Add("Stone", MaterialAttributes.Material.Stone);
|
||||
MaterialMap.Add("Metal", MaterialAttributes.Material.Metal);
|
||||
MaterialMap.Add("Glass", MaterialAttributes.Material.Glass);
|
||||
MaterialMap.Add("Wood", MaterialAttributes.Material.Wood);
|
||||
MaterialMap.Add("Flesh", MaterialAttributes.Material.Flesh);
|
||||
MaterialMap.Add("Plastic", MaterialAttributes.Material.Plastic);
|
||||
MaterialMap.Add("Rubber", MaterialAttributes.Material.Rubber);
|
||||
MaterialMap.Add("Light", MaterialAttributes.Material.Light);
|
||||
MaterialMap.Add("Avatar", MaterialAttributes.Material.Avatar);
|
||||
}
|
||||
|
||||
// This is where all the default material attributes are defined.
|
||||
public static void InitializeFromDefaults(ConfigurationParameters parms)
|
||||
{
|
||||
// Values from http://wiki.secondlife.com/wiki/PRIM_MATERIAL
|
||||
float dDensity = parms.defaultDensity;
|
||||
float dFriction = parms.defaultFriction;
|
||||
float dRestitution = parms.defaultRestitution;
|
||||
Attributes[(int)MaterialAttributes.Material.Stone] =
|
||||
new MaterialAttributes("stone",dDensity, 0.8f, 0.4f);
|
||||
Attributes[(int)MaterialAttributes.Material.Metal] =
|
||||
new MaterialAttributes("metal",dDensity, 0.3f, 0.4f);
|
||||
Attributes[(int)MaterialAttributes.Material.Glass] =
|
||||
new MaterialAttributes("glass",dDensity, 0.2f, 0.7f);
|
||||
Attributes[(int)MaterialAttributes.Material.Wood] =
|
||||
new MaterialAttributes("wood",dDensity, 0.6f, 0.5f);
|
||||
Attributes[(int)MaterialAttributes.Material.Flesh] =
|
||||
new MaterialAttributes("flesh",dDensity, 0.9f, 0.3f);
|
||||
Attributes[(int)MaterialAttributes.Material.Plastic] =
|
||||
new MaterialAttributes("plastic",dDensity, 0.4f, 0.7f);
|
||||
Attributes[(int)MaterialAttributes.Material.Rubber] =
|
||||
new MaterialAttributes("rubber",dDensity, 0.9f, 0.9f);
|
||||
Attributes[(int)MaterialAttributes.Material.Light] =
|
||||
new MaterialAttributes("light",dDensity, dFriction, dRestitution);
|
||||
Attributes[(int)MaterialAttributes.Material.Avatar] =
|
||||
new MaterialAttributes("avatar",3.5f, 0.2f, 0f);
|
||||
|
||||
Attributes[(int)MaterialAttributes.Material.Stone + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("stonePhysical",dDensity, 0.8f, 0.4f);
|
||||
Attributes[(int)MaterialAttributes.Material.Metal + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("metalPhysical",dDensity, 0.3f, 0.4f);
|
||||
Attributes[(int)MaterialAttributes.Material.Glass + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("glassPhysical",dDensity, 0.2f, 0.7f);
|
||||
Attributes[(int)MaterialAttributes.Material.Wood + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("woodPhysical",dDensity, 0.6f, 0.5f);
|
||||
Attributes[(int)MaterialAttributes.Material.Flesh + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("fleshPhysical",dDensity, 0.9f, 0.3f);
|
||||
Attributes[(int)MaterialAttributes.Material.Plastic + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("plasticPhysical",dDensity, 0.4f, 0.7f);
|
||||
Attributes[(int)MaterialAttributes.Material.Rubber + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("rubberPhysical",dDensity, 0.9f, 0.9f);
|
||||
Attributes[(int)MaterialAttributes.Material.Light + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("lightPhysical",dDensity, dFriction, dRestitution);
|
||||
Attributes[(int)MaterialAttributes.Material.Avatar + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("avatarPhysical",3.5f, 0.2f, 0f);
|
||||
}
|
||||
|
||||
// Under the [BulletSim] section, one can change the individual material
|
||||
// attribute values. The format of the configuration parameter is:
|
||||
// <materialName><Attribute>["Physical"] = floatValue
|
||||
// For instance:
|
||||
// [BulletSim]
|
||||
// StoneFriction = 0.2
|
||||
// FleshRestitutionPhysical = 0.8
|
||||
// Materials can have different parameters for their static and
|
||||
// physical instantiations. When setting the non-physical value,
|
||||
// both values are changed. Setting the physical value only changes
|
||||
// the physical value.
|
||||
public static void InitializefromParameters(IConfig pConfig)
|
||||
{
|
||||
foreach (KeyValuePair<string, MaterialAttributes.Material> kvp in MaterialMap)
|
||||
{
|
||||
string matName = kvp.Key;
|
||||
foreach (string attribName in MaterialAttributes.MaterialAttribs)
|
||||
{
|
||||
string paramName = matName + attribName;
|
||||
if (pConfig.Contains(paramName))
|
||||
{
|
||||
float paramValue = pConfig.GetFloat(paramName);
|
||||
SetAttributeValue((int)kvp.Value, attribName, paramValue);
|
||||
// set the physical value also
|
||||
SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
|
||||
}
|
||||
paramName += "Physical";
|
||||
if (pConfig.Contains(paramName))
|
||||
{
|
||||
float paramValue = pConfig.GetFloat(paramName);
|
||||
SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use reflection to set the value in the attribute structure.
|
||||
private static void SetAttributeValue(int matType, string attribName, float val)
|
||||
{
|
||||
MaterialAttributes thisAttrib = Attributes[matType];
|
||||
FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName.ToLower());
|
||||
if (fieldInfo != null)
|
||||
{
|
||||
fieldInfo.SetValue(thisAttrib, val);
|
||||
Attributes[matType] = thisAttrib;
|
||||
}
|
||||
}
|
||||
|
||||
// Given a material type, return a structure of attributes.
|
||||
public static MaterialAttributes GetAttributes(MaterialAttributes.Material type, bool isPhysical)
|
||||
{
|
||||
int ind = (int)type;
|
||||
if (isPhysical) ind += (int)MaterialAttributes.Material.NumberOfTypes;
|
||||
return Attributes[ind];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,347 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public abstract class BSMotor
|
||||
{
|
||||
// Timescales and other things can be turned off by setting them to 'infinite'.
|
||||
public const float Infinite = 12345.6f;
|
||||
public readonly static Vector3 InfiniteVector = new Vector3(BSMotor.Infinite, BSMotor.Infinite, BSMotor.Infinite);
|
||||
|
||||
public BSMotor(string useName)
|
||||
{
|
||||
UseName = useName;
|
||||
PhysicsScene = null;
|
||||
Enabled = true;
|
||||
}
|
||||
public virtual bool Enabled { get; set; }
|
||||
public virtual void Reset() { }
|
||||
public virtual void Zero() { }
|
||||
public virtual void GenerateTestOutput(float timeStep) { }
|
||||
|
||||
// A name passed at motor creation for easily identifyable debugging messages.
|
||||
public string UseName { get; private set; }
|
||||
|
||||
// Used only for outputting debug information. Might not be set so check for null.
|
||||
public BSScene PhysicsScene { get; set; }
|
||||
protected void MDetailLog(string msg, params Object[] parms)
|
||||
{
|
||||
if (PhysicsScene != null)
|
||||
{
|
||||
if (PhysicsScene.VehicleLoggingEnabled)
|
||||
{
|
||||
PhysicsScene.DetailLog(msg, parms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Motor which moves CurrentValue to TargetValue over TimeScale seconds.
|
||||
// The TargetValue decays in TargetValueDecayTimeScale and
|
||||
// the CurrentValue will be held back by FrictionTimeScale.
|
||||
// This motor will "zero itself" over time in that the targetValue will
|
||||
// decay to zero and the currentValue will follow it to that zero.
|
||||
// The overall effect is for the returned correction value to go from large
|
||||
// values (the total difference between current and target minus friction)
|
||||
// to small and eventually zero values.
|
||||
// TimeScale and TargetDelayTimeScale may be 'infinite' which means no decay.
|
||||
|
||||
// For instance, if something is moving at speed X and the desired speed is Y,
|
||||
// CurrentValue is X and TargetValue is Y. As the motor is stepped, new
|
||||
// values of CurrentValue are returned that approach the TargetValue.
|
||||
// The feature of decaying TargetValue is so vehicles will eventually
|
||||
// come to a stop rather than run forever. This can be disabled by
|
||||
// setting TargetValueDecayTimescale to 'infinite'.
|
||||
// The change from CurrentValue to TargetValue is linear over TimeScale seconds.
|
||||
public class BSVMotor : BSMotor
|
||||
{
|
||||
// public Vector3 FrameOfReference { get; set; }
|
||||
// public Vector3 Offset { get; set; }
|
||||
|
||||
public virtual float TimeScale { get; set; }
|
||||
public virtual float TargetValueDecayTimeScale { get; set; }
|
||||
public virtual Vector3 FrictionTimescale { get; set; }
|
||||
public virtual float Efficiency { get; set; }
|
||||
|
||||
public virtual float ErrorZeroThreshold { get; set; }
|
||||
|
||||
public virtual Vector3 TargetValue { get; protected set; }
|
||||
public virtual Vector3 CurrentValue { get; protected set; }
|
||||
public virtual Vector3 LastError { get; protected set; }
|
||||
|
||||
public virtual bool ErrorIsZero
|
||||
{ get {
|
||||
return (LastError == Vector3.Zero || LastError.LengthSquared() <= ErrorZeroThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
public BSVMotor(string useName)
|
||||
: base(useName)
|
||||
{
|
||||
TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite;
|
||||
Efficiency = 1f;
|
||||
FrictionTimescale = BSMotor.InfiniteVector;
|
||||
CurrentValue = TargetValue = Vector3.Zero;
|
||||
ErrorZeroThreshold = 0.001f;
|
||||
}
|
||||
public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency)
|
||||
: this(useName)
|
||||
{
|
||||
TimeScale = timeScale;
|
||||
TargetValueDecayTimeScale = decayTimeScale;
|
||||
FrictionTimescale = frictionTimeScale;
|
||||
Efficiency = efficiency;
|
||||
CurrentValue = TargetValue = Vector3.Zero;
|
||||
}
|
||||
public void SetCurrent(Vector3 current)
|
||||
{
|
||||
CurrentValue = current;
|
||||
}
|
||||
public void SetTarget(Vector3 target)
|
||||
{
|
||||
TargetValue = target;
|
||||
}
|
||||
public override void Zero()
|
||||
{
|
||||
base.Zero();
|
||||
CurrentValue = TargetValue = Vector3.Zero;
|
||||
}
|
||||
|
||||
// Compute the next step and return the new current value
|
||||
public virtual Vector3 Step(float timeStep)
|
||||
{
|
||||
if (!Enabled) return TargetValue;
|
||||
|
||||
Vector3 origTarget = TargetValue; // DEBUG
|
||||
Vector3 origCurrVal = CurrentValue; // DEBUG
|
||||
|
||||
Vector3 correction = Vector3.Zero;
|
||||
Vector3 error = TargetValue - CurrentValue;
|
||||
if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
|
||||
{
|
||||
correction = Step(timeStep, error);
|
||||
|
||||
CurrentValue += correction;
|
||||
|
||||
// The desired value reduces to zero which also reduces the difference with current.
|
||||
// If the decay time is infinite, don't decay at all.
|
||||
float decayFactor = 0f;
|
||||
if (TargetValueDecayTimeScale != BSMotor.Infinite)
|
||||
{
|
||||
decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep;
|
||||
TargetValue *= (1f - decayFactor);
|
||||
}
|
||||
|
||||
// The amount we can correct the error is reduced by the friction
|
||||
Vector3 frictionFactor = Vector3.Zero;
|
||||
if (FrictionTimescale != BSMotor.InfiniteVector)
|
||||
{
|
||||
// frictionFactor = (Vector3.One / FrictionTimescale) * timeStep;
|
||||
// Individual friction components can be 'infinite' so compute each separately.
|
||||
frictionFactor.X = (FrictionTimescale.X == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.X);
|
||||
frictionFactor.Y = (FrictionTimescale.Y == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Y);
|
||||
frictionFactor.Z = (FrictionTimescale.Z == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Z);
|
||||
frictionFactor *= timeStep;
|
||||
CurrentValue *= (Vector3.One - frictionFactor);
|
||||
}
|
||||
|
||||
MDetailLog("{0}, BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},err={5},corr={6}",
|
||||
BSScene.DetailLogZero, UseName, origCurrVal, origTarget,
|
||||
timeStep, error, correction);
|
||||
MDetailLog("{0}, BSVMotor.Step,nonZero,{1},tgtDecayTS={2},decayFact={3},frictTS={4},frictFact={5},tgt={6},curr={7}",
|
||||
BSScene.DetailLogZero, UseName,
|
||||
TargetValueDecayTimeScale, decayFactor, FrictionTimescale, frictionFactor,
|
||||
TargetValue, CurrentValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Difference between what we have and target is small. Motor is done.
|
||||
CurrentValue = TargetValue;
|
||||
MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}",
|
||||
BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue);
|
||||
}
|
||||
|
||||
return CurrentValue;
|
||||
}
|
||||
public virtual Vector3 Step(float timeStep, Vector3 error)
|
||||
{
|
||||
if (!Enabled) return Vector3.Zero;
|
||||
|
||||
LastError = error;
|
||||
Vector3 returnCorrection = Vector3.Zero;
|
||||
if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
|
||||
{
|
||||
// correction = error / secondsItShouldTakeToCorrect
|
||||
Vector3 correctionAmount;
|
||||
if (TimeScale == 0f || TimeScale == BSMotor.Infinite)
|
||||
correctionAmount = error * timeStep;
|
||||
else
|
||||
correctionAmount = error / TimeScale * timeStep;
|
||||
|
||||
returnCorrection = correctionAmount;
|
||||
MDetailLog("{0}, BSVMotor.Step,nonZero,{1},timeStep={2},timeScale={3},err={4},corr={5}",
|
||||
BSScene.DetailLogZero, UseName, timeStep, TimeScale, error, correctionAmount);
|
||||
}
|
||||
return returnCorrection;
|
||||
}
|
||||
|
||||
// The user sets all the parameters and calls this which outputs values until error is zero.
|
||||
public override void GenerateTestOutput(float timeStep)
|
||||
{
|
||||
// maximum number of outputs to generate.
|
||||
int maxOutput = 50;
|
||||
MDetailLog("{0},BSVMotor.Test,{1},===================================== BEGIN Test Output", BSScene.DetailLogZero, UseName);
|
||||
MDetailLog("{0},BSVMotor.Test,{1},timeScale={2},targDlyTS={3},frictTS={4},eff={5},curr={6},tgt={7}",
|
||||
BSScene.DetailLogZero, UseName,
|
||||
TimeScale, TargetValueDecayTimeScale, FrictionTimescale, Efficiency,
|
||||
CurrentValue, TargetValue);
|
||||
|
||||
LastError = BSMotor.InfiniteVector;
|
||||
while (maxOutput-- > 0 && !LastError.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
|
||||
{
|
||||
Vector3 lastStep = Step(timeStep);
|
||||
MDetailLog("{0},BSVMotor.Test,{1},cur={2},tgt={3},lastError={4},lastStep={5}",
|
||||
BSScene.DetailLogZero, UseName, CurrentValue, TargetValue, LastError, lastStep);
|
||||
}
|
||||
MDetailLog("{0},BSVMotor.Test,{1},===================================== END Test Output", BSScene.DetailLogZero, UseName);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("<{0},curr={1},targ={2},decayTS={3},frictTS={4}>",
|
||||
UseName, CurrentValue, TargetValue, TargetValueDecayTimeScale, FrictionTimescale);
|
||||
}
|
||||
}
|
||||
|
||||
public class BSFMotor : BSMotor
|
||||
{
|
||||
public float TimeScale { get; set; }
|
||||
public float DecayTimeScale { get; set; }
|
||||
public float Friction { get; set; }
|
||||
public float Efficiency { get; set; }
|
||||
|
||||
public float Target { get; private set; }
|
||||
public float CurrentValue { get; private set; }
|
||||
|
||||
public BSFMotor(string useName, float timeScale, float decayTimescale, float friction, float efficiency)
|
||||
: base(useName)
|
||||
{
|
||||
}
|
||||
public void SetCurrent(float target)
|
||||
{
|
||||
}
|
||||
public void SetTarget(float target)
|
||||
{
|
||||
}
|
||||
public virtual float Step(float timeStep)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Proportional, Integral, Derivitive Motor
|
||||
// Good description at http://www.answers.com/topic/pid-controller . Includes processes for choosing p, i and d factors.
|
||||
public class BSPIDVMotor : BSVMotor
|
||||
{
|
||||
// Larger makes more overshoot, smaller means converge quicker. Range of 0.1 to 10.
|
||||
public Vector3 proportionFactor { get; set; }
|
||||
public Vector3 integralFactor { get; set; }
|
||||
public Vector3 derivFactor { get; set; }
|
||||
|
||||
// Arbritrary factor range.
|
||||
// EfficiencyHigh means move quickly to the correct number. EfficiencyLow means might over correct.
|
||||
public float EfficiencyHigh = 0.4f;
|
||||
public float EfficiencyLow = 4.0f;
|
||||
|
||||
// Running integration of the error
|
||||
Vector3 RunningIntegration { get; set; }
|
||||
|
||||
public BSPIDVMotor(string useName)
|
||||
: base(useName)
|
||||
{
|
||||
proportionFactor = new Vector3(1.00f, 1.00f, 1.00f);
|
||||
integralFactor = new Vector3(1.00f, 1.00f, 1.00f);
|
||||
derivFactor = new Vector3(1.00f, 1.00f, 1.00f);
|
||||
RunningIntegration = Vector3.Zero;
|
||||
LastError = Vector3.Zero;
|
||||
}
|
||||
|
||||
public override void Zero()
|
||||
{
|
||||
base.Zero();
|
||||
}
|
||||
|
||||
public override float Efficiency
|
||||
{
|
||||
get { return base.Efficiency; }
|
||||
set
|
||||
{
|
||||
base.Efficiency = Util.Clamp(value, 0f, 1f);
|
||||
// Compute factors based on efficiency.
|
||||
// If efficiency is high (1f), use a factor value that moves the error value to zero with little overshoot.
|
||||
// If efficiency is low (0f), use a factor value that overcorrects.
|
||||
// TODO: might want to vary contribution of different factor depending on efficiency.
|
||||
float factor = ((1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow) / 3f;
|
||||
// float factor = (1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow;
|
||||
proportionFactor = new Vector3(factor, factor, factor);
|
||||
integralFactor = new Vector3(factor, factor, factor);
|
||||
derivFactor = new Vector3(factor, factor, factor);
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore Current and Target Values and just advance the PID computation on this error.
|
||||
public override Vector3 Step(float timeStep, Vector3 error)
|
||||
{
|
||||
if (!Enabled) return Vector3.Zero;
|
||||
|
||||
// Add up the error so we can integrate over the accumulated errors
|
||||
RunningIntegration += error * timeStep;
|
||||
|
||||
// A simple derivitive is the rate of change from the last error.
|
||||
Vector3 derivFactor = (error - LastError) * timeStep;
|
||||
LastError = error;
|
||||
|
||||
// Correction = -(proportionOfPresentError + accumulationOfPastError + rateOfChangeOfError)
|
||||
Vector3 ret = -(
|
||||
error * proportionFactor
|
||||
+ RunningIntegration * integralFactor
|
||||
+ derivFactor * derivFactor
|
||||
);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,559 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
|
||||
using OpenMetaverse;
|
||||
using Nini.Config;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public static class BSParam
|
||||
{
|
||||
// Level of Detail values kept as float because that's what the Meshmerizer wants
|
||||
public static float MeshLOD { get; private set; }
|
||||
public static float MeshMegaPrimLOD { get; private set; }
|
||||
public static float MeshMegaPrimThreshold { get; private set; }
|
||||
public static float SculptLOD { get; private set; }
|
||||
|
||||
public static float MinimumObjectMass { get; private set; }
|
||||
public static float MaximumObjectMass { get; private set; }
|
||||
|
||||
public static float LinearDamping { get; private set; }
|
||||
public static float AngularDamping { get; private set; }
|
||||
public static float DeactivationTime { get; private set; }
|
||||
public static float LinearSleepingThreshold { get; private set; }
|
||||
public static float AngularSleepingThreshold { get; private set; }
|
||||
public static float CcdMotionThreshold { get; private set; }
|
||||
public static float CcdSweptSphereRadius { get; private set; }
|
||||
public static float ContactProcessingThreshold { get; private set; }
|
||||
|
||||
public static bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed
|
||||
public static bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes
|
||||
public static bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects
|
||||
|
||||
public static float TerrainImplementation { get; private set; }
|
||||
public static float TerrainFriction { get; private set; }
|
||||
public static float TerrainHitFraction { get; private set; }
|
||||
public static float TerrainRestitution { get; private set; }
|
||||
public static float TerrainCollisionMargin { get; private set; }
|
||||
|
||||
// Avatar parameters
|
||||
public static float AvatarFriction { get; private set; }
|
||||
public static float AvatarStandingFriction { get; private set; }
|
||||
public static float AvatarDensity { get; private set; }
|
||||
public static float AvatarRestitution { get; private set; }
|
||||
public static float AvatarCapsuleWidth { get; private set; }
|
||||
public static float AvatarCapsuleDepth { get; private set; }
|
||||
public static float AvatarCapsuleHeight { get; private set; }
|
||||
public static float AvatarContactProcessingThreshold { get; private set; }
|
||||
|
||||
public static float VehicleAngularDamping { get; private set; }
|
||||
|
||||
public static float LinksetImplementation { get; private set; }
|
||||
public static float LinkConstraintUseFrameOffset { get; private set; }
|
||||
public static float LinkConstraintEnableTransMotor { get; private set; }
|
||||
public static float LinkConstraintTransMotorMaxVel { get; private set; }
|
||||
public static float LinkConstraintTransMotorMaxForce { get; private set; }
|
||||
public static float LinkConstraintERP { get; private set; }
|
||||
public static float LinkConstraintCFM { get; private set; }
|
||||
public static float LinkConstraintSolverIterations { get; private set; }
|
||||
|
||||
public static float PID_D { get; private set; } // derivative
|
||||
public static float PID_P { get; private set; } // proportional
|
||||
|
||||
public delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
|
||||
public delegate float ParamGet(BSScene scene);
|
||||
public delegate void ParamSet(BSScene scene, string paramName, uint localID, float val);
|
||||
public delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val);
|
||||
|
||||
public struct ParameterDefn
|
||||
{
|
||||
public string name; // string name of the parameter
|
||||
public string desc; // a short description of what the parameter means
|
||||
public float defaultValue; // default value if not specified anywhere else
|
||||
public ParamUser userParam; // get the value from the configuration file
|
||||
public ParamGet getter; // return the current value stored for this parameter
|
||||
public ParamSet setter; // set the current value for this parameter
|
||||
public SetOnObject onObject; // set the value on an object in the physical domain
|
||||
public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s)
|
||||
{
|
||||
name = n;
|
||||
desc = d;
|
||||
defaultValue = v;
|
||||
userParam = u;
|
||||
getter = g;
|
||||
setter = s;
|
||||
onObject = null;
|
||||
}
|
||||
public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o)
|
||||
{
|
||||
name = n;
|
||||
desc = d;
|
||||
defaultValue = v;
|
||||
userParam = u;
|
||||
getter = g;
|
||||
setter = s;
|
||||
onObject = o;
|
||||
}
|
||||
}
|
||||
|
||||
// List of all of the externally visible parameters.
|
||||
// For each parameter, this table maps a text name to getter and setters.
|
||||
// To add a new externally referencable/settable parameter, add the paramter storage
|
||||
// location somewhere in the program and make an entry in this table with the
|
||||
// getters and setters.
|
||||
// It is easiest to find an existing definition and copy it.
|
||||
// Parameter values are floats. Booleans are converted to a floating value.
|
||||
//
|
||||
// A ParameterDefn() takes the following parameters:
|
||||
// -- the text name of the parameter. This is used for console input and ini file.
|
||||
// -- a short text description of the parameter. This shows up in the console listing.
|
||||
// -- a default value (float)
|
||||
// -- a delegate for fetching the parameter from the ini file.
|
||||
// Should handle fetching the right type from the ini file and converting it.
|
||||
// -- a delegate for getting the value as a float
|
||||
// -- a delegate for setting the value from a float
|
||||
// -- an optional delegate to update the value in the world. Most often used to
|
||||
// push the new value to an in-world object.
|
||||
//
|
||||
// The single letter parameters for the delegates are:
|
||||
// s = BSScene
|
||||
// o = BSPhysObject
|
||||
// p = string parameter name
|
||||
// l = localID of referenced object
|
||||
// v = value (float)
|
||||
// cf = parameter configuration class (for fetching values from ini file)
|
||||
private static ParameterDefn[] ParameterDefinitions =
|
||||
{
|
||||
new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties",
|
||||
ConfigurationParameters.numericTrue,
|
||||
(s,cf,p,v) => { ShouldMeshSculptedPrim = cf.GetBoolean(p, BSParam.BoolNumeric(v)); },
|
||||
(s) => { return BSParam.NumericBool(ShouldMeshSculptedPrim); },
|
||||
(s,p,l,v) => { ShouldMeshSculptedPrim = BSParam.BoolNumeric(v); } ),
|
||||
new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects",
|
||||
ConfigurationParameters.numericFalse,
|
||||
(s,cf,p,v) => { ShouldForceSimplePrimMeshing = cf.GetBoolean(p, BSParam.BoolNumeric(v)); },
|
||||
(s) => { return BSParam.NumericBool(ShouldForceSimplePrimMeshing); },
|
||||
(s,p,l,v) => { ShouldForceSimplePrimMeshing = BSParam.BoolNumeric(v); } ),
|
||||
new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects",
|
||||
ConfigurationParameters.numericTrue,
|
||||
(s,cf,p,v) => { ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, BSParam.BoolNumeric(v)); },
|
||||
(s) => { return BSParam.NumericBool(ShouldUseHullsForPhysicalObjects); },
|
||||
(s,p,l,v) => { ShouldUseHullsForPhysicalObjects = BSParam.BoolNumeric(v); } ),
|
||||
|
||||
new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
|
||||
8f,
|
||||
(s,cf,p,v) => { MeshLOD = (float)cf.GetInt(p, (int)v); },
|
||||
(s) => { return MeshLOD; },
|
||||
(s,p,l,v) => { MeshLOD = v; } ),
|
||||
new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters",
|
||||
16f,
|
||||
(s,cf,p,v) => { MeshMegaPrimLOD = (float)cf.GetInt(p, (int)v); },
|
||||
(s) => { return MeshMegaPrimLOD; },
|
||||
(s,p,l,v) => { MeshMegaPrimLOD = v; } ),
|
||||
new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD",
|
||||
10f,
|
||||
(s,cf,p,v) => { MeshMegaPrimThreshold = (float)cf.GetInt(p, (int)v); },
|
||||
(s) => { return MeshMegaPrimThreshold; },
|
||||
(s,p,l,v) => { MeshMegaPrimThreshold = v; } ),
|
||||
new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)",
|
||||
32f,
|
||||
(s,cf,p,v) => { SculptLOD = (float)cf.GetInt(p, (int)v); },
|
||||
(s) => { return SculptLOD; },
|
||||
(s,p,l,v) => { SculptLOD = v; } ),
|
||||
|
||||
new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps",
|
||||
10f,
|
||||
(s,cf,p,v) => { s.m_maxSubSteps = cf.GetInt(p, (int)v); },
|
||||
(s) => { return (float)s.m_maxSubSteps; },
|
||||
(s,p,l,v) => { s.m_maxSubSteps = (int)v; } ),
|
||||
new ParameterDefn("FixedTimeStep", "In simulation step, seconds of one substep (1/60)",
|
||||
1f / 60f,
|
||||
(s,cf,p,v) => { s.m_fixedTimeStep = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)s.m_fixedTimeStep; },
|
||||
(s,p,l,v) => { s.m_fixedTimeStep = v; } ),
|
||||
new ParameterDefn("MaxCollisionsPerFrame", "Max collisions returned at end of each frame",
|
||||
2048f,
|
||||
(s,cf,p,v) => { s.m_maxCollisionsPerFrame = cf.GetInt(p, (int)v); },
|
||||
(s) => { return (float)s.m_maxCollisionsPerFrame; },
|
||||
(s,p,l,v) => { s.m_maxCollisionsPerFrame = (int)v; } ),
|
||||
new ParameterDefn("MaxUpdatesPerFrame", "Max updates returned at end of each frame",
|
||||
8000f,
|
||||
(s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); },
|
||||
(s) => { return (float)s.m_maxUpdatesPerFrame; },
|
||||
(s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
|
||||
new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step",
|
||||
500f,
|
||||
(s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); },
|
||||
(s) => { return (float)s.m_taintsToProcessPerStep; },
|
||||
(s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ),
|
||||
new ParameterDefn("MinObjectMass", "Minimum object mass (0.0001)",
|
||||
0.0001f,
|
||||
(s,cf,p,v) => { MinimumObjectMass = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)MinimumObjectMass; },
|
||||
(s,p,l,v) => { MinimumObjectMass = v; } ),
|
||||
new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)",
|
||||
10000.01f,
|
||||
(s,cf,p,v) => { MaximumObjectMass = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)MaximumObjectMass; },
|
||||
(s,p,l,v) => { MaximumObjectMass = v; } ),
|
||||
|
||||
new ParameterDefn("PID_D", "Derivitive factor for motion smoothing",
|
||||
2200f,
|
||||
(s,cf,p,v) => { PID_D = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)PID_D; },
|
||||
(s,p,l,v) => { PID_D = v; } ),
|
||||
new ParameterDefn("PID_P", "Parameteric factor for motion smoothing",
|
||||
900f,
|
||||
(s,cf,p,v) => { PID_P = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)PID_P; },
|
||||
(s,p,l,v) => { PID_P = v; } ),
|
||||
|
||||
new ParameterDefn("DefaultFriction", "Friction factor used on new objects",
|
||||
0.2f,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].defaultFriction = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].defaultFriction; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].defaultFriction = v; } ),
|
||||
new ParameterDefn("DefaultDensity", "Density for new objects" ,
|
||||
10.000006836f, // Aluminum g/cm3
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].defaultDensity = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].defaultDensity; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].defaultDensity = v; } ),
|
||||
new ParameterDefn("DefaultRestitution", "Bouncyness of an object" ,
|
||||
0f,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].defaultRestitution = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].defaultRestitution; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].defaultRestitution = v; } ),
|
||||
new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)",
|
||||
0.04f,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].collisionMargin = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].collisionMargin; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].collisionMargin = v; } ),
|
||||
new ParameterDefn("Gravity", "Vertical force of gravity (negative means down)",
|
||||
-9.80665f,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].gravity = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].gravity; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{s.UnmanagedParams[0].gravity=x;}, p, PhysParameterEntry.APPLY_TO_NONE, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ),
|
||||
|
||||
|
||||
new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)",
|
||||
0f,
|
||||
(s,cf,p,v) => { LinearDamping = cf.GetFloat(p, v); },
|
||||
(s) => { return LinearDamping; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{LinearDamping=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, v, AngularDamping); } ),
|
||||
new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)",
|
||||
0f,
|
||||
(s,cf,p,v) => { AngularDamping = cf.GetFloat(p, v); },
|
||||
(s) => { return AngularDamping; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AngularDamping=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, LinearDamping, v); } ),
|
||||
new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static",
|
||||
0.2f,
|
||||
(s,cf,p,v) => { DeactivationTime = cf.GetFloat(p, v); },
|
||||
(s) => { return DeactivationTime; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{DeactivationTime=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.PhysBody.ptr, v); } ),
|
||||
new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static",
|
||||
0.8f,
|
||||
(s,cf,p,v) => { LinearSleepingThreshold = cf.GetFloat(p, v); },
|
||||
(s) => { return LinearSleepingThreshold; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{LinearSleepingThreshold=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
|
||||
new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static",
|
||||
1.0f,
|
||||
(s,cf,p,v) => { AngularSleepingThreshold = cf.GetFloat(p, v); },
|
||||
(s) => { return AngularSleepingThreshold; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AngularSleepingThreshold=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
|
||||
new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" ,
|
||||
0f, // set to zero to disable
|
||||
(s,cf,p,v) => { CcdMotionThreshold = cf.GetFloat(p, v); },
|
||||
(s) => { return CcdMotionThreshold; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdMotionThreshold=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.PhysBody.ptr, v); } ),
|
||||
new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" ,
|
||||
0f,
|
||||
(s,cf,p,v) => { CcdSweptSphereRadius = cf.GetFloat(p, v); },
|
||||
(s) => { return CcdSweptSphereRadius; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdSweptSphereRadius=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetCcdSweptSphereRadius2(o.PhysBody.ptr, v); } ),
|
||||
new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" ,
|
||||
0.1f,
|
||||
(s,cf,p,v) => { ContactProcessingThreshold = cf.GetFloat(p, v); },
|
||||
(s) => { return ContactProcessingThreshold; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{ContactProcessingThreshold=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ),
|
||||
|
||||
new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)",
|
||||
(float)BSTerrainPhys.TerrainImplementation.Heightmap,
|
||||
(s,cf,p,v) => { TerrainImplementation = cf.GetFloat(p,v); },
|
||||
(s) => { return TerrainImplementation; },
|
||||
(s,p,l,v) => { TerrainImplementation = v; } ),
|
||||
new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
|
||||
0.3f,
|
||||
(s,cf,p,v) => { TerrainFriction = cf.GetFloat(p, v); },
|
||||
(s) => { return TerrainFriction; },
|
||||
(s,p,l,v) => { TerrainFriction = v; /* TODO: set on real terrain */} ),
|
||||
new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" ,
|
||||
0.8f,
|
||||
(s,cf,p,v) => { TerrainHitFraction = cf.GetFloat(p, v); },
|
||||
(s) => { return TerrainHitFraction; },
|
||||
(s,p,l,v) => { TerrainHitFraction = v; /* TODO: set on real terrain */ } ),
|
||||
new ParameterDefn("TerrainRestitution", "Bouncyness" ,
|
||||
0f,
|
||||
(s,cf,p,v) => { TerrainRestitution = cf.GetFloat(p, v); },
|
||||
(s) => { return TerrainRestitution; },
|
||||
(s,p,l,v) => { TerrainRestitution = v; /* TODO: set on real terrain */ } ),
|
||||
new ParameterDefn("TerrainCollisionMargin", "Margin where collision checking starts" ,
|
||||
0.04f,
|
||||
(s,cf,p,v) => { TerrainCollisionMargin = cf.GetFloat(p, v); },
|
||||
(s) => { return TerrainCollisionMargin; },
|
||||
(s,p,l,v) => { TerrainCollisionMargin = v; /* TODO: set on real terrain */ } ),
|
||||
|
||||
new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
|
||||
0.2f,
|
||||
(s,cf,p,v) => { AvatarFriction = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarFriction; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarFriction=x;}, p, l, v); } ),
|
||||
new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
|
||||
10.0f,
|
||||
(s,cf,p,v) => { AvatarStandingFriction = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarStandingFriction; },
|
||||
(s,p,l,v) => { AvatarStandingFriction = v; } ),
|
||||
new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.",
|
||||
3.5f,
|
||||
(s,cf,p,v) => { AvatarDensity = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarDensity; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarDensity=x;}, p, l, v); } ),
|
||||
new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.",
|
||||
0f,
|
||||
(s,cf,p,v) => { AvatarRestitution = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarRestitution; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarRestitution=x;}, p, l, v); } ),
|
||||
new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule",
|
||||
0.6f,
|
||||
(s,cf,p,v) => { AvatarCapsuleWidth = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarCapsuleWidth; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleWidth=x;}, p, l, v); } ),
|
||||
new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule",
|
||||
0.45f,
|
||||
(s,cf,p,v) => { AvatarCapsuleDepth = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarCapsuleDepth; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleDepth=x;}, p, l, v); } ),
|
||||
new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar",
|
||||
1.5f,
|
||||
(s,cf,p,v) => { AvatarCapsuleHeight = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarCapsuleHeight; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleHeight=x;}, p, l, v); } ),
|
||||
new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions",
|
||||
0.1f,
|
||||
(s,cf,p,v) => { AvatarContactProcessingThreshold = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarContactProcessingThreshold; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarContactProcessingThreshold=x;}, p, l, v); } ),
|
||||
|
||||
new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)",
|
||||
0.95f,
|
||||
(s,cf,p,v) => { VehicleAngularDamping = cf.GetFloat(p, v); },
|
||||
(s) => { return VehicleAngularDamping; },
|
||||
(s,p,l,v) => { VehicleAngularDamping = v; } ),
|
||||
|
||||
new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)",
|
||||
0f,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].maxPersistantManifoldPoolSize; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].maxPersistantManifoldPoolSize = v; } ),
|
||||
new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)",
|
||||
0f,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize = v; } ),
|
||||
new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count",
|
||||
ConfigurationParameters.numericFalse,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||
(s) => { return s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = v; } ),
|
||||
new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step",
|
||||
ConfigurationParameters.numericFalse,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].shouldForceUpdateAllAabbs = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||
(s) => { return s.UnmanagedParams[0].shouldForceUpdateAllAabbs; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].shouldForceUpdateAllAabbs = v; } ),
|
||||
new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction",
|
||||
ConfigurationParameters.numericTrue,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].shouldRandomizeSolverOrder = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||
(s) => { return s.UnmanagedParams[0].shouldRandomizeSolverOrder; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].shouldRandomizeSolverOrder = v; } ),
|
||||
new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands",
|
||||
ConfigurationParameters.numericTrue,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].shouldSplitSimulationIslands = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||
(s) => { return s.UnmanagedParams[0].shouldSplitSimulationIslands; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].shouldSplitSimulationIslands = v; } ),
|
||||
new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching",
|
||||
ConfigurationParameters.numericFalse,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||
(s) => { return s.UnmanagedParams[0].shouldEnableFrictionCaching; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = v; } ),
|
||||
new ParameterDefn("NumberOfSolverIterations", "Number of internal iterations (0 means default)",
|
||||
0f, // zero says use Bullet default
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].numberOfSolverIterations = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].numberOfSolverIterations; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].numberOfSolverIterations = v; } ),
|
||||
|
||||
new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)",
|
||||
(float)BSLinkset.LinksetImplementation.Compound,
|
||||
(s,cf,p,v) => { LinksetImplementation = cf.GetFloat(p,v); },
|
||||
(s) => { return LinksetImplementation; },
|
||||
(s,p,l,v) => { LinksetImplementation = v; } ),
|
||||
new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.",
|
||||
ConfigurationParameters.numericFalse,
|
||||
(s,cf,p,v) => { LinkConstraintUseFrameOffset = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||
(s) => { return LinkConstraintUseFrameOffset; },
|
||||
(s,p,l,v) => { LinkConstraintUseFrameOffset = v; } ),
|
||||
new ParameterDefn("LinkConstraintEnableTransMotor", "Whether to enable translational motor on linkset constraints",
|
||||
ConfigurationParameters.numericTrue,
|
||||
(s,cf,p,v) => { LinkConstraintEnableTransMotor = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||
(s) => { return LinkConstraintEnableTransMotor; },
|
||||
(s,p,l,v) => { LinkConstraintEnableTransMotor = v; } ),
|
||||
new ParameterDefn("LinkConstraintTransMotorMaxVel", "Maximum velocity to be applied by translational motor in linkset constraints",
|
||||
5.0f,
|
||||
(s,cf,p,v) => { LinkConstraintTransMotorMaxVel = cf.GetFloat(p, v); },
|
||||
(s) => { return LinkConstraintTransMotorMaxVel; },
|
||||
(s,p,l,v) => { LinkConstraintTransMotorMaxVel = v; } ),
|
||||
new ParameterDefn("LinkConstraintTransMotorMaxForce", "Maximum force to be applied by translational motor in linkset constraints",
|
||||
0.1f,
|
||||
(s,cf,p,v) => { LinkConstraintTransMotorMaxForce = cf.GetFloat(p, v); },
|
||||
(s) => { return LinkConstraintTransMotorMaxForce; },
|
||||
(s,p,l,v) => { LinkConstraintTransMotorMaxForce = v; } ),
|
||||
new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1",
|
||||
0.1f,
|
||||
(s,cf,p,v) => { LinkConstraintCFM = cf.GetFloat(p, v); },
|
||||
(s) => { return LinkConstraintCFM; },
|
||||
(s,p,l,v) => { LinkConstraintCFM = v; } ),
|
||||
new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2",
|
||||
0.1f,
|
||||
(s,cf,p,v) => { LinkConstraintERP = cf.GetFloat(p, v); },
|
||||
(s) => { return LinkConstraintERP; },
|
||||
(s,p,l,v) => { LinkConstraintERP = v; } ),
|
||||
new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)",
|
||||
40,
|
||||
(s,cf,p,v) => { LinkConstraintSolverIterations = cf.GetFloat(p, v); },
|
||||
(s) => { return LinkConstraintSolverIterations; },
|
||||
(s,p,l,v) => { LinkConstraintSolverIterations = v; } ),
|
||||
|
||||
new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)",
|
||||
0f,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].physicsLoggingFrames = cf.GetInt(p, (int)v); },
|
||||
(s) => { return (float)s.UnmanagedParams[0].physicsLoggingFrames; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].physicsLoggingFrames = (int)v; } ),
|
||||
};
|
||||
|
||||
// Convert a boolean to our numeric true and false values
|
||||
public static float NumericBool(bool b)
|
||||
{
|
||||
return (b ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse);
|
||||
}
|
||||
|
||||
// Convert numeric true and false values to a boolean
|
||||
public static bool BoolNumeric(float b)
|
||||
{
|
||||
return (b == ConfigurationParameters.numericTrue ? true : false);
|
||||
}
|
||||
|
||||
// Search through the parameter definitions and return the matching
|
||||
// ParameterDefn structure.
|
||||
// Case does not matter as names are compared after converting to lower case.
|
||||
// Returns 'false' if the parameter is not found.
|
||||
internal static bool TryGetParameter(string paramName, out ParameterDefn defn)
|
||||
{
|
||||
bool ret = false;
|
||||
ParameterDefn foundDefn = new ParameterDefn();
|
||||
string pName = paramName.ToLower();
|
||||
|
||||
foreach (ParameterDefn parm in ParameterDefinitions)
|
||||
{
|
||||
if (pName == parm.name.ToLower())
|
||||
{
|
||||
foundDefn = parm;
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
defn = foundDefn;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Pass through the settable parameters and set the default values
|
||||
internal static void SetParameterDefaultValues(BSScene physicsScene)
|
||||
{
|
||||
foreach (ParameterDefn parm in ParameterDefinitions)
|
||||
{
|
||||
parm.setter(physicsScene, parm.name, PhysParameterEntry.APPLY_TO_NONE, parm.defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Get user set values out of the ini file.
|
||||
internal static void SetParameterConfigurationValues(BSScene physicsScene, IConfig cfg)
|
||||
{
|
||||
foreach (ParameterDefn parm in ParameterDefinitions)
|
||||
{
|
||||
parm.userParam(physicsScene, cfg, parm.name, parm.defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
internal static PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1];
|
||||
|
||||
// This creates an array in the correct format for returning the list of
|
||||
// parameters. This is used by the 'list' option of the 'physics' command.
|
||||
internal static void BuildParameterTable()
|
||||
{
|
||||
if (SettableParameters.Length < ParameterDefinitions.Length)
|
||||
{
|
||||
List<PhysParameterEntry> entries = new List<PhysParameterEntry>();
|
||||
for (int ii = 0; ii < ParameterDefinitions.Length; ii++)
|
||||
{
|
||||
ParameterDefn pd = ParameterDefinitions[ii];
|
||||
entries.Add(new PhysParameterEntry(pd.name, pd.desc));
|
||||
}
|
||||
|
||||
// make the list in alphabetical order for estetic reasons
|
||||
entries.Sort(delegate(PhysParameterEntry ppe1, PhysParameterEntry ppe2)
|
||||
{
|
||||
return ppe1.name.CompareTo(ppe2.name);
|
||||
});
|
||||
|
||||
SettableParameters = entries.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -1,346 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using OMV = OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
/*
|
||||
* Class to wrap all objects.
|
||||
* The rest of BulletSim doesn't need to keep checking for avatars or prims
|
||||
* unless the difference is significant.
|
||||
*
|
||||
* Variables in the physicsl objects are in three forms:
|
||||
* VariableName: used by the simulator and performs taint operations, etc
|
||||
* RawVariableName: direct reference to the BulletSim storage for the variable value
|
||||
* ForceVariableName: direct reference (store and fetch) to the value in the physics engine.
|
||||
* The last two (and certainly the last one) should be referenced only in taint-time.
|
||||
*/
|
||||
|
||||
/*
|
||||
* As of 20121221, the following are the call sequences (going down) for different script physical functions:
|
||||
* llApplyImpulse llApplyRotImpulse llSetTorque llSetForce
|
||||
* SOP.ApplyImpulse SOP.ApplyAngularImpulse SOP.SetAngularImpulse SOP.SetForce
|
||||
* SOG.ApplyImpulse SOG.ApplyAngularImpulse SOG.SetAngularImpulse
|
||||
* PA.AddForce PA.AddAngularForce PA.Torque = v PA.Force = v
|
||||
* BS.ApplyCentralForce BS.ApplyTorque
|
||||
*/
|
||||
|
||||
public abstract class BSPhysObject : PhysicsActor
|
||||
{
|
||||
protected BSPhysObject()
|
||||
{
|
||||
}
|
||||
protected BSPhysObject(BSScene parentScene, uint localID, string name, string typeName)
|
||||
{
|
||||
PhysicsScene = parentScene;
|
||||
LocalID = localID;
|
||||
PhysObjectName = name;
|
||||
TypeName = typeName;
|
||||
|
||||
Linkset = BSLinkset.Factory(PhysicsScene, this);
|
||||
LastAssetBuildFailed = false;
|
||||
|
||||
// Default material type
|
||||
Material = MaterialAttributes.Material.Wood;
|
||||
|
||||
CollisionCollection = new CollisionEventUpdate();
|
||||
SubscribedEventsMs = 0;
|
||||
CollidingStep = 0;
|
||||
CollidingGroundStep = 0;
|
||||
}
|
||||
|
||||
// Tell the object to clean up.
|
||||
public virtual void Destroy()
|
||||
{
|
||||
UnRegisterAllPreStepActions();
|
||||
}
|
||||
|
||||
public BSScene PhysicsScene { get; protected set; }
|
||||
// public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor
|
||||
public string PhysObjectName { get; protected set; }
|
||||
public string TypeName { get; protected set; }
|
||||
|
||||
public BSLinkset Linkset { get; set; }
|
||||
public BSLinksetInfo LinksetInfo { get; set; }
|
||||
|
||||
// Return the object mass without calculating it or having side effects
|
||||
public abstract float RawMass { get; }
|
||||
// Set the raw mass but also update physical mass properties (inertia, ...)
|
||||
// 'inWorld' true if the object has already been added to the dynamic world.
|
||||
public abstract void UpdatePhysicalMassProperties(float mass, bool inWorld);
|
||||
|
||||
// The last value calculated for the prim's inertia
|
||||
public OMV.Vector3 Inertia { get; set; }
|
||||
|
||||
// Reference to the physical body (btCollisionObject) of this object
|
||||
public BulletBody PhysBody;
|
||||
// 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 objects base shape information. Null if not a prim type shape.
|
||||
public PrimitiveBaseShape BaseShape { get; protected set; }
|
||||
// Some types of objects have preferred physical representations.
|
||||
// Returns SHAPE_UNKNOWN if there is no preference.
|
||||
public virtual BSPhysicsShapeType PreferredPhysicalShape
|
||||
{
|
||||
get { return BSPhysicsShapeType.SHAPE_UNKNOWN; }
|
||||
}
|
||||
|
||||
// When the physical properties are updated, an EntityProperty holds the update values.
|
||||
// Keep the current and last EntityProperties to enable computation of differences
|
||||
// between the current update and the previous values.
|
||||
public EntityProperties CurrentEntityProperties { get; set; }
|
||||
public EntityProperties LastEntityProperties { get; set; }
|
||||
|
||||
public virtual OMV.Vector3 Scale { get; set; }
|
||||
public abstract bool IsSolid { get; }
|
||||
public abstract bool IsStatic { get; }
|
||||
|
||||
// Materialness
|
||||
public MaterialAttributes.Material Material { get; private set; }
|
||||
public override void SetMaterial(int material)
|
||||
{
|
||||
Material = (MaterialAttributes.Material)material;
|
||||
}
|
||||
|
||||
// Stop all physical motion.
|
||||
public abstract void ZeroMotion(bool inTaintTime);
|
||||
public abstract void ZeroAngularMotion(bool inTaintTime);
|
||||
|
||||
// Step the vehicle simulation for this object. A NOOP if the vehicle was not configured.
|
||||
public virtual void StepVehicle(float timeStep) { }
|
||||
|
||||
// Update the physical location and motion of the object. Called with data from Bullet.
|
||||
public abstract void UpdateProperties(EntityProperties entprop);
|
||||
|
||||
public abstract OMV.Vector3 RawPosition { get; set; }
|
||||
public abstract OMV.Vector3 ForcePosition { get; set; }
|
||||
|
||||
public abstract OMV.Quaternion RawOrientation { get; set; }
|
||||
public abstract OMV.Quaternion ForceOrientation { get; set; }
|
||||
|
||||
// The system is telling us the velocity it wants to move at.
|
||||
// protected OMV.Vector3 m_targetVelocity; // use the definition in PhysicsActor
|
||||
public override OMV.Vector3 TargetVelocity
|
||||
{
|
||||
get { return m_targetVelocity; }
|
||||
set
|
||||
{
|
||||
m_targetVelocity = value;
|
||||
Velocity = value;
|
||||
}
|
||||
}
|
||||
public abstract OMV.Vector3 ForceVelocity { get; set; }
|
||||
|
||||
public abstract OMV.Vector3 ForceRotationalVelocity { get; set; }
|
||||
|
||||
public abstract float ForceBuoyancy { get; set; }
|
||||
|
||||
public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
|
||||
|
||||
#region Collisions
|
||||
|
||||
// Requested number of milliseconds between collision events. Zero means disabled.
|
||||
protected int SubscribedEventsMs { get; set; }
|
||||
// Given subscription, the time that a collision may be passed up
|
||||
protected int NextCollisionOkTime { get; set; }
|
||||
// The simulation step that last had a collision
|
||||
protected long CollidingStep { get; set; }
|
||||
// The simulation step that last had a collision with the ground
|
||||
protected long CollidingGroundStep { get; set; }
|
||||
// The collision flags we think are set in Bullet
|
||||
protected CollisionFlags CurrentCollisionFlags { get; set; }
|
||||
|
||||
// The collisions that have been collected this tick
|
||||
protected CollisionEventUpdate CollisionCollection;
|
||||
|
||||
// The simulation step is telling this object about a collision.
|
||||
// Return 'true' if a collision was processed and should be sent up.
|
||||
// Called at taint time from within the Step() function
|
||||
public virtual bool Collide(uint collidingWith, BSPhysObject collidee,
|
||||
OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
// The following lines make IsColliding() and IsCollidingGround() work
|
||||
CollidingStep = PhysicsScene.SimulationStep;
|
||||
if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID)
|
||||
{
|
||||
CollidingGroundStep = PhysicsScene.SimulationStep;
|
||||
}
|
||||
|
||||
// prims in the same linkset cannot collide with each other
|
||||
if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
// if someone has subscribed for collision events....
|
||||
if (SubscribedEvents()) {
|
||||
CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
|
||||
DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}",
|
||||
LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth);
|
||||
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Send the collected collisions into the simulator.
|
||||
// Called at taint time from within the Step() function thus no locking problems
|
||||
// with CollisionCollection and ObjectsWithNoMoreCollisions.
|
||||
// Return 'true' if there were some actual collisions passed up
|
||||
public virtual bool SendCollisions()
|
||||
{
|
||||
bool ret = true;
|
||||
// If the 'no collision' call, force it to happen right now so quick collision_end
|
||||
bool force = (CollisionCollection.Count == 0);
|
||||
|
||||
// throttle the collisions to the number of milliseconds specified in the subscription
|
||||
if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime))
|
||||
{
|
||||
NextCollisionOkTime = PhysicsScene.SimulationNowTime + SubscribedEventsMs;
|
||||
|
||||
// We are called if we previously had collisions. If there are no collisions
|
||||
// this time, send up one last empty event so OpenSim can sense collision end.
|
||||
if (CollisionCollection.Count == 0)
|
||||
{
|
||||
// If I have no collisions this time, remove me from the list of objects with collisions.
|
||||
ret = false;
|
||||
}
|
||||
|
||||
// DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
|
||||
base.SendCollisionUpdate(CollisionCollection);
|
||||
|
||||
// The CollisionCollection instance is passed around in the simulator.
|
||||
// Make sure we don't have a handle to that one and that a new one is used for next time.
|
||||
// This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here,
|
||||
// a race condition is created for the other users of this instance.
|
||||
CollisionCollection = new CollisionEventUpdate();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Subscribe for collision events.
|
||||
// Parameter is the millisecond rate the caller wishes collision events to occur.
|
||||
public override void SubscribeEvents(int ms) {
|
||||
// DetailLog("{0},{1}.SubscribeEvents,subscribing,ms={2}", LocalID, TypeName, ms);
|
||||
SubscribedEventsMs = ms;
|
||||
if (ms > 0)
|
||||
{
|
||||
// make sure first collision happens
|
||||
NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs);
|
||||
|
||||
PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate()
|
||||
{
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subscribing for zero or less is the same as unsubscribing
|
||||
UnSubscribeEvents();
|
||||
}
|
||||
}
|
||||
public override void UnSubscribeEvents() {
|
||||
// DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName);
|
||||
SubscribedEventsMs = 0;
|
||||
PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate()
|
||||
{
|
||||
// Make sure there is a body there because sometimes destruction happens in an un-ideal order.
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||
});
|
||||
}
|
||||
// Return 'true' if the simulator wants collision events
|
||||
public override bool SubscribedEvents() {
|
||||
return (SubscribedEventsMs > 0);
|
||||
}
|
||||
|
||||
#endregion // Collisions
|
||||
|
||||
#region Per Simulation Step actions
|
||||
// There are some actions that must be performed for a physical object before each simulation step.
|
||||
// These actions are optional so, rather than scanning all the physical objects and asking them
|
||||
// if they have anything to do, a physical object registers for an event call before the step is performed.
|
||||
// This bookkeeping makes it easy to add, remove and clean up after all these registrations.
|
||||
private Dictionary<string, BSScene.PreStepAction> RegisteredActions = new Dictionary<string, BSScene.PreStepAction>();
|
||||
protected void RegisterPreStepAction(string op, uint id, BSScene.PreStepAction actn)
|
||||
{
|
||||
string identifier = op + "-" + id.ToString();
|
||||
RegisteredActions[identifier] = actn;
|
||||
PhysicsScene.BeforeStep += actn;
|
||||
DetailLog("{0},BSPhysObject.RegisterPreStepAction,id={1}", LocalID, identifier);
|
||||
}
|
||||
|
||||
// Unregister a pre step action. Safe to call if the action has not been registered.
|
||||
protected void UnRegisterPreStepAction(string op, uint id)
|
||||
{
|
||||
string identifier = op + "-" + id.ToString();
|
||||
bool removed = false;
|
||||
if (RegisteredActions.ContainsKey(identifier))
|
||||
{
|
||||
PhysicsScene.BeforeStep -= RegisteredActions[identifier];
|
||||
RegisteredActions.Remove(identifier);
|
||||
removed = true;
|
||||
}
|
||||
DetailLog("{0},BSPhysObject.UnRegisterPreStepAction,id={1},removed={2}", LocalID, identifier, removed);
|
||||
}
|
||||
|
||||
protected void UnRegisterAllPreStepActions()
|
||||
{
|
||||
foreach (KeyValuePair<string, BSScene.PreStepAction> kvp in RegisteredActions)
|
||||
{
|
||||
PhysicsScene.BeforeStep -= kvp.Value;
|
||||
}
|
||||
RegisteredActions.Clear();
|
||||
DetailLog("{0},BSPhysObject.UnRegisterAllPreStepActions,", LocalID);
|
||||
}
|
||||
|
||||
|
||||
#endregion // Per Simulation Step actions
|
||||
|
||||
// High performance detailed logging routine used by the physical objects.
|
||||
protected void DetailLog(string msg, params Object[] args)
|
||||
{
|
||||
if (PhysicsScene.PhysicsLogging.Enabled)
|
||||
PhysicsScene.DetailLog(msg, args);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Entry for a port of Bullet (http://bulletphysics.org/) to OpenSim.
|
||||
/// This module interfaces to an unmanaged C++ library which makes the
|
||||
/// actual calls into the Bullet physics engine.
|
||||
/// The unmanaged library is found in opensim-libs::trunk/unmanaged/BulletSim/.
|
||||
/// The unmanaged library is compiled and linked statically with Bullet
|
||||
/// to create BulletSim.dll and libBulletSim.so (for both 32 and 64 bit).
|
||||
/// </summary>
|
||||
public class BSPlugin : IPhysicsPlugin
|
||||
{
|
||||
//private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private BSScene _mScene;
|
||||
|
||||
public BSPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Init()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public PhysicsScene GetScene(String sceneIdentifier)
|
||||
{
|
||||
if (_mScene == null)
|
||||
{
|
||||
|
||||
// If not Windows, loading is performed by the
|
||||
// Mono loader as specified in
|
||||
// "bin/Physics/OpenSim.Region.Physics.BulletSNPlugin.dll.config".
|
||||
|
||||
_mScene = new BSScene(sceneIdentifier);
|
||||
}
|
||||
return (_mScene);
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return ("BulletSimN");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,957 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework;
|
||||
using OpenSim.Region.CoreModules;
|
||||
using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
using Nini.Config;
|
||||
using log4net;
|
||||
using OpenMetaverse;
|
||||
|
||||
// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
|
||||
// Based on material, set density and friction
|
||||
// More efficient memory usage when passing hull information from BSPrim to BulletSim
|
||||
// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
|
||||
// Implement LockAngularMotion
|
||||
// Add PID movement operations. What does ScenePresence.MoveToTarget do?
|
||||
// Check terrain size. 128 or 127?
|
||||
// Raycast
|
||||
//
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public sealed class BSScene : PhysicsScene, IPhysicsParameters
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
private static readonly string LogHeader = "[BULLETS SCENE]";
|
||||
|
||||
// The name of the region we're working for.
|
||||
public string RegionName { get; private set; }
|
||||
|
||||
public string BulletSimVersion = "?";
|
||||
|
||||
public Dictionary<uint, BSPhysObject> PhysObjects;
|
||||
public BSShapeCollection Shapes;
|
||||
|
||||
// Keeping track of the objects with collisions so we can report begin and end of a collision
|
||||
public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>();
|
||||
public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>();
|
||||
// Keep track of all the avatars so we can send them a collision event
|
||||
// every tick so OpenSim will update its animation.
|
||||
private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>();
|
||||
|
||||
// let my minuions use my logger
|
||||
public ILog Logger { get { return m_log; } }
|
||||
|
||||
public IMesher mesher;
|
||||
public uint WorldID { get; private set; }
|
||||
public BulletWorld World { get; private set; }
|
||||
|
||||
// All the constraints that have been allocated in this instance.
|
||||
public BSConstraintCollection Constraints { get; private set; }
|
||||
|
||||
// Simulation parameters
|
||||
internal int m_maxSubSteps;
|
||||
internal float m_fixedTimeStep;
|
||||
internal long m_simulationStep = 0;
|
||||
public long SimulationStep { get { return m_simulationStep; } }
|
||||
internal int m_taintsToProcessPerStep;
|
||||
internal float LastTimeStep { get; private set; }
|
||||
|
||||
// Physical objects can register for prestep or poststep events
|
||||
public delegate void PreStepAction(float timeStep);
|
||||
public delegate void PostStepAction(float timeStep);
|
||||
public event PreStepAction BeforeStep;
|
||||
public event PreStepAction AfterStep;
|
||||
|
||||
// A value of the time now so all the collision and update routines do not have to get their own
|
||||
// Set to 'now' just before all the prims and actors are called for collisions and updates
|
||||
public int SimulationNowTime { get; private set; }
|
||||
|
||||
// True if initialized and ready to do simulation steps
|
||||
private bool m_initialized = false;
|
||||
|
||||
// Flag which is true when processing taints.
|
||||
// Not guaranteed to be correct all the time (don't depend on this) but good for debugging.
|
||||
public bool InTaintTime { get; private set; }
|
||||
|
||||
// Pinned memory used to pass step information between managed and unmanaged
|
||||
internal int m_maxCollisionsPerFrame;
|
||||
private List<BulletXNA.CollisionDesc> m_collisionArray;
|
||||
//private GCHandle m_collisionArrayPinnedHandle;
|
||||
|
||||
internal int m_maxUpdatesPerFrame;
|
||||
private List<BulletXNA.EntityProperties> m_updateArray;
|
||||
//private GCHandle m_updateArrayPinnedHandle;
|
||||
|
||||
|
||||
public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero
|
||||
public const uint GROUNDPLANE_ID = 1;
|
||||
public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
|
||||
|
||||
public float SimpleWaterLevel { get; set; }
|
||||
public BSTerrainManager TerrainManager { get; private set; }
|
||||
|
||||
public ConfigurationParameters Params
|
||||
{
|
||||
get { return UnmanagedParams[0]; }
|
||||
}
|
||||
public Vector3 DefaultGravity
|
||||
{
|
||||
get { return new Vector3(0f, 0f, Params.gravity); }
|
||||
}
|
||||
// Just the Z value of the gravity
|
||||
public float DefaultGravityZ
|
||||
{
|
||||
get { return Params.gravity; }
|
||||
}
|
||||
|
||||
// When functions in the unmanaged code must be called, it is only
|
||||
// done at a known time just before the simulation step. The taint
|
||||
// system saves all these function calls and executes them in
|
||||
// order before the simulation.
|
||||
public delegate void TaintCallback();
|
||||
private struct TaintCallbackEntry
|
||||
{
|
||||
public String ident;
|
||||
public TaintCallback callback;
|
||||
public TaintCallbackEntry(string i, TaintCallback c)
|
||||
{
|
||||
ident = i;
|
||||
callback = c;
|
||||
}
|
||||
}
|
||||
private Object _taintLock = new Object(); // lock for using the next object
|
||||
private List<TaintCallbackEntry> _taintOperations;
|
||||
private Dictionary<string, TaintCallbackEntry> _postTaintOperations;
|
||||
private List<TaintCallbackEntry> _postStepOperations;
|
||||
|
||||
// A pointer to an instance if this structure is passed to the C++ code
|
||||
// Used to pass basic configuration values to the unmanaged code.
|
||||
internal ConfigurationParameters[] UnmanagedParams;
|
||||
//GCHandle m_paramsHandle;
|
||||
|
||||
// Handle to the callback used by the unmanaged code to call into the managed code.
|
||||
// Used for debug logging.
|
||||
// Need to store the handle in a persistant variable so it won't be freed.
|
||||
private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle;
|
||||
|
||||
// Sometimes you just have to log everything.
|
||||
public Logging.LogWriter PhysicsLogging;
|
||||
private bool m_physicsLoggingEnabled;
|
||||
private string m_physicsLoggingDir;
|
||||
private string m_physicsLoggingPrefix;
|
||||
private int m_physicsLoggingFileMinutes;
|
||||
private bool m_physicsLoggingDoFlush;
|
||||
// 'true' of the vehicle code is to log lots of details
|
||||
public bool VehicleLoggingEnabled { get; private set; }
|
||||
public bool VehiclePhysicalLoggingEnabled { get; private set; }
|
||||
|
||||
#region Construction and Initialization
|
||||
public BSScene(string identifier)
|
||||
{
|
||||
m_initialized = false;
|
||||
// we are passed the name of the region we're working for.
|
||||
RegionName = identifier;
|
||||
}
|
||||
|
||||
public override void Initialise(IMesher meshmerizer, IConfigSource config)
|
||||
{
|
||||
mesher = meshmerizer;
|
||||
_taintOperations = new List<TaintCallbackEntry>();
|
||||
_postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
|
||||
_postStepOperations = new List<TaintCallbackEntry>();
|
||||
PhysObjects = new Dictionary<uint, BSPhysObject>();
|
||||
Shapes = new BSShapeCollection(this);
|
||||
|
||||
// Allocate pinned memory to pass parameters.
|
||||
UnmanagedParams = new ConfigurationParameters[1];
|
||||
//m_paramsHandle = GCHandle.Alloc(UnmanagedParams, GCHandleType.Pinned);
|
||||
|
||||
// Set default values for physics parameters plus any overrides from the ini file
|
||||
GetInitialParameterValues(config);
|
||||
|
||||
// allocate more pinned memory close to the above in an attempt to get the memory all together
|
||||
m_collisionArray = new List<BulletXNA.CollisionDesc>();
|
||||
//m_collisionArrayPinnedHandle = GCHandle.Alloc(m_collisionArray, GCHandleType.Pinned);
|
||||
m_updateArray = new List<BulletXNA.EntityProperties>();
|
||||
//m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned);
|
||||
|
||||
// Enable very detailed logging.
|
||||
// By creating an empty logger when not logging, the log message invocation code
|
||||
// can be left in and every call doesn't have to check for null.
|
||||
if (m_physicsLoggingEnabled)
|
||||
{
|
||||
PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes);
|
||||
PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output error messages.
|
||||
}
|
||||
else
|
||||
{
|
||||
PhysicsLogging = new Logging.LogWriter();
|
||||
}
|
||||
|
||||
// If Debug logging level, enable logging from the unmanaged code
|
||||
m_DebugLogCallbackHandle = null;
|
||||
if (m_log.IsDebugEnabled || PhysicsLogging.Enabled)
|
||||
{
|
||||
m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader);
|
||||
if (PhysicsLogging.Enabled)
|
||||
// The handle is saved in a variable to make sure it doesn't get freed after this call
|
||||
m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLoggerPhysLog);
|
||||
else
|
||||
m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger);
|
||||
}
|
||||
|
||||
// Get the version of the DLL
|
||||
// TODO: this doesn't work yet. Something wrong with marshaling the returned string.
|
||||
// BulletSimVersion = BulletSimAPI.GetVersion();
|
||||
// m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion);
|
||||
|
||||
// The bounding box for the simulated world. The origin is 0,0,0 unless we're
|
||||
// a child in a mega-region.
|
||||
// Bullet actually doesn't care about the extents of the simulated
|
||||
// area. It tracks active objects no matter where they are.
|
||||
Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
|
||||
|
||||
// m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
|
||||
|
||||
World = new BulletWorld(0, this, BulletSimAPI.Initialize2(worldExtent, UnmanagedParams,
|
||||
m_maxCollisionsPerFrame, ref m_collisionArray,
|
||||
m_maxUpdatesPerFrame,ref m_updateArray,
|
||||
m_DebugLogCallbackHandle));
|
||||
|
||||
Constraints = new BSConstraintCollection(World);
|
||||
|
||||
TerrainManager = new BSTerrainManager(this);
|
||||
TerrainManager.CreateInitialGroundPlaneAndTerrain();
|
||||
|
||||
m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation);
|
||||
|
||||
InTaintTime = false;
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
// All default parameter values are set here. There should be no values set in the
|
||||
// variable definitions.
|
||||
private void GetInitialParameterValues(IConfigSource config)
|
||||
{
|
||||
ConfigurationParameters parms = new ConfigurationParameters();
|
||||
UnmanagedParams[0] = parms;
|
||||
|
||||
BSParam.SetParameterDefaultValues(this);
|
||||
|
||||
if (config != null)
|
||||
{
|
||||
// If there are specifications in the ini file, use those values
|
||||
IConfig pConfig = config.Configs["BulletSim"];
|
||||
if (pConfig != null)
|
||||
{
|
||||
BSParam.SetParameterConfigurationValues(this, pConfig);
|
||||
|
||||
// Very detailed logging for physics debugging
|
||||
m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false);
|
||||
m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
|
||||
m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
|
||||
m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
|
||||
m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
|
||||
// Very detailed logging for vehicle debugging
|
||||
VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
|
||||
VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);
|
||||
|
||||
// Do any replacements in the parameters
|
||||
m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
|
||||
}
|
||||
|
||||
// The material characteristics.
|
||||
BSMaterials.InitializeFromDefaults(Params);
|
||||
if (pConfig != null)
|
||||
{
|
||||
// Let the user add new and interesting material property values.
|
||||
BSMaterials.InitializefromParameters(pConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A helper function that handles a true/false parameter and returns the proper float number encoding
|
||||
float ParamBoolean(IConfig config, string parmName, float deflt)
|
||||
{
|
||||
float ret = deflt;
|
||||
if (config.Contains(parmName))
|
||||
{
|
||||
ret = ConfigurationParameters.numericFalse;
|
||||
if (config.GetBoolean(parmName, false))
|
||||
{
|
||||
ret = ConfigurationParameters.numericTrue;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Called directly from unmanaged code so don't do much
|
||||
private void BulletLogger(string msg)
|
||||
{
|
||||
m_log.Debug("[BULLETS UNMANAGED]:" + msg);
|
||||
}
|
||||
|
||||
// Called directly from unmanaged code so don't do much
|
||||
private void BulletLoggerPhysLog(string msg)
|
||||
{
|
||||
DetailLog("[BULLETS UNMANAGED]:" + msg);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
// m_log.DebugFormat("{0}: Dispose()", LogHeader);
|
||||
|
||||
// make sure no stepping happens while we're deleting stuff
|
||||
m_initialized = false;
|
||||
|
||||
foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
|
||||
{
|
||||
kvp.Value.Destroy();
|
||||
}
|
||||
PhysObjects.Clear();
|
||||
|
||||
// Now that the prims are all cleaned up, there should be no constraints left
|
||||
if (Constraints != null)
|
||||
{
|
||||
Constraints.Dispose();
|
||||
Constraints = null;
|
||||
}
|
||||
|
||||
if (Shapes != null)
|
||||
{
|
||||
Shapes.Dispose();
|
||||
Shapes = null;
|
||||
}
|
||||
|
||||
if (TerrainManager != null)
|
||||
{
|
||||
TerrainManager.ReleaseGroundPlaneAndTerrain();
|
||||
TerrainManager.Dispose();
|
||||
TerrainManager = null;
|
||||
}
|
||||
|
||||
// Anything left in the unmanaged code should be cleaned out
|
||||
BulletSimAPI.Shutdown2(World.ptr);
|
||||
|
||||
// Not logging any more
|
||||
PhysicsLogging.Close();
|
||||
}
|
||||
#endregion // Construction and Initialization
|
||||
|
||||
#region Prim and Avatar addition and removal
|
||||
|
||||
public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
|
||||
{
|
||||
m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
|
||||
return null;
|
||||
}
|
||||
|
||||
public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying)
|
||||
{
|
||||
// m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName);
|
||||
|
||||
if (!m_initialized) return null;
|
||||
|
||||
BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying);
|
||||
lock (PhysObjects) PhysObjects.Add(localID, actor);
|
||||
|
||||
// TODO: Remove kludge someday.
|
||||
// We must generate a collision for avatars whether they collide or not.
|
||||
// This is required by OpenSim to update avatar animations, etc.
|
||||
lock (m_avatars) m_avatars.Add(actor);
|
||||
|
||||
return actor;
|
||||
}
|
||||
|
||||
public override void RemoveAvatar(PhysicsActor actor)
|
||||
{
|
||||
// m_log.DebugFormat("{0}: RemoveAvatar", LogHeader);
|
||||
|
||||
if (!m_initialized) return;
|
||||
|
||||
BSCharacter bsactor = actor as BSCharacter;
|
||||
if (bsactor != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (PhysObjects) PhysObjects.Remove(actor.LocalID);
|
||||
// Remove kludge someday
|
||||
lock (m_avatars) m_avatars.Remove(bsactor);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e);
|
||||
}
|
||||
bsactor.Destroy();
|
||||
// bsactor.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public override void RemovePrim(PhysicsActor prim)
|
||||
{
|
||||
if (!m_initialized) return;
|
||||
|
||||
BSPrim bsprim = prim as BSPrim;
|
||||
if (bsprim != null)
|
||||
{
|
||||
DetailLog("{0},RemovePrim,call", bsprim.LocalID);
|
||||
// m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID);
|
||||
try
|
||||
{
|
||||
lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.ErrorFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e);
|
||||
}
|
||||
bsprim.Destroy();
|
||||
// bsprim.dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.ErrorFormat("{0}: Attempt to remove prim that is not a BSPrim type.", LogHeader);
|
||||
}
|
||||
}
|
||||
|
||||
public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
|
||||
Vector3 size, Quaternion rotation, bool isPhysical, uint localID)
|
||||
{
|
||||
// m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName);
|
||||
|
||||
if (!m_initialized) return null;
|
||||
|
||||
DetailLog("{0},AddPrimShape,call", localID);
|
||||
|
||||
BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical);
|
||||
lock (PhysObjects) PhysObjects.Add(localID, prim);
|
||||
return prim;
|
||||
}
|
||||
|
||||
// This is a call from the simulator saying that some physical property has been updated.
|
||||
// The BulletSim driver senses the changing of relevant properties so this taint
|
||||
// information call is not needed.
|
||||
public override void AddPhysicsActorTaint(PhysicsActor prim) { }
|
||||
|
||||
#endregion // Prim and Avatar addition and removal
|
||||
|
||||
#region Simulation
|
||||
// Simulate one timestep
|
||||
public override float Simulate(float timeStep)
|
||||
{
|
||||
// prevent simulation until we've been initialized
|
||||
if (!m_initialized) return 5.0f;
|
||||
|
||||
LastTimeStep = timeStep;
|
||||
|
||||
int updatedEntityCount = 0;
|
||||
//Object updatedEntitiesPtr;
|
||||
int collidersCount = 0;
|
||||
//Object collidersPtr;
|
||||
|
||||
int beforeTime = 0;
|
||||
int simTime = 0;
|
||||
|
||||
// update the prim states while we know the physics engine is not busy
|
||||
int numTaints = _taintOperations.Count;
|
||||
|
||||
InTaintTime = true; // Only used for debugging so locking is not necessary.
|
||||
|
||||
ProcessTaints();
|
||||
|
||||
// Some of the physical objects requre individual, pre-step calls
|
||||
TriggerPreStepEvent(timeStep);
|
||||
|
||||
// the prestep actions might have added taints
|
||||
ProcessTaints();
|
||||
|
||||
InTaintTime = false; // Only used for debugging so locking is not necessary.
|
||||
|
||||
// The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
|
||||
// Only enable this in a limited test world with few objects.
|
||||
// BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
|
||||
|
||||
// step the physical world one interval
|
||||
m_simulationStep++;
|
||||
int numSubSteps = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
|
||||
|
||||
numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
|
||||
out updatedEntityCount, out m_updateArray, out collidersCount, out m_collisionArray);
|
||||
|
||||
if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
|
||||
DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
|
||||
DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
|
||||
updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}",
|
||||
LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e);
|
||||
DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}",
|
||||
DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
|
||||
updatedEntityCount = 0;
|
||||
collidersCount = 0;
|
||||
}
|
||||
|
||||
// Don't have to use the pointers passed back since we know it is the same pinned memory we passed in.
|
||||
|
||||
// Get a value for 'now' so all the collision and update routines don't have to get their own.
|
||||
SimulationNowTime = Util.EnvironmentTickCount();
|
||||
|
||||
// If there were collisions, process them by sending the event to the prim.
|
||||
// Collisions must be processed before updates.
|
||||
if (collidersCount > 0)
|
||||
{
|
||||
for (int ii = 0; ii < collidersCount; ii++)
|
||||
{
|
||||
uint cA = m_collisionArray[ii].aID;
|
||||
uint cB = m_collisionArray[ii].bID;
|
||||
Vector3 point = new Vector3(m_collisionArray[ii].point.X, m_collisionArray[ii].point.Y,
|
||||
m_collisionArray[ii].point.Z);
|
||||
Vector3 normal = new Vector3(m_collisionArray[ii].normal.X, m_collisionArray[ii].normal.Y,
|
||||
m_collisionArray[ii].normal.Z);
|
||||
SendCollision(cA, cB, point, normal, 0.01f);
|
||||
SendCollision(cB, cA, point, -normal, 0.01f);
|
||||
}
|
||||
}
|
||||
|
||||
// The above SendCollision's batch up the collisions on the objects.
|
||||
// Now push the collisions into the simulator.
|
||||
if (ObjectsWithCollisions.Count > 0)
|
||||
{
|
||||
foreach (BSPhysObject bsp in ObjectsWithCollisions)
|
||||
if (!bsp.SendCollisions())
|
||||
{
|
||||
// If the object is done colliding, see that it's removed from the colliding list
|
||||
ObjectsWithNoMoreCollisions.Add(bsp);
|
||||
}
|
||||
}
|
||||
|
||||
// This is a kludge to get avatar movement updates.
|
||||
// The simulator expects collisions for avatars even if there are have been no collisions.
|
||||
// The event updates avatar animations and stuff.
|
||||
// If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
|
||||
foreach (BSPhysObject bsp in m_avatars)
|
||||
if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
|
||||
bsp.SendCollisions();
|
||||
|
||||
// Objects that are done colliding are removed from the ObjectsWithCollisions list.
|
||||
// Not done above because it is inside an iteration of ObjectWithCollisions.
|
||||
// This complex collision processing is required to create an empty collision
|
||||
// event call after all collisions have happened on an object. This enables
|
||||
// the simulator to generate the 'collision end' event.
|
||||
if (ObjectsWithNoMoreCollisions.Count > 0)
|
||||
{
|
||||
foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
|
||||
ObjectsWithCollisions.Remove(po);
|
||||
ObjectsWithNoMoreCollisions.Clear();
|
||||
}
|
||||
// Done with collisions.
|
||||
|
||||
// If any of the objects had updated properties, tell the object it has been changed by the physics engine
|
||||
if (updatedEntityCount > 0)
|
||||
{
|
||||
for (int ii = 0; ii < updatedEntityCount; ii++)
|
||||
{
|
||||
|
||||
BulletXNA.EntityProperties entprop = m_updateArray[ii];
|
||||
BSPhysObject pobj;
|
||||
if (PhysObjects.TryGetValue(entprop.ID, out pobj))
|
||||
{
|
||||
EntityProperties prop = new EntityProperties()
|
||||
{
|
||||
Acceleration = new Vector3(entprop.Acceleration.X, entprop.Acceleration.Y, entprop.Acceleration.Z),
|
||||
ID = entprop.ID,
|
||||
Position = new Vector3(entprop.Position.X,entprop.Position.Y,entprop.Position.Z),
|
||||
Rotation = new Quaternion(entprop.Rotation.X,entprop.Rotation.Y,entprop.Rotation.Z,entprop.Rotation.W),
|
||||
RotationalVelocity = new Vector3(entprop.AngularVelocity.X,entprop.AngularVelocity.Y,entprop.AngularVelocity.Z),
|
||||
Velocity = new Vector3(entprop.Velocity.X,entprop.Velocity.Y,entprop.Velocity.Z)
|
||||
};
|
||||
//m_log.Debug(pobj.Name + ":" + prop.ToString() + "\n");
|
||||
pobj.UpdateProperties(prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TriggerPostStepEvent(timeStep);
|
||||
|
||||
// The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
|
||||
// Only enable this in a limited test world with few objects.
|
||||
// BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
|
||||
|
||||
// The physics engine returns the number of milliseconds it simulated this call.
|
||||
// These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
|
||||
// Multiply by 55 to give a nominal frame rate of 55.
|
||||
return (float)numSubSteps * m_fixedTimeStep * 1000f * 55f;
|
||||
}
|
||||
|
||||
// Something has collided
|
||||
private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration)
|
||||
{
|
||||
if (localID <= TerrainManager.HighestTerrainID)
|
||||
{
|
||||
return; // don't send collisions to the terrain
|
||||
}
|
||||
|
||||
BSPhysObject collider;
|
||||
if (!PhysObjects.TryGetValue(localID, out collider))
|
||||
{
|
||||
// If the object that is colliding cannot be found, just ignore the collision.
|
||||
DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith);
|
||||
return;
|
||||
}
|
||||
|
||||
// The terrain is not in the physical object list so 'collidee' can be null when Collide() is called.
|
||||
BSPhysObject collidee = null;
|
||||
PhysObjects.TryGetValue(collidingWith, out collidee);
|
||||
|
||||
// DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
|
||||
|
||||
if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
|
||||
{
|
||||
// If a collision was posted, remember to send it to the simulator
|
||||
ObjectsWithCollisions.Add(collider);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion // Simulation
|
||||
|
||||
public override void GetResults() { }
|
||||
|
||||
#region Terrain
|
||||
|
||||
public override void SetTerrain(float[] heightMap) {
|
||||
TerrainManager.SetTerrain(heightMap);
|
||||
}
|
||||
|
||||
public override void SetWaterLevel(float baseheight)
|
||||
{
|
||||
SimpleWaterLevel = baseheight;
|
||||
}
|
||||
|
||||
public override void DeleteTerrain()
|
||||
{
|
||||
// m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
|
||||
}
|
||||
|
||||
// Although no one seems to check this, I do support combining.
|
||||
public override bool SupportsCombining()
|
||||
{
|
||||
return TerrainManager.SupportsCombining();
|
||||
}
|
||||
// This call says I am a child to region zero in a mega-region. 'pScene' is that
|
||||
// of region zero, 'offset' is my offset from regions zero's origin, and
|
||||
// 'extents' is the largest XY that is handled in my region.
|
||||
public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
|
||||
{
|
||||
TerrainManager.Combine(pScene, offset, extents);
|
||||
}
|
||||
|
||||
// Unhook all the combining that I know about.
|
||||
public override void UnCombine(PhysicsScene pScene)
|
||||
{
|
||||
TerrainManager.UnCombine(pScene);
|
||||
}
|
||||
|
||||
#endregion // Terrain
|
||||
|
||||
public override Dictionary<uint, float> GetTopColliders()
|
||||
{
|
||||
return new Dictionary<uint, float>();
|
||||
}
|
||||
|
||||
public override bool IsThreaded { get { return false; } }
|
||||
|
||||
#region Taints
|
||||
// The simulation execution order is:
|
||||
// Simulate()
|
||||
// DoOneTimeTaints
|
||||
// TriggerPreStepEvent
|
||||
// DoOneTimeTaints
|
||||
// Step()
|
||||
// ProcessAndForwardCollisions
|
||||
// ProcessAndForwardPropertyUpdates
|
||||
// TriggerPostStepEvent
|
||||
|
||||
// Calls to the PhysicsActors can't directly call into the physics engine
|
||||
// because it might be busy. We delay changes to a known time.
|
||||
// We rely on C#'s closure to save and restore the context for the delegate.
|
||||
public void TaintedObject(String ident, TaintCallback callback)
|
||||
{
|
||||
if (!m_initialized) return;
|
||||
|
||||
lock (_taintLock)
|
||||
{
|
||||
_taintOperations.Add(new TaintCallbackEntry(ident, callback));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Sometimes a potentially tainted operation can be used in and out of taint time.
|
||||
// This routine executes the command immediately if in taint-time otherwise it is queued.
|
||||
public void TaintedObject(bool inTaintTime, string ident, TaintCallback callback)
|
||||
{
|
||||
if (inTaintTime)
|
||||
callback();
|
||||
else
|
||||
TaintedObject(ident, callback);
|
||||
}
|
||||
|
||||
private void TriggerPreStepEvent(float timeStep)
|
||||
{
|
||||
PreStepAction actions = BeforeStep;
|
||||
if (actions != null)
|
||||
actions(timeStep);
|
||||
|
||||
}
|
||||
|
||||
private void TriggerPostStepEvent(float timeStep)
|
||||
{
|
||||
PreStepAction actions = AfterStep;
|
||||
if (actions != null)
|
||||
actions(timeStep);
|
||||
|
||||
}
|
||||
|
||||
// When someone tries to change a property on a BSPrim or BSCharacter, the object queues
|
||||
// a callback into itself to do the actual property change. That callback is called
|
||||
// here just before the physics engine is called to step the simulation.
|
||||
public void ProcessTaints()
|
||||
{
|
||||
ProcessRegularTaints();
|
||||
ProcessPostTaintTaints();
|
||||
}
|
||||
|
||||
private void ProcessRegularTaints()
|
||||
{
|
||||
if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process
|
||||
{
|
||||
// swizzle a new list into the list location so we can process what's there
|
||||
List<TaintCallbackEntry> oldList;
|
||||
lock (_taintLock)
|
||||
{
|
||||
oldList = _taintOperations;
|
||||
_taintOperations = new List<TaintCallbackEntry>();
|
||||
}
|
||||
|
||||
foreach (TaintCallbackEntry tcbe in oldList)
|
||||
{
|
||||
try
|
||||
{
|
||||
DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG
|
||||
tcbe.callback();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e);
|
||||
}
|
||||
}
|
||||
oldList.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule an update to happen after all the regular taints are processed.
|
||||
// Note that new requests for the same operation ("ident") for the same object ("ID")
|
||||
// will replace any previous operation by the same object.
|
||||
public void PostTaintObject(String ident, uint ID, TaintCallback callback)
|
||||
{
|
||||
string uniqueIdent = ident + "-" + ID.ToString();
|
||||
lock (_taintLock)
|
||||
{
|
||||
_postTaintOperations[uniqueIdent] = new TaintCallbackEntry(uniqueIdent, callback);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Taints that happen after the normal taint processing but before the simulation step.
|
||||
private void ProcessPostTaintTaints()
|
||||
{
|
||||
if (_postTaintOperations.Count > 0)
|
||||
{
|
||||
Dictionary<string, TaintCallbackEntry> oldList;
|
||||
lock (_taintLock)
|
||||
{
|
||||
oldList = _postTaintOperations;
|
||||
_postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string,TaintCallbackEntry> kvp in oldList)
|
||||
{
|
||||
try
|
||||
{
|
||||
DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG
|
||||
kvp.Value.callback();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e);
|
||||
}
|
||||
}
|
||||
oldList.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Only used for debugging. Does not change state of anything so locking is not necessary.
|
||||
public bool AssertInTaintTime(string whereFrom)
|
||||
{
|
||||
if (!InTaintTime)
|
||||
{
|
||||
DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
|
||||
m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
|
||||
Util.PrintCallStack(DetailLog);
|
||||
}
|
||||
return InTaintTime;
|
||||
}
|
||||
|
||||
#endregion // Taints
|
||||
|
||||
#region INI and command line parameter processing
|
||||
|
||||
#region IPhysicsParameters
|
||||
// Get the list of parameters this physics engine supports
|
||||
public PhysParameterEntry[] GetParameterList()
|
||||
{
|
||||
BSParam.BuildParameterTable();
|
||||
return BSParam.SettableParameters;
|
||||
}
|
||||
|
||||
// Set parameter on a specific or all instances.
|
||||
// Return 'false' if not able to set the parameter.
|
||||
// Setting the value in the m_params block will change the value the physics engine
|
||||
// will use the next time since it's pinned and shared memory.
|
||||
// Some of the values require calling into the physics engine to get the new
|
||||
// value activated ('terrainFriction' for instance).
|
||||
public bool SetPhysicsParameter(string parm, float val, uint localID)
|
||||
{
|
||||
bool ret = false;
|
||||
BSParam.ParameterDefn theParam;
|
||||
if (BSParam.TryGetParameter(parm, out theParam))
|
||||
{
|
||||
theParam.setter(this, parm, localID, val);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// update all the localIDs specified
|
||||
// If the local ID is APPLY_TO_NONE, just change the default value
|
||||
// If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
|
||||
// If the localID is a specific object, apply the parameter change to only that object
|
||||
internal delegate void AssignVal(float x);
|
||||
internal void UpdateParameterObject(AssignVal setDefault, string parm, uint localID, float val)
|
||||
{
|
||||
List<uint> objectIDs = new List<uint>();
|
||||
switch (localID)
|
||||
{
|
||||
case PhysParameterEntry.APPLY_TO_NONE:
|
||||
setDefault(val); // setting only the default value
|
||||
// This will cause a call into the physical world if some operation is specified (SetOnObject).
|
||||
objectIDs.Add(TERRAIN_ID);
|
||||
TaintedUpdateParameter(parm, objectIDs, val);
|
||||
break;
|
||||
case PhysParameterEntry.APPLY_TO_ALL:
|
||||
setDefault(val); // setting ALL also sets the default value
|
||||
lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
|
||||
TaintedUpdateParameter(parm, objectIDs, val);
|
||||
break;
|
||||
default:
|
||||
// setting only one localID
|
||||
objectIDs.Add(localID);
|
||||
TaintedUpdateParameter(parm, objectIDs, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// schedule the actual updating of the paramter to when the phys engine is not busy
|
||||
private void TaintedUpdateParameter(string parm, List<uint> lIDs, float val)
|
||||
{
|
||||
float xval = val;
|
||||
List<uint> xlIDs = lIDs;
|
||||
string xparm = parm;
|
||||
TaintedObject("BSScene.UpdateParameterSet", delegate() {
|
||||
BSParam.ParameterDefn thisParam;
|
||||
if (BSParam.TryGetParameter(xparm, out thisParam))
|
||||
{
|
||||
if (thisParam.onObject != null)
|
||||
{
|
||||
foreach (uint lID in xlIDs)
|
||||
{
|
||||
BSPhysObject theObject = null;
|
||||
PhysObjects.TryGetValue(lID, out theObject);
|
||||
thisParam.onObject(this, theObject, xval);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Get parameter.
|
||||
// Return 'false' if not able to get the parameter.
|
||||
public bool GetPhysicsParameter(string parm, out float value)
|
||||
{
|
||||
float val = 0f;
|
||||
bool ret = false;
|
||||
BSParam.ParameterDefn theParam;
|
||||
if (BSParam.TryGetParameter(parm, out theParam))
|
||||
{
|
||||
val = theParam.getter(this);
|
||||
ret = true;
|
||||
}
|
||||
value = val;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endregion IPhysicsParameters
|
||||
|
||||
#endregion Runtime settable parameters
|
||||
|
||||
// Invoke the detailed logger and output something if it's enabled.
|
||||
public void DetailLog(string msg, params Object[] args)
|
||||
{
|
||||
PhysicsLogging.Write(msg, args);
|
||||
// Add the Flush() if debugging crashes. Gets all the messages written out.
|
||||
if (m_physicsLoggingDoFlush) PhysicsLogging.Flush();
|
||||
}
|
||||
// Used to fill in the LocalID when there isn't one. It's the correct number of characters.
|
||||
public const string DetailLogZero = "0000000000";
|
||||
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,208 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public abstract class BSShape
|
||||
{
|
||||
public Object ptr { get; set; }
|
||||
public BSPhysicsShapeType type { get; set; }
|
||||
public System.UInt64 key { get; set; }
|
||||
public int referenceCount { get; set; }
|
||||
public DateTime lastReferenced { get; set; }
|
||||
|
||||
public BSShape()
|
||||
{
|
||||
ptr = null;
|
||||
type = BSPhysicsShapeType.SHAPE_UNKNOWN;
|
||||
key = 0;
|
||||
referenceCount = 0;
|
||||
lastReferenced = DateTime.Now;
|
||||
}
|
||||
|
||||
// Get a reference to a physical shape. Create if it doesn't exist
|
||||
public static BSShape GetShapeReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
|
||||
{
|
||||
BSShape ret = null;
|
||||
|
||||
if (prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE)
|
||||
{
|
||||
// an avatar capsule is close to a native shape (it is not shared)
|
||||
ret = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_CAPSULE,
|
||||
FixedShapeKey.KEY_CAPSULE);
|
||||
physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret);
|
||||
}
|
||||
|
||||
// Compound shapes are handled special as they are rebuilt from scratch.
|
||||
// This isn't too great a hardship since most of the child shapes will already been created.
|
||||
if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND)
|
||||
{
|
||||
// Getting a reference to a compound shape gets you the compound shape with the root prim shape added
|
||||
ret = BSShapeCompound.GetReference(prim);
|
||||
physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, ret);
|
||||
}
|
||||
|
||||
if (ret == null)
|
||||
ret = GetShapeReferenceNonSpecial(physicsScene, forceRebuild, prim);
|
||||
|
||||
return ret;
|
||||
}
|
||||
public static BSShape GetShapeReferenceNonSpecial(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public static BSShape GetShapeReferenceNonNative(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Release the use of a physical shape.
|
||||
public abstract void Dereference(BSScene physicsScene);
|
||||
|
||||
// All shapes have a static call to get a reference to the physical shape
|
||||
// protected abstract static BSShape GetReference();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder buff = new StringBuilder();
|
||||
buff.Append("<p=");
|
||||
buff.Append(ptr.ToString());
|
||||
buff.Append(",s=");
|
||||
buff.Append(type.ToString());
|
||||
buff.Append(",k=");
|
||||
buff.Append(key.ToString("X"));
|
||||
buff.Append(",c=");
|
||||
buff.Append(referenceCount.ToString());
|
||||
buff.Append(">");
|
||||
return buff.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public class BSShapeNull : BSShape
|
||||
{
|
||||
public BSShapeNull() : base()
|
||||
{
|
||||
}
|
||||
public static BSShape GetReference() { return new BSShapeNull(); }
|
||||
public override void Dereference(BSScene physicsScene) { /* The magic of garbage collection will make this go away */ }
|
||||
}
|
||||
|
||||
public class BSShapeNative : BSShape
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM SHAPE NATIVE]";
|
||||
public BSShapeNative() : base()
|
||||
{
|
||||
}
|
||||
public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim,
|
||||
BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
|
||||
{
|
||||
// Native shapes are not shared and are always built anew.
|
||||
return new BSShapeNative(physicsScene, prim, shapeType, shapeKey);
|
||||
}
|
||||
|
||||
private BSShapeNative(BSScene physicsScene, BSPhysObject prim,
|
||||
BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
|
||||
{
|
||||
ShapeData nativeShapeData = new ShapeData();
|
||||
nativeShapeData.Type = shapeType;
|
||||
nativeShapeData.ID = prim.LocalID;
|
||||
nativeShapeData.Scale = prim.Scale;
|
||||
nativeShapeData.Size = prim.Scale;
|
||||
nativeShapeData.MeshKey = (ulong)shapeKey;
|
||||
nativeShapeData.HullKey = (ulong)shapeKey;
|
||||
|
||||
|
||||
if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
|
||||
{
|
||||
ptr = BulletSimAPI.BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale);
|
||||
physicsScene.DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = BulletSimAPI.BuildNativeShape2(physicsScene.World.ptr, nativeShapeData);
|
||||
}
|
||||
if (ptr == null)
|
||||
{
|
||||
physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
|
||||
LogHeader, prim.LocalID, shapeType);
|
||||
}
|
||||
type = shapeType;
|
||||
key = (UInt64)shapeKey;
|
||||
}
|
||||
// Make this reference to the physical shape go away since native shapes are not shared.
|
||||
public override void Dereference(BSScene physicsScene)
|
||||
{
|
||||
// Native shapes are not tracked and are released immediately
|
||||
physicsScene.DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this);
|
||||
BulletSimAPI.DeleteCollisionShape2(physicsScene.World.ptr, ptr);
|
||||
ptr = null;
|
||||
// Garbage collection will free up this instance.
|
||||
}
|
||||
}
|
||||
|
||||
public class BSShapeMesh : BSShape
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM SHAPE MESH]";
|
||||
private static Dictionary<System.UInt64, BSShapeMesh> Meshes = new Dictionary<System.UInt64, BSShapeMesh>();
|
||||
|
||||
public BSShapeMesh() : base()
|
||||
{
|
||||
}
|
||||
public static BSShape GetReference() { return new BSShapeNull(); }
|
||||
public override void Dereference(BSScene physicsScene) { }
|
||||
}
|
||||
|
||||
public class BSShapeHull : BSShape
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM SHAPE HULL]";
|
||||
private static Dictionary<System.UInt64, BSShapeHull> Hulls = new Dictionary<System.UInt64, BSShapeHull>();
|
||||
|
||||
public BSShapeHull() : base()
|
||||
{
|
||||
}
|
||||
public static BSShape GetReference() { return new BSShapeNull(); }
|
||||
public override void Dereference(BSScene physicsScene) { }
|
||||
}
|
||||
|
||||
public class BSShapeCompound : BSShape
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]";
|
||||
public BSShapeCompound() : base()
|
||||
{
|
||||
}
|
||||
public static BSShape GetReference(BSPhysObject prim)
|
||||
{
|
||||
return new BSShapeNull();
|
||||
}
|
||||
public override void Dereference(BSScene physicsScene) { }
|
||||
}
|
||||
}
|
|
@ -1,175 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework;
|
||||
using OpenSim.Region.CoreModules;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
|
||||
using Nini.Config;
|
||||
using log4net;
|
||||
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public sealed class BSTerrainHeightmap : BSTerrainPhys
|
||||
{
|
||||
static string LogHeader = "[BULLETSIM TERRAIN HEIGHTMAP]";
|
||||
|
||||
BulletHeightMapInfo m_mapInfo = null;
|
||||
|
||||
// Constructor to build a default, flat heightmap terrain.
|
||||
public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
|
||||
: base(physicsScene, regionBase, id)
|
||||
{
|
||||
Vector3 minTerrainCoords = new Vector3(0f, 0f, BSTerrainManager.HEIGHT_INITIALIZATION - BSTerrainManager.HEIGHT_EQUAL_FUDGE);
|
||||
Vector3 maxTerrainCoords = new Vector3(regionSize.X, regionSize.Y, BSTerrainManager.HEIGHT_INITIALIZATION);
|
||||
int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y;
|
||||
float[] initialMap = new float[totalHeights];
|
||||
for (int ii = 0; ii < totalHeights; ii++)
|
||||
{
|
||||
initialMap[ii] = BSTerrainManager.HEIGHT_INITIALIZATION;
|
||||
}
|
||||
m_mapInfo = new BulletHeightMapInfo(id, initialMap, null);
|
||||
m_mapInfo.minCoords = minTerrainCoords;
|
||||
m_mapInfo.maxCoords = maxTerrainCoords;
|
||||
m_mapInfo.terrainRegionBase = TerrainBase;
|
||||
// Don't have to free any previous since we just got here.
|
||||
BuildHeightmapTerrain();
|
||||
}
|
||||
|
||||
// This minCoords and maxCoords passed in give the size of the terrain (min and max Z
|
||||
// are the high and low points of the heightmap).
|
||||
public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
|
||||
Vector3 minCoords, Vector3 maxCoords)
|
||||
: base(physicsScene, regionBase, id)
|
||||
{
|
||||
m_mapInfo = new BulletHeightMapInfo(id, initialMap, null);
|
||||
m_mapInfo.minCoords = minCoords;
|
||||
m_mapInfo.maxCoords = maxCoords;
|
||||
m_mapInfo.minZ = minCoords.Z;
|
||||
m_mapInfo.maxZ = maxCoords.Z;
|
||||
m_mapInfo.terrainRegionBase = TerrainBase;
|
||||
|
||||
// Don't have to free any previous since we just got here.
|
||||
BuildHeightmapTerrain();
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
ReleaseHeightMapTerrain();
|
||||
}
|
||||
|
||||
// Using the information in m_mapInfo, create the physical representation of the heightmap.
|
||||
private void BuildHeightmapTerrain()
|
||||
{
|
||||
m_mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, m_mapInfo.ID,
|
||||
m_mapInfo.minCoords, m_mapInfo.maxCoords,
|
||||
m_mapInfo.heightMap, BSParam.TerrainCollisionMargin);
|
||||
|
||||
// Create the terrain shape from the mapInfo
|
||||
m_mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(m_mapInfo.Ptr),
|
||||
BSPhysicsShapeType.SHAPE_TERRAIN);
|
||||
|
||||
// The terrain object initial position is at the center of the object
|
||||
Vector3 centerPos;
|
||||
centerPos.X = m_mapInfo.minCoords.X + (m_mapInfo.sizeX / 2f);
|
||||
centerPos.Y = m_mapInfo.minCoords.Y + (m_mapInfo.sizeY / 2f);
|
||||
centerPos.Z = m_mapInfo.minZ + ((m_mapInfo.maxZ - m_mapInfo.minZ) / 2f - 0.5f);
|
||||
|
||||
m_mapInfo.terrainBody = new BulletBody(m_mapInfo.ID,
|
||||
BulletSimAPI.CreateBodyWithDefaultMotionState2(m_mapInfo.terrainShape.ptr,
|
||||
m_mapInfo.ID, centerPos, Quaternion.Identity));
|
||||
|
||||
// Set current terrain attributes
|
||||
BulletSimAPI.SetFriction2(m_mapInfo.terrainBody.ptr, BSParam.TerrainFriction);
|
||||
BulletSimAPI.SetHitFraction2(m_mapInfo.terrainBody.ptr, BSParam.TerrainHitFraction);
|
||||
BulletSimAPI.SetRestitution2(m_mapInfo.terrainBody.ptr, BSParam.TerrainRestitution);
|
||||
BulletSimAPI.SetCollisionFlags2(m_mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
|
||||
|
||||
// Return the new terrain to the world of physical objects
|
||||
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr, centerPos, Quaternion.Identity);
|
||||
|
||||
// redo its bounding box now that it is in the world
|
||||
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
|
||||
|
||||
m_mapInfo.terrainBody.collisionType = CollisionType.Terrain;
|
||||
m_mapInfo.terrainBody.ApplyCollisionMask();
|
||||
|
||||
// Make it so the terrain will not move or be considered for movement.
|
||||
BulletSimAPI.ForceActivationState2(m_mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is information in m_mapInfo pointing to physical structures, release same.
|
||||
private void ReleaseHeightMapTerrain()
|
||||
{
|
||||
if (m_mapInfo != null)
|
||||
{
|
||||
if (m_mapInfo.terrainBody.HasPhysicalBody)
|
||||
{
|
||||
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
|
||||
// Frees both the body and the shape.
|
||||
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
|
||||
BulletSimAPI.ReleaseHeightMapInfo2(m_mapInfo.Ptr);
|
||||
}
|
||||
}
|
||||
m_mapInfo = null;
|
||||
}
|
||||
|
||||
// The passed position is relative to the base of the region.
|
||||
public override float GetTerrainHeightAtXYZ(Vector3 pos)
|
||||
{
|
||||
float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
|
||||
|
||||
int mapIndex = (int)pos.Y * (int)m_mapInfo.sizeY + (int)pos.X;
|
||||
try
|
||||
{
|
||||
ret = m_mapInfo.heightMap[mapIndex];
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Sometimes they give us wonky values of X and Y. Give a warning and return something.
|
||||
PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
|
||||
LogHeader, m_mapInfo.terrainRegionBase, pos);
|
||||
ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The passed position is relative to the base of the region.
|
||||
public override float GetWaterLevelAtXYZ(Vector3 pos)
|
||||
{
|
||||
return PhysicsScene.SimpleWaterLevel;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,461 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework;
|
||||
using OpenSim.Region.CoreModules;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
|
||||
using Nini.Config;
|
||||
using log4net;
|
||||
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
// The physical implementation of the terrain is wrapped in this class.
|
||||
public abstract class BSTerrainPhys : IDisposable
|
||||
{
|
||||
public enum TerrainImplementation
|
||||
{
|
||||
Heightmap = 0,
|
||||
Mesh = 1
|
||||
}
|
||||
|
||||
public BSScene PhysicsScene { get; private set; }
|
||||
// Base of the region in world coordinates. Coordinates inside the region are relative to this.
|
||||
public Vector3 TerrainBase { get; private set; }
|
||||
public uint ID { get; private set; }
|
||||
|
||||
public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id)
|
||||
{
|
||||
PhysicsScene = physicsScene;
|
||||
TerrainBase = regionBase;
|
||||
ID = id;
|
||||
}
|
||||
public abstract void Dispose();
|
||||
public abstract float GetTerrainHeightAtXYZ(Vector3 pos);
|
||||
public abstract float GetWaterLevelAtXYZ(Vector3 pos);
|
||||
}
|
||||
|
||||
// ==========================================================================================
|
||||
public sealed class BSTerrainManager : IDisposable
|
||||
{
|
||||
static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
|
||||
|
||||
// These height values are fractional so the odd values will be
|
||||
// noticable when debugging.
|
||||
public const float HEIGHT_INITIALIZATION = 24.987f;
|
||||
public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f;
|
||||
public const float HEIGHT_GETHEIGHT_RET = 24.765f;
|
||||
public const float WATER_HEIGHT_GETHEIGHT_RET = 19.998f;
|
||||
|
||||
// If the min and max height are equal, we reduce the min by this
|
||||
// amount to make sure that a bounding box is built for the terrain.
|
||||
public const float HEIGHT_EQUAL_FUDGE = 0.2f;
|
||||
|
||||
// Until the whole simulator is changed to pass us the region size, we rely on constants.
|
||||
public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
|
||||
|
||||
// The scene that I am part of
|
||||
private BSScene PhysicsScene { get; set; }
|
||||
|
||||
// The ground plane created to keep thing from falling to infinity.
|
||||
private BulletBody m_groundPlane;
|
||||
|
||||
// If doing mega-regions, if we're region zero we will be managing multiple
|
||||
// region terrains since region zero does the physics for the whole mega-region.
|
||||
private Dictionary<Vector3, BSTerrainPhys> m_terrains;
|
||||
|
||||
// Flags used to know when to recalculate the height.
|
||||
private bool m_terrainModified = false;
|
||||
|
||||
// If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount.
|
||||
// This is incremented before assigning to new region so it is the last ID allocated.
|
||||
private uint m_terrainCount = BSScene.CHILDTERRAIN_ID - 1;
|
||||
public uint HighestTerrainID { get {return m_terrainCount; } }
|
||||
|
||||
// If doing mega-regions, this holds our offset from region zero of
|
||||
// the mega-regions. "parentScene" points to the PhysicsScene of region zero.
|
||||
private Vector3 m_worldOffset;
|
||||
// If the parent region (region 0), this is the extent of the combined regions
|
||||
// relative to the origin of region zero
|
||||
private Vector3 m_worldMax;
|
||||
private PhysicsScene MegaRegionParentPhysicsScene { get; set; }
|
||||
|
||||
public BSTerrainManager(BSScene physicsScene)
|
||||
{
|
||||
PhysicsScene = physicsScene;
|
||||
m_terrains = new Dictionary<Vector3,BSTerrainPhys>();
|
||||
|
||||
// Assume one region of default size
|
||||
m_worldOffset = Vector3.Zero;
|
||||
m_worldMax = new Vector3(DefaultRegionSize);
|
||||
MegaRegionParentPhysicsScene = null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ReleaseGroundPlaneAndTerrain();
|
||||
}
|
||||
|
||||
// Create the initial instance of terrain and the underlying ground plane.
|
||||
// This is called from the initialization routine so we presume it is
|
||||
// safe to call Bullet in real time. We hope no one is moving prims around yet.
|
||||
public void CreateInitialGroundPlaneAndTerrain()
|
||||
{
|
||||
// The ground plane is here to catch things that are trying to drop to negative infinity
|
||||
BulletShape groundPlaneShape = new BulletShape(
|
||||
BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f,
|
||||
BSParam.TerrainCollisionMargin),
|
||||
BSPhysicsShapeType.SHAPE_GROUNDPLANE);
|
||||
m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
|
||||
BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
|
||||
Vector3.Zero, Quaternion.Identity));
|
||||
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr, Vector3.Zero, Quaternion.Identity);
|
||||
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_groundPlane.ptr);
|
||||
// Ground plane does not move
|
||||
BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION);
|
||||
// Everything collides with the ground plane.
|
||||
m_groundPlane.collisionType = CollisionType.Groundplane;
|
||||
m_groundPlane.ApplyCollisionMask();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Release all the terrain structures we might have allocated
|
||||
public void ReleaseGroundPlaneAndTerrain()
|
||||
{
|
||||
if (m_groundPlane.HasPhysicalBody)
|
||||
{
|
||||
if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr))
|
||||
{
|
||||
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_groundPlane.ptr);
|
||||
}
|
||||
m_groundPlane.Clear();
|
||||
}
|
||||
|
||||
ReleaseTerrain();
|
||||
}
|
||||
|
||||
// Release all the terrain we have allocated
|
||||
public void ReleaseTerrain()
|
||||
{
|
||||
lock (m_terrains)
|
||||
{
|
||||
foreach (KeyValuePair<Vector3, BSTerrainPhys> kvp in m_terrains)
|
||||
{
|
||||
kvp.Value.Dispose();
|
||||
}
|
||||
m_terrains.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// The simulator wants to set a new heightmap for the terrain.
|
||||
public void SetTerrain(float[] heightMap) {
|
||||
float[] localHeightMap = heightMap;
|
||||
// If there are multiple requests for changes to the same terrain between ticks,
|
||||
// only do that last one.
|
||||
PhysicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate()
|
||||
{
|
||||
if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null)
|
||||
{
|
||||
// If a child of a mega-region, we shouldn't have any terrain allocated for us
|
||||
ReleaseGroundPlaneAndTerrain();
|
||||
// If doing the mega-prim stuff and we are the child of the zero region,
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not doing the mega-prim thing, just change the terrain
|
||||
DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
|
||||
|
||||
UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap,
|
||||
m_worldOffset, m_worldOffset + DefaultRegionSize, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// If called with no mapInfo for the terrain, this will create a new mapInfo and terrain
|
||||
// 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
|
||||
// terrain shape is created and added to the body.
|
||||
// This call is most often used to update the heightMap and parameters of the terrain.
|
||||
// (The above does suggest that some simplification/refactoring is in order.)
|
||||
// Called during taint-time.
|
||||
private void UpdateTerrain(uint id, float[] heightMap,
|
||||
Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
|
||||
{
|
||||
DetailLog("{0},BSTerrainManager.UpdateTerrain,call,minC={1},maxC={2},inTaintTime={3}",
|
||||
BSScene.DetailLogZero, 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
|
||||
// object height, for instance). The terrain wants the bounding box for the
|
||||
// terrain so replace passed min and max Z with the actual terrain min/max Z.
|
||||
float minZ = float.MaxValue;
|
||||
float maxZ = float.MinValue;
|
||||
foreach (float height in heightMap)
|
||||
{
|
||||
if (height < minZ) minZ = height;
|
||||
if (height > maxZ) maxZ = height;
|
||||
}
|
||||
if (minZ == maxZ)
|
||||
{
|
||||
// If min and max are the same, reduce min a little bit so a good bounding box is created.
|
||||
minZ -= BSTerrainManager.HEIGHT_EQUAL_FUDGE;
|
||||
}
|
||||
minCoords.Z = minZ;
|
||||
maxCoords.Z = maxZ;
|
||||
|
||||
Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f);
|
||||
|
||||
lock (m_terrains)
|
||||
{
|
||||
BSTerrainPhys terrainPhys;
|
||||
if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
|
||||
{
|
||||
// There is already a terrain in this spot. Free the old and build the new.
|
||||
DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}",
|
||||
BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
|
||||
|
||||
// Remove old terrain from the collection
|
||||
m_terrains.Remove(terrainRegionBase);
|
||||
// Release any physical memory it may be using.
|
||||
terrainPhys.Dispose();
|
||||
|
||||
if (MegaRegionParentPhysicsScene == null)
|
||||
{
|
||||
BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
|
||||
m_terrains.Add(terrainRegionBase, newTerrainPhys);
|
||||
|
||||
m_terrainModified = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's possible that Combine() was called after this code was queued.
|
||||
// If we are a child of combined regions, we don't create any terrain for us.
|
||||
DetailLog("{0},BSTerrainManager.UpdateTerrain:AmACombineChild,taint", BSScene.DetailLogZero);
|
||||
|
||||
// Get rid of any terrain that may have been allocated for us.
|
||||
ReleaseGroundPlaneAndTerrain();
|
||||
|
||||
// I hate doing this, but just bail
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't know about this terrain so either we are creating a new terrain or
|
||||
// our mega-prim child is giving us a new terrain to add to the phys world
|
||||
|
||||
// if this is a child terrain, calculate a unique terrain id
|
||||
uint newTerrainID = id;
|
||||
if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
|
||||
newTerrainID = ++m_terrainCount;
|
||||
|
||||
DetailLog("{0},UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}",
|
||||
BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
|
||||
BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
|
||||
m_terrains.Add(terrainRegionBase, newTerrainPhys);
|
||||
|
||||
m_terrainModified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: redo terrain implementation selection to allow other base types than heightMap.
|
||||
private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords)
|
||||
{
|
||||
PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}",
|
||||
LogHeader, PhysicsScene.RegionName, terrainRegionBase,
|
||||
(BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation);
|
||||
BSTerrainPhys newTerrainPhys = null;
|
||||
switch ((int)BSParam.TerrainImplementation)
|
||||
{
|
||||
case (int)BSTerrainPhys.TerrainImplementation.Heightmap:
|
||||
newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id,
|
||||
heightMap, minCoords, maxCoords);
|
||||
break;
|
||||
case (int)BSTerrainPhys.TerrainImplementation.Mesh:
|
||||
newTerrainPhys = new BSTerrainMesh(PhysicsScene, terrainRegionBase, id,
|
||||
heightMap, minCoords, maxCoords);
|
||||
break;
|
||||
default:
|
||||
PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}",
|
||||
LogHeader,
|
||||
(int)BSParam.TerrainImplementation,
|
||||
BSParam.TerrainImplementation,
|
||||
PhysicsScene.RegionName, terrainRegionBase);
|
||||
break;
|
||||
}
|
||||
return newTerrainPhys;
|
||||
}
|
||||
|
||||
// Return 'true' of this position is somewhere in known physical terrain space
|
||||
public bool IsWithinKnownTerrain(Vector3 pos)
|
||||
{
|
||||
Vector3 terrainBaseXYZ;
|
||||
BSTerrainPhys physTerrain;
|
||||
return GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ);
|
||||
}
|
||||
|
||||
// Given an X and Y, find the height of the terrain.
|
||||
// Since we could be handling multiple terrains for a mega-region,
|
||||
// the base of the region is calcuated assuming all regions are
|
||||
// the same size and that is the default.
|
||||
// Once the heightMapInfo is found, we have all the information to
|
||||
// compute the offset into the array.
|
||||
private float lastHeightTX = 999999f;
|
||||
private float lastHeightTY = 999999f;
|
||||
private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT;
|
||||
public float GetTerrainHeightAtXYZ(Vector3 pos)
|
||||
{
|
||||
float tX = pos.X;
|
||||
float tY = pos.Y;
|
||||
// You'd be surprized at the number of times this routine is called
|
||||
// with the same parameters as last time.
|
||||
if (!m_terrainModified && (lastHeightTX == tX) && (lastHeightTY == tY))
|
||||
return lastHeight;
|
||||
m_terrainModified = false;
|
||||
|
||||
lastHeightTX = tX;
|
||||
lastHeightTY = tY;
|
||||
float ret = HEIGHT_GETHEIGHT_RET;
|
||||
|
||||
Vector3 terrainBaseXYZ;
|
||||
BSTerrainPhys physTerrain;
|
||||
if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
|
||||
{
|
||||
ret = physTerrain.GetTerrainHeightAtXYZ(pos - terrainBaseXYZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
|
||||
LogHeader, PhysicsScene.RegionName, tX, tY);
|
||||
DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,terrainNotFound,pos={1},base={2}",
|
||||
BSScene.DetailLogZero, pos, terrainBaseXYZ);
|
||||
}
|
||||
|
||||
lastHeight = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public float GetWaterLevelAtXYZ(Vector3 pos)
|
||||
{
|
||||
float ret = WATER_HEIGHT_GETHEIGHT_RET;
|
||||
|
||||
Vector3 terrainBaseXYZ;
|
||||
BSTerrainPhys physTerrain;
|
||||
if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
|
||||
{
|
||||
ret = physTerrain.GetWaterLevelAtXYZ(pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
PhysicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}",
|
||||
LogHeader, PhysicsScene.RegionName, pos, terrainBaseXYZ, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Given an address, return 'true' of there is a description of that terrain and output
|
||||
// the descriptor class and the 'base' fo the addresses therein.
|
||||
private bool GetTerrainPhysicalAtXYZ(Vector3 pos, out BSTerrainPhys outPhysTerrain, out Vector3 outTerrainBase)
|
||||
{
|
||||
int offsetX = ((int)(pos.X / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
|
||||
int offsetY = ((int)(pos.Y / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
|
||||
Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
|
||||
|
||||
BSTerrainPhys physTerrain = null;
|
||||
lock (m_terrains)
|
||||
{
|
||||
m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain);
|
||||
}
|
||||
outTerrainBase = terrainBaseXYZ;
|
||||
outPhysTerrain = physTerrain;
|
||||
return (physTerrain != null);
|
||||
}
|
||||
|
||||
// Although no one seems to check this, I do support combining.
|
||||
public bool SupportsCombining()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// This routine is called two ways:
|
||||
// One with 'offset' and 'pScene' zero and null but 'extents' giving the maximum
|
||||
// extent of the combined regions. This is to inform the parent of the size
|
||||
// of the combined regions.
|
||||
// and one with 'offset' as the offset of the child region to the base region,
|
||||
// 'pScene' pointing to the parent and 'extents' of zero. This informs the
|
||||
// child of its relative base and new parent.
|
||||
public void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
|
||||
{
|
||||
m_worldOffset = offset;
|
||||
m_worldMax = extents;
|
||||
MegaRegionParentPhysicsScene = pScene;
|
||||
if (pScene != null)
|
||||
{
|
||||
// We are a child.
|
||||
// We want m_worldMax to be the highest coordinate of our piece of terrain.
|
||||
m_worldMax = offset + DefaultRegionSize;
|
||||
}
|
||||
DetailLog("{0},BSTerrainManager.Combine,offset={1},extents={2},wOffset={3},wMax={4}",
|
||||
BSScene.DetailLogZero, offset, extents, m_worldOffset, m_worldMax);
|
||||
}
|
||||
|
||||
// Unhook all the combining that I know about.
|
||||
public void UnCombine(PhysicsScene pScene)
|
||||
{
|
||||
// Just like ODE, we don't do anything yet.
|
||||
DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero);
|
||||
}
|
||||
|
||||
|
||||
private void DetailLog(string msg, params Object[] args)
|
||||
{
|
||||
PhysicsScene.PhysicsLogging.Write(msg, args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,267 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework;
|
||||
using OpenSim.Region.CoreModules;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
|
||||
using Nini.Config;
|
||||
using log4net;
|
||||
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public sealed class BSTerrainMesh : BSTerrainPhys
|
||||
{
|
||||
static string LogHeader = "[BULLETSIM TERRAIN MESH]";
|
||||
|
||||
private float[] m_savedHeightMap;
|
||||
int m_sizeX;
|
||||
int m_sizeY;
|
||||
|
||||
BulletShape m_terrainShape;
|
||||
BulletBody m_terrainBody;
|
||||
|
||||
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
|
||||
: base(physicsScene, regionBase, id)
|
||||
{
|
||||
}
|
||||
|
||||
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id /* parameters for making mesh */)
|
||||
: base(physicsScene, regionBase, id)
|
||||
{
|
||||
}
|
||||
|
||||
// Create terrain mesh from a heightmap.
|
||||
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
|
||||
Vector3 minCoords, Vector3 maxCoords)
|
||||
: base(physicsScene, regionBase, id)
|
||||
{
|
||||
int indicesCount;
|
||||
int[] indices;
|
||||
int verticesCount;
|
||||
float[] vertices;
|
||||
|
||||
m_savedHeightMap = initialMap;
|
||||
|
||||
m_sizeX = (int)(maxCoords.X - minCoords.X);
|
||||
m_sizeY = (int)(maxCoords.Y - minCoords.Y);
|
||||
|
||||
if (!BSTerrainMesh.ConvertHeightmapToMesh(PhysicsScene, initialMap,
|
||||
m_sizeX, m_sizeY,
|
||||
(float)m_sizeX, (float)m_sizeY,
|
||||
Vector3.Zero, 1.0f,
|
||||
out indicesCount, out indices, out verticesCount, out vertices))
|
||||
{
|
||||
// DISASTER!!
|
||||
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap", 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);
|
||||
|
||||
m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
|
||||
indicesCount, indices, verticesCount, vertices),
|
||||
BSPhysicsShapeType.SHAPE_MESH);
|
||||
if (!m_terrainShape.HasPhysicalShape)
|
||||
{
|
||||
// DISASTER!!
|
||||
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", 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;
|
||||
}
|
||||
|
||||
Vector3 pos = regionBase;
|
||||
Quaternion rot = Quaternion.Identity;
|
||||
|
||||
m_terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2( m_terrainShape.ptr, ID, pos, rot));
|
||||
if (!m_terrainBody.HasPhysicalBody)
|
||||
{
|
||||
// DISASTER!!
|
||||
physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase);
|
||||
// Something is very messed up and a crash is in our future.
|
||||
return;
|
||||
}
|
||||
|
||||
// Set current terrain attributes
|
||||
BulletSimAPI.SetFriction2(m_terrainBody.ptr, BSParam.TerrainFriction);
|
||||
BulletSimAPI.SetHitFraction2(m_terrainBody.ptr, BSParam.TerrainHitFraction);
|
||||
BulletSimAPI.SetRestitution2(m_terrainBody.ptr, BSParam.TerrainRestitution);
|
||||
BulletSimAPI.SetCollisionFlags2(m_terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
|
||||
|
||||
// Static objects are not very massive.
|
||||
BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero);
|
||||
|
||||
// Put the new terrain to the world of physical objects
|
||||
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr, pos, rot);
|
||||
|
||||
// Redo its bounding box now that it is in the world
|
||||
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr);
|
||||
|
||||
m_terrainBody.collisionType = CollisionType.Terrain;
|
||||
m_terrainBody.ApplyCollisionMask();
|
||||
|
||||
// Make it so the terrain will not move or be considered for movement.
|
||||
BulletSimAPI.ForceActivationState2(m_terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (m_terrainBody.HasPhysicalBody)
|
||||
{
|
||||
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
|
||||
// Frees both the body and the shape.
|
||||
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_terrainBody.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetTerrainHeightAtXYZ(Vector3 pos)
|
||||
{
|
||||
// For the moment use the saved heightmap to get the terrain height.
|
||||
// TODO: raycast downward to find the true terrain below the position.
|
||||
float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
|
||||
|
||||
int mapIndex = (int)pos.Y * m_sizeY + (int)pos.X;
|
||||
try
|
||||
{
|
||||
ret = m_savedHeightMap[mapIndex];
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Sometimes they give us wonky values of X and Y. Give a warning and return something.
|
||||
PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
|
||||
LogHeader, TerrainBase, pos);
|
||||
ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The passed position is relative to the base of the region.
|
||||
public override float GetWaterLevelAtXYZ(Vector3 pos)
|
||||
{
|
||||
return PhysicsScene.SimpleWaterLevel;
|
||||
}
|
||||
|
||||
// Convert the passed heightmap to mesh information suitable for CreateMeshShape2().
|
||||
// Return 'true' if successfully created.
|
||||
public static bool ConvertHeightmapToMesh(
|
||||
BSScene physicsScene,
|
||||
float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap
|
||||
float extentX, float extentY, // zero based range for output vertices
|
||||
Vector3 extentBase, // base to be added to all vertices
|
||||
float magnification, // number of vertices to create between heightMap coords
|
||||
out int indicesCountO, out int[] indicesO,
|
||||
out int verticesCountO, out float[] verticesO)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
int indicesCount = 0;
|
||||
int verticesCount = 0;
|
||||
int[] indices = new int[0];
|
||||
float[] vertices = new float[0];
|
||||
|
||||
// Simple mesh creation which assumes magnification == 1.
|
||||
// TODO: do a more general solution that scales, adds new vertices and smoothes the result.
|
||||
|
||||
// Create an array of vertices that is sizeX+1 by sizeY+1 (note the loop
|
||||
// from zero to <= sizeX). The triangle indices are then generated as two triangles
|
||||
// per heightmap point. There are sizeX by sizeY of these squares. The extra row and
|
||||
// column of vertices are used to complete the triangles of the last row and column
|
||||
// of the heightmap.
|
||||
try
|
||||
{
|
||||
// One vertice per heightmap value plus the vertices off the top and bottom edge.
|
||||
int totalVertices = (sizeX + 1) * (sizeY + 1);
|
||||
vertices = new float[totalVertices * 3];
|
||||
int totalIndices = sizeX * sizeY * 6;
|
||||
indices = new int[totalIndices];
|
||||
|
||||
float magX = (float)sizeX / extentX;
|
||||
float magY = (float)sizeY / extentY;
|
||||
physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}",
|
||||
BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY);
|
||||
float minHeight = float.MaxValue;
|
||||
// Note that sizeX+1 vertices are created since there is land between this and the next region.
|
||||
for (int yy = 0; yy <= sizeY; yy++)
|
||||
{
|
||||
for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we go around sizeX + 1 times
|
||||
{
|
||||
int offset = yy * sizeX + xx;
|
||||
// Extend the height with the height from the last row or column
|
||||
if (yy == sizeY) offset -= sizeX;
|
||||
if (xx == sizeX) offset -= 1;
|
||||
float height = heightMap[offset];
|
||||
minHeight = Math.Min(minHeight, height);
|
||||
vertices[verticesCount + 0] = (float)xx * magX + extentBase.X;
|
||||
vertices[verticesCount + 1] = (float)yy * magY + extentBase.Y;
|
||||
vertices[verticesCount + 2] = height + extentBase.Z;
|
||||
verticesCount += 3;
|
||||
}
|
||||
}
|
||||
verticesCount = verticesCount / 3;
|
||||
|
||||
for (int yy = 0; yy < sizeY; yy++)
|
||||
{
|
||||
for (int xx = 0; xx < sizeX; xx++)
|
||||
{
|
||||
int offset = yy * (sizeX + 1) + xx;
|
||||
// Each vertices is presumed to be the upper left corner of a box of two triangles
|
||||
indices[indicesCount + 0] = offset;
|
||||
indices[indicesCount + 1] = offset + 1;
|
||||
indices[indicesCount + 2] = offset + sizeX + 1; // accounting for the extra column
|
||||
indices[indicesCount + 3] = offset + 1;
|
||||
indices[indicesCount + 4] = offset + sizeX + 2;
|
||||
indices[indicesCount + 5] = offset + sizeX + 1;
|
||||
indicesCount += 6;
|
||||
}
|
||||
}
|
||||
|
||||
ret = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}",
|
||||
LogHeader, physicsScene.RegionName, extentBase, e);
|
||||
}
|
||||
|
||||
indicesCountO = indicesCount;
|
||||
indicesO = indices;
|
||||
verticesCountO = verticesCount;
|
||||
verticesO = vertices;
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,280 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OMV = OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
// Classes to allow some type checking for the API
|
||||
// These hold pointers to allocated objects in the unmanaged space.
|
||||
|
||||
// The physics engine controller class created at initialization
|
||||
public struct BulletWorld
|
||||
{
|
||||
public BulletWorld(uint worldId, BSScene bss, object xx)
|
||||
{
|
||||
ptr = xx;
|
||||
worldID = worldId;
|
||||
physicsScene = bss;
|
||||
}
|
||||
public object ptr;
|
||||
public uint worldID;
|
||||
// The scene is only in here so very low level routines have a handle to print debug/error messages
|
||||
public BSScene physicsScene;
|
||||
}
|
||||
|
||||
// An allocated Bullet btRigidBody
|
||||
public struct BulletBody
|
||||
{
|
||||
public BulletBody(uint id) : this(id, null)
|
||||
{
|
||||
}
|
||||
public BulletBody(uint id, object xx)
|
||||
{
|
||||
ID = id;
|
||||
ptr = xx;
|
||||
collisionType = CollisionType.Static;
|
||||
}
|
||||
public object ptr;
|
||||
public uint ID;
|
||||
public CollisionType collisionType;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ptr = null;
|
||||
}
|
||||
public bool HasPhysicalBody { get { return ptr != null; } }
|
||||
|
||||
// Apply the specificed collision mask into the physical world
|
||||
public void ApplyCollisionMask()
|
||||
{
|
||||
// Should assert the body has been added to the physical world.
|
||||
// (The collision masks are stored in the collision proxy cache which only exists for
|
||||
// a collision body that is in the world.)
|
||||
BulletSimAPI.SetCollisionGroupMask2(ptr,
|
||||
BulletSimData.CollisionTypeMasks[collisionType].group,
|
||||
BulletSimData.CollisionTypeMasks[collisionType].mask);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder buff = new StringBuilder();
|
||||
buff.Append("<id=");
|
||||
buff.Append(ID.ToString());
|
||||
buff.Append(",p=");
|
||||
buff.Append(ptr.ToString());
|
||||
buff.Append(",c=");
|
||||
buff.Append(collisionType);
|
||||
buff.Append(">");
|
||||
return buff.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public struct BulletShape
|
||||
{
|
||||
public BulletShape(object xx) : this(xx, BSPhysicsShapeType.SHAPE_UNKNOWN)
|
||||
{
|
||||
}
|
||||
public BulletShape(object xx, BSPhysicsShapeType typ)
|
||||
{
|
||||
ptr = xx;
|
||||
type = typ;
|
||||
shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE;
|
||||
isNativeShape = false;
|
||||
}
|
||||
public object ptr;
|
||||
public BSPhysicsShapeType type;
|
||||
public System.UInt64 shapeKey;
|
||||
public bool isNativeShape;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ptr = null;
|
||||
}
|
||||
public bool HasPhysicalShape { get { return ptr != null; } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder buff = new StringBuilder();
|
||||
buff.Append("<p=");
|
||||
buff.Append(ptr.ToString());
|
||||
buff.Append(",s=");
|
||||
buff.Append(type.ToString());
|
||||
buff.Append(",k=");
|
||||
buff.Append(shapeKey.ToString("X"));
|
||||
buff.Append(",n=");
|
||||
buff.Append(isNativeShape.ToString());
|
||||
buff.Append(">");
|
||||
return buff.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
// An allocated Bullet btConstraint
|
||||
public struct BulletConstraint
|
||||
{
|
||||
public BulletConstraint(object xx)
|
||||
{
|
||||
ptr = xx;
|
||||
}
|
||||
public object ptr;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ptr = null;
|
||||
}
|
||||
public bool HasPhysicalConstraint { get { return ptr != null; } }
|
||||
}
|
||||
|
||||
// An allocated HeightMapThing which holds various heightmap info.
|
||||
// Made a class rather than a struct so there would be only one
|
||||
// instance of this and C# will pass around pointers rather
|
||||
// than making copies.
|
||||
public class BulletHeightMapInfo
|
||||
{
|
||||
public BulletHeightMapInfo(uint id, float[] hm, object xx) {
|
||||
ID = id;
|
||||
Ptr = xx;
|
||||
heightMap = hm;
|
||||
terrainRegionBase = OMV.Vector3.Zero;
|
||||
minCoords = new OMV.Vector3(100f, 100f, 25f);
|
||||
maxCoords = new OMV.Vector3(101f, 101f, 26f);
|
||||
minZ = maxZ = 0f;
|
||||
sizeX = sizeY = 256f;
|
||||
}
|
||||
public uint ID;
|
||||
public object Ptr;
|
||||
public float[] heightMap;
|
||||
public OMV.Vector3 terrainRegionBase;
|
||||
public OMV.Vector3 minCoords;
|
||||
public OMV.Vector3 maxCoords;
|
||||
public float sizeX, sizeY;
|
||||
public float minZ, maxZ;
|
||||
public BulletShape terrainShape;
|
||||
public BulletBody terrainBody;
|
||||
|
||||
public float collisionMargin { get; set; }
|
||||
}
|
||||
|
||||
// The general class of collsion object.
|
||||
public enum CollisionType
|
||||
{
|
||||
Avatar,
|
||||
Groundplane,
|
||||
Terrain,
|
||||
Static,
|
||||
Dynamic,
|
||||
VolumeDetect,
|
||||
// Linkset, // A linkset should be either Static or Dynamic
|
||||
LinksetChild,
|
||||
Unknown
|
||||
};
|
||||
|
||||
// Hold specification of group and mask collision flags for a CollisionType
|
||||
public struct CollisionTypeFilterGroup
|
||||
{
|
||||
public CollisionTypeFilterGroup(CollisionType t, uint g, uint m)
|
||||
{
|
||||
type = t;
|
||||
group = g;
|
||||
mask = m;
|
||||
}
|
||||
public CollisionType type;
|
||||
public uint group;
|
||||
public uint mask;
|
||||
};
|
||||
|
||||
/* NOTE: old definitions kept for reference. Delete when things are working.
|
||||
// The collsion filters and masked are defined in one place -- don't want them scattered
|
||||
AvatarGroup = BCharacterGroup,
|
||||
AvatarMask = BAllGroup,
|
||||
ObjectGroup = BSolidGroup,
|
||||
ObjectMask = BAllGroup,
|
||||
StaticObjectGroup = BStaticGroup,
|
||||
StaticObjectMask = AvatarGroup | ObjectGroup, // static things don't interact with much
|
||||
LinksetGroup = BLinksetGroup,
|
||||
LinksetMask = BAllGroup,
|
||||
LinksetChildGroup = BLinksetChildGroup,
|
||||
LinksetChildMask = BNoneGroup, // Linkset children disappear from the world
|
||||
VolumeDetectGroup = BSensorTrigger,
|
||||
VolumeDetectMask = ~BSensorTrigger,
|
||||
TerrainGroup = BTerrainGroup,
|
||||
TerrainMask = BAllGroup & ~BStaticGroup, // static objects on the ground don't collide
|
||||
GroundPlaneGroup = BGroundPlaneGroup,
|
||||
GroundPlaneMask = BAllGroup
|
||||
*/
|
||||
|
||||
public static class BulletSimData
|
||||
{
|
||||
|
||||
// Map of collisionTypes to flags for collision groups and masks.
|
||||
// As mentioned above, don't use the CollisionFilterGroups definitions directly in the code
|
||||
// but, instead, use references to this dictionary. Finding and debugging
|
||||
// collision flag problems will be made easier.
|
||||
public static Dictionary<CollisionType, CollisionTypeFilterGroup> CollisionTypeMasks
|
||||
= new Dictionary<CollisionType, CollisionTypeFilterGroup>()
|
||||
{
|
||||
{ CollisionType.Avatar,
|
||||
new CollisionTypeFilterGroup(CollisionType.Avatar,
|
||||
(uint)CollisionFilterGroups.BCharacterGroup,
|
||||
(uint)CollisionFilterGroups.BAllGroup)
|
||||
},
|
||||
{ CollisionType.Groundplane,
|
||||
new CollisionTypeFilterGroup(CollisionType.Groundplane,
|
||||
(uint)CollisionFilterGroups.BGroundPlaneGroup,
|
||||
(uint)CollisionFilterGroups.BAllGroup)
|
||||
},
|
||||
{ CollisionType.Terrain,
|
||||
new CollisionTypeFilterGroup(CollisionType.Terrain,
|
||||
(uint)CollisionFilterGroups.BTerrainGroup,
|
||||
(uint)(CollisionFilterGroups.BAllGroup & ~CollisionFilterGroups.BStaticGroup))
|
||||
},
|
||||
{ CollisionType.Static,
|
||||
new CollisionTypeFilterGroup(CollisionType.Static,
|
||||
(uint)CollisionFilterGroups.BStaticGroup,
|
||||
(uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup))
|
||||
},
|
||||
{ CollisionType.Dynamic,
|
||||
new CollisionTypeFilterGroup(CollisionType.Dynamic,
|
||||
(uint)CollisionFilterGroups.BSolidGroup,
|
||||
(uint)(CollisionFilterGroups.BAllGroup))
|
||||
},
|
||||
{ CollisionType.VolumeDetect,
|
||||
new CollisionTypeFilterGroup(CollisionType.VolumeDetect,
|
||||
(uint)CollisionFilterGroups.BSensorTrigger,
|
||||
(uint)(~CollisionFilterGroups.BSensorTrigger))
|
||||
},
|
||||
{ CollisionType.LinksetChild,
|
||||
new CollisionTypeFilterGroup(CollisionType.LinksetChild,
|
||||
(uint)CollisionFilterGroups.BTerrainGroup,
|
||||
(uint)(CollisionFilterGroups.BNoneGroup))
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -140,6 +140,25 @@ public struct EntityProperties
|
|||
public Vector3 Velocity;
|
||||
public Vector3 Acceleration;
|
||||
public Vector3 RotationalVelocity;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder buff = new StringBuilder();
|
||||
buff.Append("<i=");
|
||||
buff.Append(ID.ToString());
|
||||
buff.Append(",p=");
|
||||
buff.Append(Position.ToString());
|
||||
buff.Append(",r=");
|
||||
buff.Append(Rotation.ToString());
|
||||
buff.Append(",v=");
|
||||
buff.Append(Velocity.ToString());
|
||||
buff.Append(",a=");
|
||||
buff.Append(Acceleration.ToString());
|
||||
buff.Append(",rv=");
|
||||
buff.Append(RotationalVelocity.ToString());
|
||||
buff.Append(">");
|
||||
return buff.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
// Format of this structure must match the definition in the C++ code
|
||||
|
|
|
@ -56,6 +56,7 @@ public sealed class BSCharacter : BSPhysObject
|
|||
private int _physicsActorType;
|
||||
private bool _isPhysical;
|
||||
private bool _flying;
|
||||
private bool _wasWalking; // 'true' if the avatar was walking/moving last frame
|
||||
private bool _setAlwaysRun;
|
||||
private bool _throttleUpdates;
|
||||
private bool _floatOnWater;
|
||||
|
@ -83,6 +84,7 @@ public sealed class BSCharacter : BSPhysObject
|
|||
_position = pos;
|
||||
|
||||
_flying = isFlying;
|
||||
_wasWalking = true; // causes first step to initialize standing
|
||||
_orientation = OMV.Quaternion.Identity;
|
||||
_velocity = OMV.Vector3.Zero;
|
||||
_buoyancy = ComputeBuoyancyFromFlying(isFlying);
|
||||
|
@ -198,7 +200,48 @@ public sealed class BSCharacter : BSPhysObject
|
|||
// TODO: Decide if the step parameters should be changed depending on the avatar's
|
||||
// state (flying, colliding, ...). There is code in ODE to do this.
|
||||
|
||||
OMV.Vector3 stepVelocity = _velocityMotor.Step(timeStep);
|
||||
// COMMENTARY: when the user is making the avatar walk, except for falling, the velocity
|
||||
// specified for the avatar is the one that should be used. For falling, if the avatar
|
||||
// is not flying and is not colliding then it is presumed to be falling and the Z
|
||||
// component is not fooled with (thus allowing gravity to do its thing).
|
||||
// When the avatar is standing, though, the user has specified a velocity of zero and
|
||||
// the avatar should be standing. But if the avatar is pushed by something in the world
|
||||
// (raising elevator platform, moving vehicle, ...) the avatar should be allowed to
|
||||
// move. Thus, the velocity cannot be forced to zero. The problem is that small velocity
|
||||
// errors can creap in and the avatar will slowly float off in some direction.
|
||||
// So, the problem is that, when an avatar is standing, we cannot tell creaping error
|
||||
// from real pushing.OMV.Vector3.Zero;
|
||||
// The code below keeps setting the velocity to zero hoping the world will keep pushing.
|
||||
|
||||
_velocityMotor.Step(timeStep);
|
||||
|
||||
// If we're not supposed to be moving, make sure things are zero.
|
||||
if (_velocityMotor.ErrorIsZero() && _velocityMotor.TargetValue == OMV.Vector3.Zero && IsColliding)
|
||||
{
|
||||
// The avatar shouldn't be moving
|
||||
_velocityMotor.Zero();
|
||||
ZeroMotion(true /* inTaintTime */);
|
||||
|
||||
// Standing has more friction on the ground
|
||||
if (_currentFriction != BSParam.AvatarStandingFriction)
|
||||
{
|
||||
_currentFriction = BSParam.AvatarStandingFriction;
|
||||
PhysicsScene.PE.SetFriction(PhysBody, _currentFriction);
|
||||
}
|
||||
DetailLog("{0},BSCharacter.MoveMotor,taint,stopping,target={1}", LocalID, _velocityMotor.TargetValue);
|
||||
|
||||
_wasWalking = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
OMV.Vector3 stepVelocity = _velocityMotor.CurrentValue;
|
||||
|
||||
if (_currentFriction != BSParam.AvatarFriction)
|
||||
{
|
||||
// Probably starting up walking. Set friction to moving friction.
|
||||
_currentFriction = BSParam.AvatarFriction;
|
||||
PhysicsScene.PE.SetFriction(PhysBody, _currentFriction);
|
||||
}
|
||||
|
||||
// If falling, we keep the world's downward vector no matter what the other axis specify.
|
||||
if (!Flying && !IsColliding)
|
||||
|
@ -215,8 +258,10 @@ public sealed class BSCharacter : BSPhysObject
|
|||
// Add special movement force to allow avatars to walk up stepped surfaces.
|
||||
moveForce += WalkUpStairs();
|
||||
|
||||
// DetailLog("{0},BSCharacter.MoveMotor,move,stepVel={1},vel={2},mass={3},moveForce={4}", LocalID, stepVelocity, _velocity, Mass, moveForce);
|
||||
DetailLog("{0},BSCharacter.MoveMotor,move,stepVel={1},vel={2},mass={3},moveForce={4}", LocalID, stepVelocity, _velocity, Mass, moveForce);
|
||||
PhysicsScene.PE.ApplyCentralImpulse(PhysBody, moveForce);
|
||||
_wasWalking = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -559,27 +604,6 @@ public sealed class BSCharacter : BSPhysObject
|
|||
PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity");
|
||||
|
||||
_velocity = value;
|
||||
// Depending on whether the avatar is moving or not, change the friction
|
||||
// to keep the avatar from slipping around
|
||||
if (_velocity.Length() == 0)
|
||||
{
|
||||
if (_currentFriction != BSParam.AvatarStandingFriction)
|
||||
{
|
||||
_currentFriction = BSParam.AvatarStandingFriction;
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
PhysicsScene.PE.SetFriction(PhysBody, _currentFriction);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_currentFriction != BSParam.AvatarFriction)
|
||||
{
|
||||
_currentFriction = BSParam.AvatarFriction;
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
PhysicsScene.PE.SetFriction(PhysBody, _currentFriction);
|
||||
}
|
||||
}
|
||||
|
||||
PhysicsScene.PE.SetLinearVelocity(PhysBody, _velocity);
|
||||
PhysicsScene.PE.Activate(PhysBody, true);
|
||||
}
|
||||
|
@ -652,6 +676,9 @@ public sealed class BSCharacter : BSPhysObject
|
|||
public override bool IsStatic {
|
||||
get { return false; }
|
||||
}
|
||||
public override bool IsPhysicallyActive {
|
||||
get { return true; }
|
||||
}
|
||||
public override bool Flying {
|
||||
get { return _flying; }
|
||||
set {
|
||||
|
@ -850,7 +877,14 @@ public sealed class BSCharacter : BSPhysObject
|
|||
{
|
||||
_position = entprop.Position;
|
||||
_orientation = entprop.Rotation;
|
||||
|
||||
// Smooth velocity. OpenSimulator is VERY sensitive to changes in velocity of the avatar
|
||||
// and will send agent updates to the clients if velocity changes by more than
|
||||
// 0.001m/s. Bullet introduces a lot of jitter in the velocity which causes many
|
||||
// extra updates.
|
||||
if (!entprop.Velocity.ApproxEquals(_velocity, 0.1f))
|
||||
_velocity = entprop.Velocity;
|
||||
|
||||
_acceleration = entprop.Acceleration;
|
||||
_rotationalVelocity = entprop.RotationalVelocity;
|
||||
|
||||
|
@ -865,7 +899,7 @@ public sealed class BSCharacter : BSPhysObject
|
|||
CurrentEntityProperties = entprop;
|
||||
|
||||
// Tell the linkset about value changes
|
||||
Linkset.UpdateProperties(this, true);
|
||||
Linkset.UpdateProperties(UpdatedProperties.EntPropUpdates, this);
|
||||
|
||||
// Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
|
||||
// base.RequestPhysicsterseUpdate();
|
||||
|
|
|
@ -35,6 +35,7 @@ using System.Collections.Generic;
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSPlugin
|
||||
|
@ -154,7 +155,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
// Return 'true' if this vehicle is doing vehicle things
|
||||
public bool IsActive
|
||||
{
|
||||
get { return (Type != Vehicle.TYPE_NONE && !Prim.IsStatic); }
|
||||
get { return (Type != Vehicle.TYPE_NONE && Prim.IsPhysicallyActive); }
|
||||
}
|
||||
|
||||
#region Vehicle parameter setting
|
||||
|
@ -230,6 +231,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
break;
|
||||
case Vehicle.ANGULAR_MOTOR_DIRECTION:
|
||||
m_angularMotorDirection = new Vector3(pValue, pValue, pValue);
|
||||
m_angularMotor.Zero();
|
||||
m_angularMotor.SetTarget(m_angularMotorDirection);
|
||||
break;
|
||||
case Vehicle.LINEAR_FRICTION_TIMESCALE:
|
||||
|
@ -263,6 +265,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
pValue.Y = ClampInRange(-12.56f, pValue.Y, 12.56f);
|
||||
pValue.Z = ClampInRange(-12.56f, pValue.Z, 12.56f);
|
||||
m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
|
||||
m_angularMotor.Zero();
|
||||
m_angularMotor.SetTarget(m_angularMotorDirection);
|
||||
break;
|
||||
case Vehicle.LINEAR_FRICTION_TIMESCALE:
|
||||
|
@ -687,7 +690,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
// Bullet does a bunch of smoothing for changing parameters.
|
||||
// Since the vehicle is demanding this setting, we override Bullet's smoothing
|
||||
// by telling Bullet the value was the same last time.
|
||||
PhysicsScene.PE.SetInterpolationLinearVelocity(Prim.PhysBody, m_knownVelocity);
|
||||
// PhysicsScene.PE.SetInterpolationLinearVelocity(Prim.PhysBody, m_knownVelocity);
|
||||
}
|
||||
|
||||
if ((m_knownChanged & m_knownChangedForce) != 0)
|
||||
|
@ -699,7 +702,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
if ((m_knownChanged & m_knownChangedRotationalVelocity) != 0)
|
||||
{
|
||||
Prim.ForceRotationalVelocity = m_knownRotationalVelocity;
|
||||
PhysicsScene.PE.SetInterpolationAngularVelocity(Prim.PhysBody, m_knownRotationalVelocity);
|
||||
// PhysicsScene.PE.SetInterpolationAngularVelocity(Prim.PhysBody, m_knownRotationalVelocity);
|
||||
}
|
||||
|
||||
if ((m_knownChanged & m_knownChangedRotationalImpulse) != 0)
|
||||
|
@ -719,10 +722,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
|
||||
// Since the computation of terrain height can be a little involved, this routine
|
||||
// is used to fetch the height only once for each vehicle simulation step.
|
||||
Vector3 lastRememberedHeightPos;
|
||||
private float GetTerrainHeight(Vector3 pos)
|
||||
{
|
||||
if ((m_knownHas & m_knownChangedTerrainHeight) == 0)
|
||||
if ((m_knownHas & m_knownChangedTerrainHeight) == 0 || pos != lastRememberedHeightPos)
|
||||
{
|
||||
lastRememberedHeightPos = pos;
|
||||
m_knownTerrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos);
|
||||
m_knownHas |= m_knownChangedTerrainHeight;
|
||||
}
|
||||
|
@ -942,10 +947,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
// ==================================================================
|
||||
// Clamp high or low velocities
|
||||
float newVelocityLengthSq = VehicleVelocity.LengthSquared();
|
||||
if (newVelocityLengthSq > 1000f)
|
||||
if (newVelocityLengthSq > BSParam.VehicleMaxLinearVelocity)
|
||||
{
|
||||
VehicleVelocity /= VehicleVelocity.Length();
|
||||
VehicleVelocity *= 1000f;
|
||||
VehicleVelocity *= BSParam.VehicleMaxLinearVelocity;
|
||||
}
|
||||
else if (newVelocityLengthSq < 0.001f)
|
||||
VehicleVelocity = Vector3.Zero;
|
||||
|
@ -956,39 +961,25 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
|
||||
public void ComputeLinearVelocity(float pTimestep)
|
||||
{
|
||||
Vector3 linearMotorStep = m_linearMotor.Step(pTimestep);
|
||||
// Step the motor from the current value. Get the correction needed this step.
|
||||
Vector3 currentVel = VehicleVelocity * Quaternion.Inverse(VehicleOrientation);
|
||||
Vector3 linearMotorCorrectionV = m_linearMotor.Step(pTimestep, currentVel);
|
||||
|
||||
// The movement computed in the linear motor is relative to the vehicle
|
||||
// coordinates. Rotate the movement to world coordinates.
|
||||
Vector3 linearMotorVelocity = linearMotorStep * VehicleOrientation;
|
||||
// Motor is vehicle coordinates. Rotate it to world coordinates
|
||||
Vector3 linearMotorVelocityW = linearMotorCorrectionV * VehicleOrientation;
|
||||
|
||||
// If we're a ground vehicle, don't loose any Z action (like gravity acceleration).
|
||||
float mixFactor = 1f; // 1 means use all linear motor Z value, 0 means use all existing Z
|
||||
// If we're a ground vehicle, don't add any upward Z movement
|
||||
if ((m_flags & VehicleFlag.LIMIT_MOTOR_UP) != 0)
|
||||
{
|
||||
if (!Prim.IsColliding)
|
||||
{
|
||||
// If a ground vehicle and not on the ground, I want gravity effect
|
||||
mixFactor = 0.2f;
|
||||
if (linearMotorVelocityW.Z > 0f)
|
||||
linearMotorVelocityW.Z = 0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// I'm not a ground vehicle but don't totally loose the effect of the environment
|
||||
mixFactor = 0.8f;
|
||||
}
|
||||
linearMotorVelocity.Z = mixFactor * linearMotorVelocity.Z + (1f - mixFactor) * VehicleVelocity.Z;
|
||||
|
||||
// What we want to contribute to the vehicle's existing velocity
|
||||
Vector3 linearMotorForce = linearMotorVelocity - VehicleVelocity;
|
||||
// Add this correction to the velocity to make it faster/slower.
|
||||
VehicleVelocity += linearMotorVelocityW;
|
||||
|
||||
// Act against the inertia of the vehicle
|
||||
linearMotorForce *= m_vehicleMass;
|
||||
|
||||
VehicleAddForceImpulse(linearMotorForce * pTimestep);
|
||||
|
||||
VDetailLog("{0}, MoveLinear,velocity,vehVel={1},step={2},stepVel={3},mix={4},force={5}",
|
||||
Prim.LocalID, VehicleVelocity, linearMotorStep, linearMotorVelocity, mixFactor, linearMotorForce);
|
||||
VDetailLog("{0}, MoveLinear,velocity,vehVel={1},correction={2},force={3}",
|
||||
Prim.LocalID, VehicleVelocity, linearMotorCorrectionV, linearMotorVelocityW);
|
||||
}
|
||||
|
||||
public void ComputeLinearTerrainHeightCorrection(float pTimestep)
|
||||
|
@ -1132,8 +1123,6 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
// a downward raycast to find what is below.
|
||||
public void ComputeLinearMotorUp(float pTimestep)
|
||||
{
|
||||
Vector3 ret = Vector3.Zero;
|
||||
|
||||
if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
|
||||
{
|
||||
// This code tries to decide if the object is not on the ground and then pushing down
|
||||
|
@ -1201,62 +1190,27 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
// set directly on the vehicle.
|
||||
private void MoveAngular(float pTimestep)
|
||||
{
|
||||
// The user wants this many radians per second angular change?
|
||||
Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep);
|
||||
// VehicleRotationalVelocity = Vector3.Zero;
|
||||
|
||||
ComputeAngularTurning(pTimestep);
|
||||
|
||||
ComputeAngularVerticalAttraction();
|
||||
|
||||
ComputeAngularDeflection();
|
||||
|
||||
ComputeAngularBanking();
|
||||
|
||||
// ==================================================================
|
||||
// From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
|
||||
// This flag prevents linear deflection parallel to world z-axis. This is useful
|
||||
// for preventing ground vehicles with large linear deflection, like bumper cars,
|
||||
// from climbing their linear deflection into the sky.
|
||||
// That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement
|
||||
// TODO: This is here because this is where ODE put it but documentation says it
|
||||
// is a linear effect. Where should this check go?
|
||||
if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
|
||||
{
|
||||
angularMotorContribution.X = 0f;
|
||||
angularMotorContribution.Y = 0f;
|
||||
VDetailLog("{0}, MoveAngular,noDeflectionUp,angularMotorContrib={1}", Prim.LocalID, angularMotorContribution);
|
||||
}
|
||||
|
||||
Vector3 verticalAttractionContribution = ComputeAngularVerticalAttraction();
|
||||
|
||||
Vector3 deflectionContribution = ComputeAngularDeflection();
|
||||
|
||||
Vector3 bankingContribution = ComputeAngularBanking();
|
||||
|
||||
// ==================================================================
|
||||
m_lastVertAttractor = verticalAttractionContribution;
|
||||
|
||||
m_lastAngularVelocity = angularMotorContribution
|
||||
+ verticalAttractionContribution
|
||||
+ deflectionContribution
|
||||
+ bankingContribution;
|
||||
|
||||
// Add of the above computation are made relative to vehicle coordinates.
|
||||
// Convert to world coordinates.
|
||||
m_lastAngularVelocity *= VehicleOrientation;
|
||||
|
||||
// ==================================================================
|
||||
// Apply the correction velocity.
|
||||
// TODO: Should this be applied as an angular force (torque)?
|
||||
if (!m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f))
|
||||
{
|
||||
VehicleRotationalVelocity = m_lastAngularVelocity;
|
||||
|
||||
VDetailLog("{0}, MoveAngular,done,nonZero,angMotorContrib={1},vertAttrContrib={2},bankContrib={3},deflectContrib={4},totalContrib={5}",
|
||||
Prim.LocalID,
|
||||
angularMotorContribution, verticalAttractionContribution,
|
||||
bankingContribution, deflectionContribution,
|
||||
m_lastAngularVelocity
|
||||
);
|
||||
}
|
||||
else
|
||||
if (VehicleRotationalVelocity.ApproxEquals(Vector3.Zero, 0.01f))
|
||||
{
|
||||
// The vehicle is not adding anything angular wise.
|
||||
VehicleRotationalVelocity = Vector3.Zero;
|
||||
VDetailLog("{0}, MoveAngular,done,zero", Prim.LocalID);
|
||||
}
|
||||
else
|
||||
{
|
||||
VDetailLog("{0}, MoveAngular,done,nonZero,angVel={1}", Prim.LocalID, VehicleRotationalVelocity);
|
||||
}
|
||||
|
||||
// ==================================================================
|
||||
//Offset section
|
||||
|
@ -1290,6 +1244,31 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
private void ComputeAngularTurning(float pTimestep)
|
||||
{
|
||||
// The user wants this many radians per second angular change?
|
||||
Vector3 currentAngularV = VehicleRotationalVelocity * Quaternion.Inverse(VehicleOrientation);
|
||||
Vector3 angularMotorContributionV = m_angularMotor.Step(pTimestep, currentAngularV);
|
||||
|
||||
// ==================================================================
|
||||
// From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
|
||||
// This flag prevents linear deflection parallel to world z-axis. This is useful
|
||||
// for preventing ground vehicles with large linear deflection, like bumper cars,
|
||||
// from climbing their linear deflection into the sky.
|
||||
// That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement
|
||||
// TODO: This is here because this is where ODE put it but documentation says it
|
||||
// is a linear effect. Where should this check go?
|
||||
if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
|
||||
{
|
||||
angularMotorContributionV.X = 0f;
|
||||
angularMotorContributionV.Y = 0f;
|
||||
}
|
||||
|
||||
VehicleRotationalVelocity += angularMotorContributionV * VehicleOrientation;
|
||||
VDetailLog("{0}, MoveAngular,angularTurning,angularMotorContrib={1}", Prim.LocalID, angularMotorContributionV);
|
||||
}
|
||||
|
||||
// From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
|
||||
// Some vehicles, like boats, should always keep their up-side up. This can be done by
|
||||
// enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to
|
||||
|
@ -1298,13 +1277,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
// and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An
|
||||
// efficiency of 0.0 will cause the spring to wobble around its equilibrium, while an
|
||||
// efficiency of 1.0 will cause the spring to reach its equilibrium with exponential decay.
|
||||
public Vector3 ComputeAngularVerticalAttraction()
|
||||
public void ComputeAngularVerticalAttraction()
|
||||
{
|
||||
Vector3 ret = Vector3.Zero;
|
||||
|
||||
// If vertical attaction timescale is reasonable
|
||||
if (enableAngularVerticalAttraction && m_verticalAttractionTimescale < m_verticalAttractionCutoff)
|
||||
{
|
||||
Vector3 vertContributionV = Vector3.Zero;
|
||||
|
||||
// Take a vector pointing up and convert it from world to vehicle relative coords.
|
||||
Vector3 verticalError = Vector3.UnitZ * VehicleOrientation;
|
||||
|
||||
|
@ -1318,37 +1297,36 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
|
||||
// Y error means needed rotation around X axis and visa versa.
|
||||
// Since the error goes from zero to one, the asin is the corresponding angle.
|
||||
ret.X = (float)Math.Asin(verticalError.Y);
|
||||
vertContributionV.X = (float)Math.Asin(verticalError.Y);
|
||||
// (Tilt forward (positive X) needs to tilt back (rotate negative) around Y axis.)
|
||||
ret.Y = -(float)Math.Asin(verticalError.X);
|
||||
vertContributionV.Y = -(float)Math.Asin(verticalError.X);
|
||||
|
||||
// If verticalError.Z is negative, the vehicle is upside down. Add additional push.
|
||||
if (verticalError.Z < 0f)
|
||||
{
|
||||
ret.X += PIOverFour;
|
||||
ret.Y += PIOverFour;
|
||||
vertContributionV.X += PIOverFour;
|
||||
// vertContribution.Y -= PIOverFour;
|
||||
}
|
||||
|
||||
// 'ret' is now the necessary velocity 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.
|
||||
Vector3 unscaledContrib = ret;
|
||||
ret /= m_verticalAttractionTimescale;
|
||||
Vector3 unscaledContrib = vertContributionV; // DEBUG DEBUG
|
||||
vertContributionV /= m_verticalAttractionTimescale;
|
||||
|
||||
VehicleRotationalVelocity += vertContributionV * VehicleOrientation;
|
||||
|
||||
VDetailLog("{0}, MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},eff={3},ts={4},vertAttr={5}",
|
||||
Prim.LocalID, verticalError, unscaledContrib, m_verticalAttractionEfficiency, m_verticalAttractionTimescale, ret);
|
||||
Prim.LocalID, verticalError, unscaledContrib, m_verticalAttractionEfficiency, m_verticalAttractionTimescale, vertContributionV);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Return the angular correction to correct the direction the vehicle is pointing to be
|
||||
// Angular correction to correct the direction the vehicle is pointing to be
|
||||
// the direction is should want to be pointing.
|
||||
// The vehicle is moving in some direction and correct its orientation to it is pointing
|
||||
// in that direction.
|
||||
// TODO: implement reference frame.
|
||||
public Vector3 ComputeAngularDeflection()
|
||||
public void ComputeAngularDeflection()
|
||||
{
|
||||
Vector3 ret = Vector3.Zero;
|
||||
|
||||
// Since angularMotorUp and angularDeflection are computed independently, they will calculate
|
||||
// approximately the same X or Y correction. When added together (when contributions are combined)
|
||||
// this creates an over-correction and then wabbling as the target is overshot.
|
||||
|
@ -1356,6 +1334,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
|
||||
if (enableAngularDeflection && m_angularDeflectionEfficiency != 0 && VehicleForwardSpeed > 0.2)
|
||||
{
|
||||
Vector3 deflectContributionV = Vector3.Zero;
|
||||
|
||||
// The direction the vehicle is moving
|
||||
Vector3 movingDirection = VehicleVelocity;
|
||||
movingDirection.Normalize();
|
||||
|
@ -1381,18 +1361,19 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
// ret = m_angularDeflectionCorrectionMotor(1f, deflectionError);
|
||||
|
||||
// Scale the correction by recovery timescale and efficiency
|
||||
ret = (-deflectionError) * m_angularDeflectionEfficiency;
|
||||
ret /= m_angularDeflectionTimescale;
|
||||
deflectContributionV = (-deflectionError) * m_angularDeflectionEfficiency;
|
||||
deflectContributionV /= m_angularDeflectionTimescale;
|
||||
|
||||
VehicleRotationalVelocity += deflectContributionV * VehicleOrientation;
|
||||
|
||||
VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}",
|
||||
Prim.LocalID, movingDirection, pointingDirection, deflectionError, ret);
|
||||
Prim.LocalID, movingDirection, pointingDirection, deflectionError, deflectContributionV);
|
||||
VDetailLog("{0}, MoveAngular,Deflection,fwdSpd={1},defEff={2},defTS={3}",
|
||||
Prim.LocalID, VehicleForwardSpeed, m_angularDeflectionEfficiency, m_angularDeflectionTimescale);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Return an angular change to rotate the vehicle around the Z axis when the vehicle
|
||||
// Angular change to rotate the vehicle around the Z axis when the vehicle
|
||||
// is tipped around the X axis.
|
||||
// From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
|
||||
// The vertical attractor feature must be enabled in order for the banking behavior to
|
||||
|
@ -1423,12 +1404,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
// world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to
|
||||
// bank quickly then give it a banking timescale of about a second or less, otherwise you can
|
||||
// make a sluggish vehicle by giving it a timescale of several seconds.
|
||||
public Vector3 ComputeAngularBanking()
|
||||
public void ComputeAngularBanking()
|
||||
{
|
||||
Vector3 ret = Vector3.Zero;
|
||||
|
||||
if (enableAngularBanking && m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff)
|
||||
{
|
||||
Vector3 bankingContributionV = Vector3.Zero;
|
||||
|
||||
// Rotate a UnitZ vector (pointing up) to how the vehicle is oriented.
|
||||
// As the vehicle rolls to the right or left, the Y value will increase from
|
||||
// zero (straight up) to 1 or -1 (full tilt right or left)
|
||||
|
@ -1445,15 +1426,16 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
mixedYawAngle = ClampInRange(-20f, mixedYawAngle, 20f);
|
||||
|
||||
// Build the force vector to change rotation from what it is to what it should be
|
||||
ret.Z = -mixedYawAngle;
|
||||
bankingContributionV.Z = -mixedYawAngle;
|
||||
|
||||
// Don't do it all at once.
|
||||
ret /= m_bankingTimescale;
|
||||
bankingContributionV /= m_bankingTimescale;
|
||||
|
||||
VehicleRotationalVelocity += bankingContributionV * VehicleOrientation;
|
||||
|
||||
VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},rollComp={3},yAng={4},mYAng={5},ret={6}",
|
||||
Prim.LocalID, rollComponents, VehicleForwardSpeed, rollComponents, yawAngle, mixedYawAngle, ret);
|
||||
Prim.LocalID, rollComponents, VehicleForwardSpeed, rollComponents, yawAngle, mixedYawAngle, bankingContributionV);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// This is from previous instantiations of XXXDynamics.cs.
|
||||
|
|
|
@ -252,7 +252,7 @@ public abstract class BSLinkset
|
|||
// of the linkset is received.
|
||||
// Passed flag is update came from physics engine (true) or the user (false).
|
||||
// Called at taint-time!!
|
||||
public abstract void UpdateProperties(BSPhysObject physObject, bool physicalUpdate);
|
||||
public abstract void UpdateProperties(UpdatedProperties whichUpdated, BSPhysObject physObject);
|
||||
|
||||
// Routine used when rebuilding the body of the root of the linkset
|
||||
// Destroy all the constraints have have been made to root.
|
||||
|
@ -261,11 +261,6 @@ public abstract class BSLinkset
|
|||
// Called at taint-time!!
|
||||
public abstract bool RemoveBodyDependencies(BSPrim child);
|
||||
|
||||
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
|
||||
// this routine will restore the removed constraints.
|
||||
// Called at taint-time!!
|
||||
public abstract void RestoreBodyDependencies(BSPrim child);
|
||||
|
||||
// ================================================================
|
||||
protected virtual float ComputeLinksetMass()
|
||||
{
|
||||
|
|
|
@ -51,6 +51,21 @@ sealed class BSLinksetCompoundInfo : BSLinksetInfo
|
|||
OffsetFromCenterOfMass = p;
|
||||
OffsetRot = r;
|
||||
}
|
||||
// 'centerDisplacement' is the distance from the root the the center-of-mass (Bullet 'zero' of the shape)
|
||||
public BSLinksetCompoundInfo(int indx, BSPhysObject root, BSPhysObject child, OMV.Vector3 centerDisplacement)
|
||||
{
|
||||
// Each child position and rotation is given relative to the center-of-mass.
|
||||
OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(root.RawOrientation);
|
||||
OMV.Vector3 displacementFromRoot = (child.RawPosition - root.RawPosition) * invRootOrientation;
|
||||
OMV.Vector3 displacementFromCOM = displacementFromRoot - centerDisplacement;
|
||||
OMV.Quaternion displacementRot = child.RawOrientation * invRootOrientation;
|
||||
|
||||
// Save relative position for recomputing child's world position after moving linkset.
|
||||
Index = indx;
|
||||
OffsetFromRoot = displacementFromRoot;
|
||||
OffsetFromCenterOfMass = displacementFromCOM;
|
||||
OffsetRot = displacementRot;
|
||||
}
|
||||
public override void Clear()
|
||||
{
|
||||
Index = 0;
|
||||
|
@ -182,24 +197,71 @@ public sealed class BSLinksetCompound : BSLinkset
|
|||
|
||||
// 'physicalUpdate' is true if these changes came directly from the physics engine. Don't need to rebuild then.
|
||||
// Called at taint-time.
|
||||
public override void UpdateProperties(BSPhysObject updated, bool physicalUpdate)
|
||||
public override void UpdateProperties(UpdatedProperties whichUpdated, BSPhysObject updated)
|
||||
{
|
||||
// The user moving a child around requires the rebuilding of the linkset compound shape
|
||||
// One problem is this happens when a border is crossed -- the simulator implementation
|
||||
// is to store the position into the group which causes the move of the object
|
||||
// stores the position into the group which causes the move of the object
|
||||
// but it also means all the child positions get updated.
|
||||
// What would cause an unnecessary rebuild so we make sure the linkset is in a
|
||||
// region before bothering to do a rebuild.
|
||||
if (!IsRoot(updated)
|
||||
&& !physicalUpdate
|
||||
&& PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
|
||||
if (!IsRoot(updated) && PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
|
||||
{
|
||||
// TODO: replace this with are calculation of the child prim's orientation and pos.
|
||||
// TODO: for the moment, don't rebuild the compound shape.
|
||||
// This is often just the car turning its wheels. When we can just reorient the one
|
||||
// member shape of the compound shape, the overhead of rebuilding won't be a problem.
|
||||
// updated.LinksetInfo = null;
|
||||
// ScheduleRebuild(updated);
|
||||
// If a child of the linkset is updating only the position or rotation, that can be done
|
||||
// without rebuilding the linkset.
|
||||
// If a handle for the child can be fetch, we update the child here. If a rebuild was
|
||||
// scheduled by someone else, the rebuild will just replace this setting.
|
||||
|
||||
bool updatedChild = false;
|
||||
// Anything other than updating position or orientation usually means a physical update
|
||||
// and that is caused by us updating the object.
|
||||
if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0)
|
||||
{
|
||||
// Gather the child info. It might not be there if the linkset is in transition.
|
||||
BSLinksetCompoundInfo lsi = updated.LinksetInfo as BSLinksetCompoundInfo;
|
||||
if (LinksetRoot.PhysShape.HasPhysicalShape && lsi != null)
|
||||
{
|
||||
if (PhysicsScene.PE.IsCompound(LinksetRoot.PhysShape))
|
||||
{
|
||||
BulletShape linksetChildShape = PhysicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape, lsi.Index);
|
||||
if (linksetChildShape.HasPhysicalShape)
|
||||
{
|
||||
// Compute the offset from the center-of-gravity
|
||||
BSLinksetCompoundInfo newLsi = new BSLinksetCompoundInfo(lsi.Index, LinksetRoot, updated, LinksetRoot.PositionDisplacement);
|
||||
PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, lsi.Index,
|
||||
newLsi.OffsetFromCenterOfMass,
|
||||
newLsi.OffsetRot,
|
||||
true /* shouldRecalculateLocalAabb */);
|
||||
DetailLog("{0},BSLinksetCompound.UpdateProperties,changeChildPosRot,whichUpdated={1}newLsi={2}",
|
||||
updated.LocalID, whichUpdated, newLsi);
|
||||
updated.LinksetInfo = newLsi;
|
||||
updatedChild = true;
|
||||
}
|
||||
else // DEBUG DEBUG
|
||||
{ // DEBUG DEBUG
|
||||
DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noChildShape,shape={1}",
|
||||
updated.LocalID, linksetChildShape);
|
||||
} // DEBUG DEBUG
|
||||
}
|
||||
else // DEBUG DEBUG
|
||||
{ // DEBUG DEBUG
|
||||
DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,notCompound", updated.LocalID);
|
||||
} // DEBUG DEBUG
|
||||
}
|
||||
else // DEBUG DEBUG
|
||||
{ // DEBUG DEBUG
|
||||
DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,rootPhysShape={1},lsi={2}",
|
||||
updated.LocalID, LinksetRoot.PhysShape, lsi == null ? "NULL" : lsi.ToString());
|
||||
} // DEBUG DEBUG
|
||||
if (!updatedChild)
|
||||
{
|
||||
// If couldn't do the individual child, the linkset needs a rebuild to incorporate the new child info.
|
||||
DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild.schedulingRebuild,whichUpdated={1}",
|
||||
updated.LocalID, whichUpdated);
|
||||
updated.LinksetInfo = null; // setting to 'null' causes relative position to be recomputed.
|
||||
ScheduleRebuild(updated);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,13 +290,6 @@ public sealed class BSLinksetCompound : BSLinkset
|
|||
return ret;
|
||||
}
|
||||
|
||||
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
|
||||
// this routine will restore the removed constraints.
|
||||
// Called at taint-time!!
|
||||
public override void RestoreBodyDependencies(BSPrim child)
|
||||
{
|
||||
}
|
||||
|
||||
// When the linkset is built, the child shape is added to the compound shape relative to the
|
||||
// root shape. The linkset then moves around but this does not move the actual child
|
||||
// prim. The child prim's location must be recomputed based on the location of the root shape.
|
||||
|
@ -322,7 +377,7 @@ public sealed class BSLinksetCompound : BSLinkset
|
|||
// Constraint linksets are rebuilt every time.
|
||||
// Note that this works for rebuilding just the root after a linkset is taken apart.
|
||||
// Called at taint time!!
|
||||
private bool disableCOM = true; // disable until we get this debugged
|
||||
private bool disableCOM = false; // disable until we get this debugged
|
||||
private void RecomputeLinksetCompound()
|
||||
{
|
||||
try
|
||||
|
@ -345,12 +400,16 @@ public sealed class BSLinksetCompound : BSLinkset
|
|||
} // DEBUG DEBUG
|
||||
else
|
||||
{
|
||||
centerOfMass = ComputeLinksetGeometricCenter();
|
||||
centerDisplacement = centerOfMass - LinksetRoot.RawPosition;
|
||||
centerOfMass = ComputeLinksetCenterOfMass();
|
||||
// 'centerDisplacement' is the value to *add* to all the shape offsets
|
||||
centerDisplacement = LinksetRoot.RawPosition - centerOfMass;
|
||||
|
||||
// Since we're displacing the center of the shape, we need to move the body in the world
|
||||
LinksetRoot.PositionDisplacement = centerDisplacement;
|
||||
|
||||
// This causes the root prim position to be set properly based on the new PositionDisplacement
|
||||
LinksetRoot.ForcePosition = LinksetRoot.RawPosition;
|
||||
// Update the local transform for the root child shape so it is offset from the <0,0,0> which is COM
|
||||
PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, 0, -centerDisplacement, OMV.Quaternion.Identity, false);
|
||||
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,com={1},rootPos={2},centerDisp={3}",
|
||||
LinksetRoot.LocalID, centerOfMass, LinksetRoot.RawPosition, centerDisplacement);
|
||||
|
@ -372,15 +431,7 @@ public sealed class BSLinksetCompound : BSLinkset
|
|||
BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo;
|
||||
if (lci == null)
|
||||
{
|
||||
// Each child position and rotation is given relative to the center-of-mass.
|
||||
OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
|
||||
OMV.Vector3 displacementFromRoot = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation;
|
||||
OMV.Vector3 displacementFromCOM = displacementFromRoot - centerDisplacement;
|
||||
OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation;
|
||||
|
||||
// Save relative position for recomputing child's world position after moving linkset.
|
||||
lci = new BSLinksetCompoundInfo(memberIndex, displacementFromCOM, displacementRot);
|
||||
lci.OffsetFromRoot = displacementFromRoot;
|
||||
lci = new BSLinksetCompoundInfo(memberIndex, LinksetRoot, cPrim, centerDisplacement);
|
||||
cPrim.LinksetInfo = lci;
|
||||
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci);
|
||||
}
|
||||
|
@ -390,7 +441,7 @@ public sealed class BSLinksetCompound : BSLinkset
|
|||
|
||||
if (cPrim.PhysShape.isNativeShape)
|
||||
{
|
||||
// A native shape is turning into a hull collision shape because native
|
||||
// A native shape is turned into a hull collision shape because native
|
||||
// shapes are not shared so we have to hullify it so it will be tracked
|
||||
// and freed at the correct time. This also solves the scaling problem
|
||||
// (native shapes scaled but hull/meshes are assumed to not be).
|
||||
|
|
|
@ -83,7 +83,7 @@ public sealed class BSLinksetConstraints : BSLinkset
|
|||
}
|
||||
|
||||
// Called at taint-time!!
|
||||
public override void UpdateProperties(BSPhysObject updated, bool inTaintTime)
|
||||
public override void UpdateProperties(UpdatedProperties whichUpdated, BSPhysObject pObj)
|
||||
{
|
||||
// Nothing to do for constraints on property updates
|
||||
}
|
||||
|
@ -110,14 +110,6 @@ public sealed class BSLinksetConstraints : BSLinkset
|
|||
return ret;
|
||||
}
|
||||
|
||||
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
|
||||
// this routine will restore the removed constraints.
|
||||
// Called at taint-time!!
|
||||
public override void RestoreBodyDependencies(BSPrim child)
|
||||
{
|
||||
// The Refresh operation queued by RemoveBodyDependencies() will build any missing constraints.
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
|
||||
// Add a new child to the linkset.
|
||||
|
|
|
@ -138,7 +138,8 @@ public class BSVMotor : BSMotor
|
|||
CurrentValue = TargetValue = Vector3.Zero;
|
||||
}
|
||||
|
||||
// Compute the next step and return the new current value
|
||||
// Compute the next step and return the new current value.
|
||||
// Returns the correction needed to move 'current' to 'target'.
|
||||
public virtual Vector3 Step(float timeStep)
|
||||
{
|
||||
if (!Enabled) return TargetValue;
|
||||
|
@ -148,9 +149,10 @@ public class BSVMotor : BSMotor
|
|||
|
||||
Vector3 correction = Vector3.Zero;
|
||||
Vector3 error = TargetValue - CurrentValue;
|
||||
LastError = error;
|
||||
if (!ErrorIsZero(error))
|
||||
{
|
||||
correction = Step(timeStep, error);
|
||||
correction = StepError(timeStep, error);
|
||||
|
||||
CurrentValue += correction;
|
||||
|
||||
|
@ -187,20 +189,31 @@ public class BSVMotor : BSMotor
|
|||
else
|
||||
{
|
||||
// Difference between what we have and target is small. Motor is done.
|
||||
if (TargetValue.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
|
||||
{
|
||||
// The target can step down to nearly zero but not get there. If close to zero
|
||||
// it is really zero.
|
||||
TargetValue = Vector3.Zero;
|
||||
}
|
||||
CurrentValue = TargetValue;
|
||||
MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}",
|
||||
BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue);
|
||||
MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},currTgt={4},currCurr={5}",
|
||||
BSScene.DetailLogZero, UseName, origCurrVal, origTarget, TargetValue, CurrentValue);
|
||||
}
|
||||
|
||||
return CurrentValue;
|
||||
return correction;
|
||||
}
|
||||
public virtual Vector3 Step(float timeStep, Vector3 error)
|
||||
// version of step that sets the current value before doing the step
|
||||
public virtual Vector3 Step(float timeStep, Vector3 current)
|
||||
{
|
||||
CurrentValue = current;
|
||||
return Step(timeStep);
|
||||
}
|
||||
public virtual Vector3 StepError(float timeStep, Vector3 error)
|
||||
{
|
||||
if (!Enabled) return Vector3.Zero;
|
||||
|
||||
LastError = error;
|
||||
Vector3 returnCorrection = Vector3.Zero;
|
||||
if (!ErrorIsZero())
|
||||
if (!ErrorIsZero(error))
|
||||
{
|
||||
// correction = error / secondsItShouldTakeToCorrect
|
||||
Vector3 correctionAmount;
|
||||
|
@ -302,9 +315,10 @@ public class BSFMotor : BSMotor
|
|||
|
||||
float correction = 0f;
|
||||
float error = TargetValue - CurrentValue;
|
||||
LastError = error;
|
||||
if (!ErrorIsZero(error))
|
||||
{
|
||||
correction = Step(timeStep, error);
|
||||
correction = StepError(timeStep, error);
|
||||
|
||||
CurrentValue += correction;
|
||||
|
||||
|
@ -339,6 +353,12 @@ public class BSFMotor : BSMotor
|
|||
else
|
||||
{
|
||||
// Difference between what we have and target is small. Motor is done.
|
||||
if (Util.InRange<float>(TargetValue, -ErrorZeroThreshold, ErrorZeroThreshold))
|
||||
{
|
||||
// The target can step down to nearly zero but not get there. If close to zero
|
||||
// it is really zero.
|
||||
TargetValue = 0f;
|
||||
}
|
||||
CurrentValue = TargetValue;
|
||||
MDetailLog("{0}, BSFMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}",
|
||||
BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue);
|
||||
|
@ -347,13 +367,12 @@ public class BSFMotor : BSMotor
|
|||
return CurrentValue;
|
||||
}
|
||||
|
||||
public virtual float Step(float timeStep, float error)
|
||||
public virtual float StepError(float timeStep, float error)
|
||||
{
|
||||
if (!Enabled) return 0f;
|
||||
|
||||
LastError = error;
|
||||
float returnCorrection = 0f;
|
||||
if (!ErrorIsZero())
|
||||
if (!ErrorIsZero(error))
|
||||
{
|
||||
// correction = error / secondsItShouldTakeToCorrect
|
||||
float correctionAmount;
|
||||
|
@ -440,8 +459,8 @@ public class BSPIDVMotor : BSVMotor
|
|||
}
|
||||
}
|
||||
|
||||
// Ignore Current and Target Values and just advance the PID computation on this error.
|
||||
public override Vector3 Step(float timeStep, Vector3 error)
|
||||
// Advance the PID computation on this error.
|
||||
public override Vector3 StepError(float timeStep, Vector3 error)
|
||||
{
|
||||
if (!Enabled) return Vector3.Zero;
|
||||
|
||||
|
|
|
@ -45,6 +45,9 @@ public static class BSParam
|
|||
|
||||
public static float MinimumObjectMass { get; private set; }
|
||||
public static float MaximumObjectMass { get; private set; }
|
||||
public static float MaxLinearVelocity { get; private set; }
|
||||
public static float MaxAngularVelocity { get; private set; }
|
||||
public static float MaxAddForceMagnitude { get; private set; }
|
||||
|
||||
public static float LinearDamping { get; private set; }
|
||||
public static float AngularDamping { get; private set; }
|
||||
|
@ -79,6 +82,8 @@ public static class BSParam
|
|||
public static float AvatarStepApproachFactor { get; private set; }
|
||||
public static float AvatarStepForceFactor { get; private set; }
|
||||
|
||||
public static float VehicleMaxLinearVelocity { get; private set; }
|
||||
public static float VehicleMaxAngularVelocity { get; private set; }
|
||||
public static float VehicleAngularDamping { get; private set; }
|
||||
public static float VehicleDebuggingEnabled { get; private set; }
|
||||
|
||||
|
@ -103,7 +108,6 @@ public static class BSParam
|
|||
public const float MaxDensity = 22587f;
|
||||
public const float MinRestitution = 0f;
|
||||
public const float MaxRestitution = 1f;
|
||||
public const float MaxAddForceMagnitude = 20f;
|
||||
|
||||
// ===========================================================================
|
||||
public delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
|
||||
|
@ -232,11 +236,7 @@ public static class BSParam
|
|||
(s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); },
|
||||
(s) => { return (float)s.m_maxUpdatesPerFrame; },
|
||||
(s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
|
||||
new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step",
|
||||
500f,
|
||||
(s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); },
|
||||
(s) => { return (float)s.m_taintsToProcessPerStep; },
|
||||
(s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ),
|
||||
|
||||
new ParameterDefn("MinObjectMass", "Minimum object mass (0.0001)",
|
||||
0.0001f,
|
||||
(s,cf,p,v) => { MinimumObjectMass = cf.GetFloat(p, v); },
|
||||
|
@ -247,6 +247,22 @@ public static class BSParam
|
|||
(s,cf,p,v) => { MaximumObjectMass = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)MaximumObjectMass; },
|
||||
(s,p,l,v) => { MaximumObjectMass = v; } ),
|
||||
new ParameterDefn("MaxLinearVelocity", "Maximum velocity magnitude that can be assigned to an object",
|
||||
1000.0f,
|
||||
(s,cf,p,v) => { MaxLinearVelocity = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)MaxLinearVelocity; },
|
||||
(s,p,l,v) => { MaxLinearVelocity = v; } ),
|
||||
new ParameterDefn("MaxAngularVelocity", "Maximum rotational velocity magnitude that can be assigned to an object",
|
||||
1000.0f,
|
||||
(s,cf,p,v) => { MaxAngularVelocity = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)MaxAngularVelocity; },
|
||||
(s,p,l,v) => { MaxAngularVelocity = v; } ),
|
||||
// LL documentation says thie number should be 20f for llApplyImpulse and 200f for llRezObject
|
||||
new ParameterDefn("MaxAddForceMagnitude", "Maximum force that can be applied by llApplyImpulse (SL says 20f)",
|
||||
20000.0f,
|
||||
(s,cf,p,v) => { MaxAddForceMagnitude = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)MaxAddForceMagnitude; },
|
||||
(s,p,l,v) => { MaxAddForceMagnitude = v; } ),
|
||||
|
||||
new ParameterDefn("PID_D", "Derivitive factor for motion smoothing",
|
||||
2200f,
|
||||
|
@ -423,8 +439,18 @@ public static class BSParam
|
|||
(s) => { return AvatarStepForceFactor; },
|
||||
(s,p,l,v) => { AvatarStepForceFactor = v; } ),
|
||||
|
||||
new ParameterDefn("VehicleMaxLinearVelocity", "Maximum velocity magnitude that can be assigned to a vehicle",
|
||||
1000.0f,
|
||||
(s,cf,p,v) => { VehicleMaxLinearVelocity = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)VehicleMaxLinearVelocity; },
|
||||
(s,p,l,v) => { VehicleMaxLinearVelocity = v; } ),
|
||||
new ParameterDefn("VehicleMaxAngularVelocity", "Maximum rotational velocity magnitude that can be assigned to a vehicle",
|
||||
12.0f,
|
||||
(s,cf,p,v) => { VehicleMaxAngularVelocity = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)VehicleMaxAngularVelocity; },
|
||||
(s,p,l,v) => { VehicleMaxAngularVelocity = v; } ),
|
||||
new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)",
|
||||
0.95f,
|
||||
0.0f,
|
||||
(s,cf,p,v) => { VehicleAngularDamping = cf.GetFloat(p, v); },
|
||||
(s) => { return VehicleAngularDamping; },
|
||||
(s,p,l,v) => { VehicleAngularDamping = v; } ),
|
||||
|
@ -619,11 +645,8 @@ public static class BSParam
|
|||
entries.Add(new PhysParameterEntry(pd.name, pd.desc));
|
||||
}
|
||||
|
||||
// make the list in alphabetical order for estetic reasons
|
||||
entries.Sort(delegate(PhysParameterEntry ppe1, PhysParameterEntry ppe2)
|
||||
{
|
||||
return ppe1.name.CompareTo(ppe2.name);
|
||||
});
|
||||
// make the list alphabetical for estetic reasons
|
||||
entries.Sort((ppe1, ppe2) => { return ppe1.name.CompareTo(ppe2.name); });
|
||||
|
||||
SettableParameters = entries.ToArray();
|
||||
}
|
||||
|
|
|
@ -55,6 +55,16 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
* BS.ApplyCentralForce BS.ApplyTorque
|
||||
*/
|
||||
|
||||
// Flags used to denote which properties updates when making UpdateProperties calls to linksets, etc.
|
||||
public enum UpdatedProperties : uint
|
||||
{
|
||||
Position = 1 << 0,
|
||||
Orientation = 1 << 1,
|
||||
Velocity = 1 << 2,
|
||||
Acceleration = 1 << 3,
|
||||
RotationalVelocity = 1 << 4,
|
||||
EntPropUpdates = Position | Orientation | Velocity | Acceleration | RotationalVelocity,
|
||||
}
|
||||
public abstract class BSPhysObject : PhysicsActor
|
||||
{
|
||||
protected BSPhysObject()
|
||||
|
@ -139,6 +149,11 @@ public abstract class BSPhysObject : PhysicsActor
|
|||
public abstract bool IsStatic { get; }
|
||||
public abstract bool IsSelected { get; }
|
||||
|
||||
// It can be confusing for an actor to know if it should move or update an object
|
||||
// depeneding on the setting of 'selected', 'physical, ...
|
||||
// This flag is the true test -- if true, the object is being acted on in the physical world
|
||||
public abstract bool IsPhysicallyActive { get; }
|
||||
|
||||
// Materialness
|
||||
public MaterialAttributes.Material Material { get; private set; }
|
||||
public override void SetMaterial(int material)
|
||||
|
@ -302,8 +317,9 @@ public abstract class BSPhysObject : PhysicsActor
|
|||
public virtual bool SendCollisions()
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
// If the 'no collision' call, force it to happen right now so quick collision_end
|
||||
bool force = (CollisionCollection.Count == 0);
|
||||
bool force = (CollisionCollection.Count == 0 && CollisionsLastTick.Count != 0);
|
||||
|
||||
// throttle the collisions to the number of milliseconds specified in the subscription
|
||||
if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime))
|
||||
|
@ -318,7 +334,7 @@ public abstract class BSPhysObject : PhysicsActor
|
|||
ret = false;
|
||||
}
|
||||
|
||||
// DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
|
||||
DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
|
||||
base.SendCollisionUpdate(CollisionCollection);
|
||||
|
||||
// Remember the collisions from this tick for some collision specific processing.
|
||||
|
|
|
@ -132,8 +132,8 @@ public sealed class BSPrim : BSPhysObject
|
|||
base.Destroy();
|
||||
|
||||
// Undo any links between me and any other object
|
||||
BSPhysObject parentBefore = Linkset.LinksetRoot;
|
||||
int childrenBefore = Linkset.NumberOfChildren;
|
||||
BSPhysObject parentBefore = Linkset.LinksetRoot; // DEBUG DEBUG
|
||||
int childrenBefore = Linkset.NumberOfChildren; // DEBUG DEBUG
|
||||
|
||||
Linkset = Linkset.RemoveMeFromLinkset(this);
|
||||
|
||||
|
@ -311,16 +311,18 @@ public sealed class BSPrim : BSPhysObject
|
|||
_position = value;
|
||||
PositionSanityCheck(false);
|
||||
|
||||
// A linkset might need to know if a component information changed.
|
||||
Linkset.UpdateProperties(this, false);
|
||||
|
||||
PhysicsScene.TaintedObject("BSPrim.setPosition", delegate()
|
||||
{
|
||||
DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
|
||||
ForcePosition = _position;
|
||||
|
||||
// A linkset might need to know if a component information changed.
|
||||
Linkset.UpdateProperties(UpdatedProperties.Position, this);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override OMV.Vector3 ForcePosition {
|
||||
get {
|
||||
_position = PhysicsScene.PE.GetPosition(PhysBody) - PositionDisplacement;
|
||||
|
@ -335,25 +337,6 @@ public sealed class BSPrim : BSPhysObject
|
|||
}
|
||||
}
|
||||
}
|
||||
// Override to have position displacement immediately update the physical position.
|
||||
// A feeble attempt to keep the sim and physical positions in sync
|
||||
// Must be called at taint time.
|
||||
public override OMV.Vector3 PositionDisplacement
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.PositionDisplacement;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.PositionDisplacement = value;
|
||||
PhysicsScene.TaintedObject(PhysicsScene.InTaintTime, "BSPrim.setPosition", delegate()
|
||||
{
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
PhysicsScene.PE.SetTranslation(PhysBody, _position + base.PositionDisplacement, _orientation);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the current position is sane and, if not, modify the position to make it so.
|
||||
// Check for being below terrain and being out of bounds.
|
||||
|
@ -370,11 +353,11 @@ public sealed class BSPrim : BSPhysObject
|
|||
return ret;
|
||||
}
|
||||
|
||||
float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
|
||||
float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition);
|
||||
OMV.Vector3 upForce = OMV.Vector3.Zero;
|
||||
if (RawPosition.Z < terrainHeight)
|
||||
{
|
||||
DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
|
||||
DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, RawPosition, terrainHeight);
|
||||
float targetHeight = terrainHeight + (Size.Z / 2f);
|
||||
// If the object is below ground it just has to be moved up because pushing will
|
||||
// not get it through the terrain
|
||||
|
@ -502,6 +485,12 @@ public sealed class BSPrim : BSPhysObject
|
|||
RegisterPreStepAction("BSPrim.setForce", LocalID,
|
||||
delegate(float timeStep)
|
||||
{
|
||||
if (!IsPhysicallyActive)
|
||||
{
|
||||
UnRegisterPreStepAction("BSPrim.setForce", LocalID);
|
||||
return;
|
||||
}
|
||||
|
||||
DetailLog("{0},BSPrim.setForce,preStep,force={1}", LocalID, _force);
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
{
|
||||
|
@ -627,6 +616,12 @@ public sealed class BSPrim : BSPhysObject
|
|||
RegisterPreStepAction("BSPrim.setTorque", LocalID,
|
||||
delegate(float timeStep)
|
||||
{
|
||||
if (!IsPhysicallyActive)
|
||||
{
|
||||
UnRegisterPreStepAction("BSPrim.setTorque", LocalID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
AddAngularForce(_torque, false, true);
|
||||
}
|
||||
|
@ -670,12 +665,13 @@ public sealed class BSPrim : BSPhysObject
|
|||
return;
|
||||
_orientation = value;
|
||||
|
||||
// A linkset might need to know if a component information changed.
|
||||
Linkset.UpdateProperties(this, false);
|
||||
|
||||
PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate()
|
||||
{
|
||||
ForceOrientation = _orientation;
|
||||
|
||||
// A linkset might need to know if a component information changed.
|
||||
Linkset.UpdateProperties(UpdatedProperties.Orientation, this);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -727,6 +723,12 @@ public sealed class BSPrim : BSPhysObject
|
|||
get { return !IsPhantom && !_isVolumeDetect; }
|
||||
}
|
||||
|
||||
// The object is moving and is actively being dynamic in the physical world
|
||||
public override bool IsPhysicallyActive
|
||||
{
|
||||
get { return !_isSelected && IsPhysical; }
|
||||
}
|
||||
|
||||
// Make gravity work if the object is physical and not selected
|
||||
// Called at taint-time!!
|
||||
private void SetObjectDynamic(bool forceRebuild)
|
||||
|
@ -971,10 +973,10 @@ public sealed class BSPrim : BSPhysObject
|
|||
}
|
||||
set {
|
||||
_rotationalVelocity = value;
|
||||
Util.ClampV(_rotationalVelocity, BSParam.MaxAngularVelocity);
|
||||
// m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity);
|
||||
PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
|
||||
{
|
||||
DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
|
||||
ForceRotationalVelocity = _rotationalVelocity;
|
||||
});
|
||||
}
|
||||
|
@ -987,7 +989,9 @@ public sealed class BSPrim : BSPhysObject
|
|||
_rotationalVelocity = value;
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
{
|
||||
DetailLog("{0},BSPrim.ForceRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
|
||||
PhysicsScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity);
|
||||
// PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity);
|
||||
ActivateIfPhysical(false);
|
||||
}
|
||||
}
|
||||
|
@ -1055,10 +1059,16 @@ public sealed class BSPrim : BSPhysObject
|
|||
|
||||
RegisterPreStepAction("BSPrim.PIDTarget", LocalID, delegate(float timeStep)
|
||||
{
|
||||
if (!IsPhysicallyActive)
|
||||
{
|
||||
UnRegisterPreStepAction("BSPrim.PIDTarget", LocalID);
|
||||
return;
|
||||
}
|
||||
|
||||
OMV.Vector3 origPosition = RawPosition; // DEBUG DEBUG (for printout below)
|
||||
|
||||
// 'movePosition' is where we'd like the prim to be at this moment.
|
||||
OMV.Vector3 movePosition = _targetMotor.Step(timeStep);
|
||||
OMV.Vector3 movePosition = RawPosition + _targetMotor.Step(timeStep);
|
||||
|
||||
// If we are very close to our target, turn off the movement motor.
|
||||
if (_targetMotor.ErrorIsZero())
|
||||
|
@ -1102,6 +1112,9 @@ public sealed class BSPrim : BSPhysObject
|
|||
|
||||
RegisterPreStepAction("BSPrim.Hover", LocalID, delegate(float timeStep)
|
||||
{
|
||||
if (!IsPhysicallyActive)
|
||||
return;
|
||||
|
||||
_hoverMotor.SetCurrent(RawPosition.Z);
|
||||
_hoverMotor.SetTarget(ComputeCurrentPIDHoverHeight());
|
||||
float targetHeight = _hoverMotor.Step(timeStep);
|
||||
|
@ -1166,28 +1179,25 @@ public sealed class BSPrim : BSPhysObject
|
|||
public override float APIDDamping { set { return; } }
|
||||
|
||||
public override void AddForce(OMV.Vector3 force, bool pushforce) {
|
||||
// Per documentation, max force is limited.
|
||||
OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude);
|
||||
|
||||
// Since this force is being applied in only one step, make this a force per second.
|
||||
OMV.Vector3 addForce = force / PhysicsScene.LastTimeStep;
|
||||
AddForce(addForce, pushforce, false);
|
||||
addForce /= PhysicsScene.LastTimeStep;
|
||||
AddForce(addForce, pushforce, false /* inTaintTime */);
|
||||
}
|
||||
|
||||
// Applying a force just adds this to the total force on the object.
|
||||
// This added force will only last the next simulation tick.
|
||||
public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
|
||||
// for an object, doesn't matter if force is a pushforce or not
|
||||
if (!IsStatic)
|
||||
if (IsPhysicallyActive)
|
||||
{
|
||||
if (force.IsFinite())
|
||||
{
|
||||
float magnitude = force.Length();
|
||||
if (magnitude > BSParam.MaxAddForceMagnitude)
|
||||
{
|
||||
// Force has a limit
|
||||
force = force / magnitude * BSParam.MaxAddForceMagnitude;
|
||||
}
|
||||
|
||||
OMV.Vector3 addForce = force;
|
||||
// DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce);
|
||||
|
||||
OMV.Vector3 addForce = force;
|
||||
PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate()
|
||||
{
|
||||
// Bullet adds this central force to the total force for this tick
|
||||
|
@ -1209,19 +1219,13 @@ public sealed class BSPrim : BSPhysObject
|
|||
|
||||
public void AddForceImpulse(OMV.Vector3 impulse, bool pushforce, bool inTaintTime) {
|
||||
// for an object, doesn't matter if force is a pushforce or not
|
||||
if (!IsStatic)
|
||||
if (!IsPhysicallyActive)
|
||||
{
|
||||
if (impulse.IsFinite())
|
||||
{
|
||||
float magnitude = impulse.Length();
|
||||
if (magnitude > BSParam.MaxAddForceMagnitude)
|
||||
{
|
||||
// Force has a limit
|
||||
impulse = impulse / magnitude * BSParam.MaxAddForceMagnitude;
|
||||
}
|
||||
|
||||
OMV.Vector3 addImpulse = Util.ClampV(impulse, BSParam.MaxAddForceMagnitude);
|
||||
// DetailLog("{0},BSPrim.addForceImpulse,call,impulse={1}", LocalID, impulse);
|
||||
OMV.Vector3 addImpulse = impulse;
|
||||
|
||||
PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddImpulse", delegate()
|
||||
{
|
||||
// Bullet adds this impulse immediately to the velocity
|
||||
|
@ -1584,11 +1588,6 @@ public sealed class BSPrim : BSPhysObject
|
|||
// Called at taint-time!!!
|
||||
public void CreateGeomAndObject(bool forceRebuild)
|
||||
{
|
||||
// If this prim is part of a linkset, we must remove and restore the physical
|
||||
// links if the body is rebuilt.
|
||||
bool needToRestoreLinkset = false;
|
||||
bool needToRestoreVehicle = false;
|
||||
|
||||
// Create the correct physical representation for this type of object.
|
||||
// Updates PhysBody and PhysShape with the new information.
|
||||
// Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary.
|
||||
|
@ -1597,21 +1596,10 @@ public sealed class BSPrim : BSPhysObject
|
|||
// Called if the current prim body is about to be destroyed.
|
||||
// Remove all the physical dependencies on the old body.
|
||||
// (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...)
|
||||
needToRestoreLinkset = Linkset.RemoveBodyDependencies(this);
|
||||
needToRestoreVehicle = _vehicle.RemoveBodyDependencies(this);
|
||||
Linkset.RemoveBodyDependencies(this);
|
||||
_vehicle.RemoveBodyDependencies(this);
|
||||
});
|
||||
|
||||
if (needToRestoreLinkset)
|
||||
{
|
||||
// If physical body dependencies were removed, restore them
|
||||
Linkset.RestoreBodyDependencies(this);
|
||||
}
|
||||
if (needToRestoreVehicle)
|
||||
{
|
||||
// If physical body dependencies were removed, restore them
|
||||
_vehicle.RestoreBodyDependencies(this);
|
||||
}
|
||||
|
||||
// Make sure the properties are set on the new object
|
||||
UpdatePhysicalParameters();
|
||||
return;
|
||||
|
@ -1628,17 +1616,28 @@ public sealed class BSPrim : BSPhysObject
|
|||
// TODO: handle physics introduced by Bullet with computed vehicle physics.
|
||||
if (_vehicle.IsActive)
|
||||
{
|
||||
entprop.RotationalVelocity = OMV.Vector3.Zero;
|
||||
// entprop.RotationalVelocity = OMV.Vector3.Zero;
|
||||
}
|
||||
|
||||
// DetailLog("{0},BSPrim.UpdateProperties,entry,entprop={1}", LocalID, entprop); // DEBUG DEBUG
|
||||
|
||||
// Undo any center-of-mass displacement that might have been done.
|
||||
if (PositionDisplacement != OMV.Vector3.Zero)
|
||||
{
|
||||
// Correct for any rotation around the center-of-mass
|
||||
// TODO!!!
|
||||
entprop.Position -= PositionDisplacement;
|
||||
}
|
||||
|
||||
// Assign directly to the local variables so the normal set actions do not happen
|
||||
entprop.Position -= PositionDisplacement;
|
||||
_position = entprop.Position;
|
||||
_orientation = entprop.Rotation;
|
||||
_velocity = entprop.Velocity;
|
||||
_acceleration = entprop.Acceleration;
|
||||
_rotationalVelocity = entprop.RotationalVelocity;
|
||||
|
||||
// DetailLog("{0},BSPrim.UpdateProperties,afterAssign,entprop={1}", LocalID, entprop); // DEBUG DEBUG
|
||||
|
||||
// The sanity check can change the velocity and/or position.
|
||||
if (IsPhysical && PositionSanityCheck(true))
|
||||
{
|
||||
|
@ -1647,8 +1646,7 @@ public sealed class BSPrim : BSPhysObject
|
|||
}
|
||||
|
||||
OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; // DEBUG DEBUG DEBUG
|
||||
DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}",
|
||||
LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity);
|
||||
DetailLog("{0},BSPrim.UpdateProperties,call,entProp={1},dir={2}", LocalID, entprop, direction);
|
||||
|
||||
// remember the current and last set values
|
||||
LastEntityProperties = CurrentEntityProperties;
|
||||
|
@ -1667,7 +1665,7 @@ public sealed class BSPrim : BSPhysObject
|
|||
*/
|
||||
|
||||
// The linkset implimentation might want to know about this.
|
||||
Linkset.UpdateProperties(this, true);
|
||||
Linkset.UpdateProperties(UpdatedProperties.EntPropUpdates, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
|
|||
internal long m_simulationStep = 0;
|
||||
internal float NominalFrameRate { get; set; }
|
||||
public long SimulationStep { get { return m_simulationStep; } }
|
||||
internal int m_taintsToProcessPerStep;
|
||||
internal float LastTimeStep { get; private set; }
|
||||
|
||||
// Physical objects can register for prestep or poststep events
|
||||
|
@ -709,8 +708,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
|
|||
// TriggerPreStepEvent
|
||||
// DoOneTimeTaints
|
||||
// Step()
|
||||
// ProcessAndForwardCollisions
|
||||
// ProcessAndForwardPropertyUpdates
|
||||
// ProcessAndSendToSimulatorCollisions
|
||||
// ProcessAndSendToSimulatorPropertyUpdates
|
||||
// TriggerPostStepEvent
|
||||
|
||||
// Calls to the PhysicsActors can't directly call into the physics engine
|
||||
|
@ -847,8 +846,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
|
|||
|
||||
#endregion // Taints
|
||||
|
||||
#region INI and command line parameter processing
|
||||
|
||||
#region IPhysicsParameters
|
||||
// Get the list of parameters this physics engine supports
|
||||
public PhysParameterEntry[] GetParameterList()
|
||||
|
@ -945,8 +942,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
|
|||
|
||||
#endregion IPhysicsParameters
|
||||
|
||||
#endregion Runtime settable parameters
|
||||
|
||||
// Invoke the detailed logger and output something if it's enabled.
|
||||
public void DetailLog(string msg, params Object[] args)
|
||||
{
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
CURRENT PRIORITIES
|
||||
=================================================
|
||||
Mantis 6040 script http://opensimulator.org/mantis/view.php?id=6040
|
||||
Msg Kayaker on OSGrid when working
|
||||
Deleting a linkset while standing on the root will leave the physical shape of the root behind.
|
||||
Not sure if it is because standing on it. Done with large prim linksets.
|
||||
Child movement in linkset (don't rebuild linkset)
|
||||
Vehicle angular vertical attraction
|
||||
vehicle angular banking
|
||||
Center-of-gravity
|
||||
Vehicle angular deflection
|
||||
Preferred orientation angular correction fix
|
||||
when should angular and linear motor targets be zeroed? when selected?
|
||||
Need a vehicle.clear()? Or an 'else' in prestep if not physical.
|
||||
Teravus llMoveToTarget script debug
|
||||
Mixing of hover, buoyancy/gravity, moveToTarget, into one force
|
||||
Boats floating at proper level
|
||||
Nebadon vehicles turning funny in arena
|
||||
limitMotorUp calibration (more down?)
|
||||
llRotLookAt
|
||||
llLookAt
|
||||
Vehicle angular vertical attraction
|
||||
Vehicle angular deflection
|
||||
Preferred orientation angular correction fix
|
||||
vehicle angular banking
|
||||
Avatars walking up stairs (HALF DONE)
|
||||
Radius of the capsule affects ability to climb edges.
|
||||
Vehicle movement on terrain smoothness
|
||||
Boats float low in the water (DONE)
|
||||
Avatar movement
|
||||
flying into a wall doesn't stop avatar who keeps appearing to move through the obstacle (DONE)
|
||||
walking up stairs is not calibrated correctly (stairs out of Kepler cabin)
|
||||
|
@ -72,8 +72,15 @@ Incorporate inter-relationship of angular corrections. For instance, angularDefl
|
|||
|
||||
GENERAL TODO LIST:
|
||||
=================================================
|
||||
llMoveToTarget objects are not effected by gravity until target is removed.
|
||||
Implement llSetPhysicalMaterial.
|
||||
extend it with Center-of-mass, rolling friction, density
|
||||
Implement llSetForceAndTorque.
|
||||
Change BSPrim.moveToTarget to used forces rather than changing position
|
||||
Changing position allows one to move through walls
|
||||
Implement an avatar mesh shape. The Bullet capsule is way too limited.
|
||||
Consider just hand creating a vertex/index array in a new BSShapeAvatar.
|
||||
Verify/fix phantom, volume-detect objects do not fall to infinity. Should stop at terrain.
|
||||
Revisit CollisionMargin. Builders notice the 0.04 spacing between prims.
|
||||
Duplicating a physical prim causes old prim to jump away
|
||||
Dup a phys prim and the original become unselected and thus interacts w/ selected prim.
|
||||
|
@ -307,3 +314,11 @@ Remove HeightmapInfo from terrain specification (DONE)
|
|||
Surfboard go wonky when turning (DONE)
|
||||
Angular motor direction is global coordinates rather than local coordinates?
|
||||
(Resolution: made angular motor direction correct coordinate system)
|
||||
Mantis 6040 script http://opensimulator.org/mantis/view.php?id=6040 (DONE)
|
||||
Msg Kayaker on OSGrid when working
|
||||
(Resolution: LINEAR_DIRECTION is in vehicle coords. Test script does the
|
||||
same in SL as in OS/BulletSim)
|
||||
Boats float low in the water (DONE)
|
||||
Boats floating at proper level (DONE)
|
||||
When is force introduced by SetForce removed? The prestep action could go forever. (DONE)
|
||||
(Resolution: setForce registers a prestep action which keeps applying the force)
|
|
@ -43,6 +43,35 @@ namespace OpenSim.Region.Physics.Manager
|
|||
public delegate void JointDeactivated(PhysicsJoint joint);
|
||||
public delegate void JointErrorMessage(PhysicsJoint joint, string message); // this refers to an "error message due to a problem", not "amount of joint constraint violation"
|
||||
|
||||
public enum RayFilterFlags:ushort
|
||||
{
|
||||
// the flags
|
||||
water = 0x01,
|
||||
land = 0x02,
|
||||
agent = 0x04,
|
||||
nonphysical = 0x08,
|
||||
physical = 0x10,
|
||||
phantom = 0x20,
|
||||
volumedtc = 0x40,
|
||||
|
||||
// ray cast colision control (may only work for meshs)
|
||||
ContactsUnImportant = 0x2000,
|
||||
BackFaceCull = 0x4000,
|
||||
ClosestHit = 0x8000,
|
||||
|
||||
// some combinations
|
||||
LSLPhanton = phantom | volumedtc,
|
||||
PrimsNonPhantom = nonphysical | physical,
|
||||
PrimsNonPhantomAgents = nonphysical | physical | agent,
|
||||
|
||||
AllPrims = nonphysical | phantom | volumedtc | physical,
|
||||
AllButLand = agent | nonphysical | physical | phantom | volumedtc,
|
||||
|
||||
ClosestAndBackCull = ClosestHit | BackFaceCull,
|
||||
|
||||
All = 0x3f
|
||||
}
|
||||
|
||||
public delegate void RequestAssetDelegate(UUID assetID, AssetReceivedDelegate callback);
|
||||
public delegate void AssetReceivedDelegate(AssetBase asset);
|
||||
|
||||
|
@ -286,5 +315,15 @@ namespace OpenSim.Region.Physics.Manager
|
|||
{
|
||||
return new List<ContactResult>();
|
||||
}
|
||||
|
||||
public virtual object RaycastWorld(Vector3 position, Vector3 direction, float length, int Count, RayFilterFlags filter)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual bool SupportsRaycastWorldFiltered()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2748,17 +2748,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
return src.ToLower();
|
||||
}
|
||||
|
||||
public LSL_Integer llGiveMoney(string destination, int amount)
|
||||
public void llGiveMoney(string destination, int amount)
|
||||
{
|
||||
Util.FireAndForget(x =>
|
||||
{
|
||||
m_host.AddScriptLPS(1);
|
||||
|
||||
if (m_item.PermsGranter == UUID.Zero)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_DEBIT) == 0)
|
||||
{
|
||||
LSLError("No permissions to give money");
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
UUID toID = new UUID();
|
||||
|
@ -2766,7 +2768,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
if (!UUID.TryParse(destination, out toID))
|
||||
{
|
||||
LSLError("Bad key in llGiveMoney");
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
IMoneyModule money = World.RequestModuleInterface<IMoneyModule>();
|
||||
|
@ -2774,16 +2776,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
if (money == null)
|
||||
{
|
||||
NotImplemented("llGiveMoney");
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
bool result = money.ObjectGiveMoney(
|
||||
money.ObjectGiveMoney(
|
||||
m_host.ParentGroup.RootPart.UUID, m_host.ParentGroup.RootPart.OwnerID, toID, amount);
|
||||
|
||||
if (result)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
public void llMakeExplosion(int particles, double scale, double vel, double lifetime, double arc, string texture, LSL_Vector offset)
|
||||
|
@ -3374,7 +3372,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
if (animID == UUID.Zero)
|
||||
presence.Animator.RemoveAnimation(anim);
|
||||
else
|
||||
presence.Animator.RemoveAnimation(animID);
|
||||
presence.Animator.RemoveAnimation(animID, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4933,7 +4931,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
public LSL_Vector llGetCenterOfMass()
|
||||
{
|
||||
m_host.AddScriptLPS(1);
|
||||
Vector3 center = m_host.GetGeometricCenter();
|
||||
Vector3 center = m_host.GetCenterOfMass();
|
||||
return new LSL_Vector(center.X,center.Y,center.Z);
|
||||
}
|
||||
|
||||
|
@ -5803,13 +5801,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
if (parcelOwned && land.LandData.OwnerID == id ||
|
||||
parcel && land.LandData.GlobalID == id)
|
||||
{
|
||||
result.Add(ssp.UUID.ToString());
|
||||
result.Add(new LSL_Key(ssp.UUID.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(ssp.UUID.ToString());
|
||||
result.Add(new LSL_Key(ssp.UUID.ToString()));
|
||||
}
|
||||
}
|
||||
// Maximum of 100 results
|
||||
|
@ -6849,7 +6847,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
{
|
||||
m_host.AddScriptLPS(1);
|
||||
IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface<IXMLRPC>();
|
||||
if (xmlrpcMod.IsEnabled())
|
||||
if (xmlrpcMod != null && xmlrpcMod.IsEnabled())
|
||||
{
|
||||
UUID channelID = xmlrpcMod.OpenXMLRPCChannel(m_host.LocalId, m_item.ItemID, UUID.Zero);
|
||||
IXmlRpcRouter xmlRpcRouter = m_ScriptEngine.World.RequestModuleInterface<IXmlRpcRouter>();
|
||||
|
@ -6881,6 +6879,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
m_host.AddScriptLPS(1);
|
||||
IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface<IXMLRPC>();
|
||||
ScriptSleep(3000);
|
||||
if (xmlrpcMod == null)
|
||||
return "";
|
||||
return (xmlrpcMod.SendRemoteData(m_host.LocalId, m_item.ItemID, channel, dest, idata, sdata)).ToString();
|
||||
}
|
||||
|
||||
|
@ -6888,6 +6888,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
{
|
||||
m_host.AddScriptLPS(1);
|
||||
IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface<IXMLRPC>();
|
||||
if (xmlrpcMod != null)
|
||||
xmlrpcMod.RemoteDataReply(channel, message_id, sdata, idata);
|
||||
ScriptSleep(3000);
|
||||
}
|
||||
|
@ -6903,6 +6904,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
}
|
||||
|
||||
IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface<IXMLRPC>();
|
||||
if (xmlrpcMod != null)
|
||||
xmlrpcMod.CloseXMLRPCChannel((UUID)channel);
|
||||
ScriptSleep(1000);
|
||||
}
|
||||
|
@ -10600,6 +10602,35 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
case ScriptBaseClass.OBJECT_PHYSICS_COST:
|
||||
ret.Add(new LSL_Float(0));
|
||||
break;
|
||||
case ScriptBaseClass.OBJECT_CHARACTER_TIME: // Pathfinding
|
||||
ret.Add(new LSL_Float(0));
|
||||
break;
|
||||
case ScriptBaseClass.OBJECT_ROOT:
|
||||
SceneObjectPart p = av.ParentPart;
|
||||
if (p != null)
|
||||
{
|
||||
ret.Add(new LSL_String(p.ParentGroup.RootPart.UUID.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.Add(new LSL_String(id));
|
||||
}
|
||||
break;
|
||||
case ScriptBaseClass.OBJECT_ATTACHED_POINT:
|
||||
ret.Add(new LSL_Integer(0));
|
||||
break;
|
||||
case ScriptBaseClass.OBJECT_PATHFINDING_TYPE: // Pathfinding
|
||||
ret.Add(new LSL_Integer(ScriptBaseClass.OPT_AVATAR));
|
||||
break;
|
||||
case ScriptBaseClass.OBJECT_PHYSICS:
|
||||
ret.Add(new LSL_Integer(0));
|
||||
break;
|
||||
case ScriptBaseClass.OBJECT_PHANTOM:
|
||||
ret.Add(new LSL_Integer(0));
|
||||
break;
|
||||
case ScriptBaseClass.OBJECT_TEMP_ON_REZ:
|
||||
ret.Add(new LSL_Integer(0));
|
||||
break;
|
||||
default:
|
||||
// Invalid or unhandled constant.
|
||||
ret.Add(new LSL_Integer(ScriptBaseClass.OBJECT_UNKNOWN_DETAIL));
|
||||
|
@ -10695,6 +10726,52 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
// The value returned in SL for normal prims looks like the prim count
|
||||
ret.Add(new LSL_Float(0));
|
||||
break;
|
||||
case ScriptBaseClass.OBJECT_CHARACTER_TIME: // Pathfinding
|
||||
ret.Add(new LSL_Float(0));
|
||||
break;
|
||||
case ScriptBaseClass.OBJECT_ROOT:
|
||||
ret.Add(new LSL_String(obj.ParentGroup.RootPart.UUID.ToString()));
|
||||
break;
|
||||
case ScriptBaseClass.OBJECT_ATTACHED_POINT:
|
||||
ret.Add(new LSL_Integer(obj.ParentGroup.AttachmentPoint));
|
||||
break;
|
||||
case ScriptBaseClass.OBJECT_PATHFINDING_TYPE:
|
||||
byte pcode = obj.Shape.PCode;
|
||||
if (obj.ParentGroup.AttachmentPoint != 0
|
||||
|| pcode == (byte)PCode.Grass
|
||||
|| pcode == (byte)PCode.Tree
|
||||
|| pcode == (byte)PCode.NewTree)
|
||||
{
|
||||
ret.Add(new LSL_Integer(ScriptBaseClass.OPT_OTHER));
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.Add(new LSL_Integer(ScriptBaseClass.OPT_LEGACY_LINKSET));
|
||||
}
|
||||
break;
|
||||
case ScriptBaseClass.OBJECT_PHYSICS:
|
||||
if (obj.ParentGroup.AttachmentPoint != 0)
|
||||
{
|
||||
ret.Add(new LSL_Integer(0)); // Always false if attached
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.Add(new LSL_Integer(obj.ParentGroup.UsesPhysics ? 1 : 0));
|
||||
}
|
||||
break;
|
||||
case ScriptBaseClass.OBJECT_PHANTOM:
|
||||
if (obj.ParentGroup.AttachmentPoint != 0)
|
||||
{
|
||||
ret.Add(new LSL_Integer(0)); // Always false if attached
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.Add(new LSL_Integer(obj.ParentGroup.IsPhantom ? 1 : 0));
|
||||
}
|
||||
break;
|
||||
case ScriptBaseClass.OBJECT_TEMP_ON_REZ:
|
||||
ret.Add(new LSL_Integer(obj.ParentGroup.IsTemporary ? 1 : 0));
|
||||
break;
|
||||
default:
|
||||
// Invalid or unhandled constant.
|
||||
ret.Add(new LSL_Integer(ScriptBaseClass.OBJECT_UNKNOWN_DETAIL));
|
||||
|
@ -11273,13 +11350,69 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
bool checkPhysical = !((rejectTypes & ScriptBaseClass.RC_REJECT_PHYSICAL) == ScriptBaseClass.RC_REJECT_PHYSICAL);
|
||||
|
||||
|
||||
if (checkTerrain)
|
||||
if (World.SupportsRayCastFiltered())
|
||||
{
|
||||
ContactResult? groundContact = GroundIntersection(rayStart, rayEnd);
|
||||
if (groundContact != null)
|
||||
results.Add((ContactResult)groundContact);
|
||||
if (dist == 0)
|
||||
return list;
|
||||
|
||||
RayFilterFlags rayfilter = RayFilterFlags.ClosestAndBackCull;
|
||||
if (checkTerrain)
|
||||
rayfilter |= RayFilterFlags.land;
|
||||
// if (checkAgents)
|
||||
// rayfilter |= RayFilterFlags.agent;
|
||||
if (checkPhysical)
|
||||
rayfilter |= RayFilterFlags.physical;
|
||||
if (checkNonPhysical)
|
||||
rayfilter |= RayFilterFlags.nonphysical;
|
||||
if (detectPhantom)
|
||||
rayfilter |= RayFilterFlags.LSLPhanton;
|
||||
|
||||
Vector3 direction = dir * ( 1/dist);
|
||||
|
||||
if(rayfilter == 0)
|
||||
{
|
||||
list.Add(new LSL_Integer(0));
|
||||
return list;
|
||||
}
|
||||
|
||||
// get some more contacts to sort ???
|
||||
int physcount = 4 * count;
|
||||
if (physcount > 20)
|
||||
physcount = 20;
|
||||
|
||||
object physresults;
|
||||
physresults = World.RayCastFiltered(rayStart, direction, dist, physcount, rayfilter);
|
||||
|
||||
if (physresults == null)
|
||||
{
|
||||
list.Add(new LSL_Integer(-3)); // timeout error
|
||||
return list;
|
||||
}
|
||||
|
||||
results = (List<ContactResult>)physresults;
|
||||
|
||||
// for now physics doesn't detect sitted avatars so do it outside physics
|
||||
if (checkAgents)
|
||||
{
|
||||
ContactResult[] agentHits = AvatarIntersection(rayStart, rayEnd);
|
||||
foreach (ContactResult r in agentHits)
|
||||
results.Add(r);
|
||||
}
|
||||
|
||||
// TODO: Replace this with a better solution. ObjectIntersection can only
|
||||
// detect nonphysical phantoms. They are detected by virtue of being
|
||||
// nonphysical (e.g. no PhysActor) so will not conflict with detecting
|
||||
// physicsl phantoms as done by the physics scene
|
||||
// We don't want anything else but phantoms here.
|
||||
if (detectPhantom)
|
||||
{
|
||||
ContactResult[] objectHits = ObjectIntersection(rayStart, rayEnd, false, false, true);
|
||||
foreach (ContactResult r in objectHits)
|
||||
results.Add(r);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (checkAgents)
|
||||
{
|
||||
ContactResult[] agentHits = AvatarIntersection(rayStart, rayEnd);
|
||||
|
@ -11293,6 +11426,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
foreach (ContactResult r in objectHits)
|
||||
results.Add(r);
|
||||
}
|
||||
}
|
||||
|
||||
if (checkTerrain)
|
||||
{
|
||||
ContactResult? groundContact = GroundIntersection(rayStart, rayEnd);
|
||||
if (groundContact != null)
|
||||
results.Add((ContactResult)groundContact);
|
||||
}
|
||||
|
||||
results.Sort(delegate(ContactResult a, ContactResult b)
|
||||
{
|
||||
|
@ -11489,6 +11630,79 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
NotImplemented("llGodLikeRezObject");
|
||||
}
|
||||
|
||||
public LSL_String llTransferLindenDollars(string destination, int amount)
|
||||
{
|
||||
UUID txn = UUID.Random();
|
||||
|
||||
Util.FireAndForget(delegate(object x)
|
||||
{
|
||||
int replycode = 0;
|
||||
string replydata = destination + "," + amount.ToString();
|
||||
|
||||
try
|
||||
{
|
||||
TaskInventoryItem item = m_item;
|
||||
if (item == null)
|
||||
{
|
||||
replydata = "SERVICE_ERROR";
|
||||
return;
|
||||
}
|
||||
|
||||
m_host.AddScriptLPS(1);
|
||||
|
||||
if (item.PermsGranter == UUID.Zero)
|
||||
{
|
||||
replydata = "MISSING_PERMISSION_DEBIT";
|
||||
return;
|
||||
}
|
||||
|
||||
if ((item.PermsMask & ScriptBaseClass.PERMISSION_DEBIT) == 0)
|
||||
{
|
||||
replydata = "MISSING_PERMISSION_DEBIT";
|
||||
return;
|
||||
}
|
||||
|
||||
UUID toID = new UUID();
|
||||
|
||||
if (!UUID.TryParse(destination, out toID))
|
||||
{
|
||||
replydata = "INVALID_AGENT";
|
||||
return;
|
||||
}
|
||||
|
||||
IMoneyModule money = World.RequestModuleInterface<IMoneyModule>();
|
||||
|
||||
if (money == null)
|
||||
{
|
||||
replydata = "TRANSFERS_DISABLED";
|
||||
return;
|
||||
}
|
||||
|
||||
bool result = money.ObjectGiveMoney(
|
||||
m_host.ParentGroup.RootPart.UUID, m_host.ParentGroup.RootPart.OwnerID, toID, amount);
|
||||
|
||||
if (result)
|
||||
{
|
||||
replycode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
replydata = "LINDENDOLLAR_INSUFFICIENTFUNDS";
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_ScriptEngine.PostScriptEvent(m_item.ItemID, new EventParams(
|
||||
"transaction_result", new Object[] {
|
||||
new LSL_String(txn.ToString()),
|
||||
new LSL_Integer(replycode),
|
||||
new LSL_String(replydata) },
|
||||
new DetectParams[0]));
|
||||
}
|
||||
});
|
||||
|
||||
return txn.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
|
|
@ -986,7 +986,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
if (animID == UUID.Zero)
|
||||
target.Animator.RemoveAnimation(animation);
|
||||
else
|
||||
target.Animator.RemoveAnimation(animID);
|
||||
target.Animator.RemoveAnimation(animID, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -207,7 +207,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces
|
|||
LSL_Float llGetWallclock();
|
||||
void llGiveInventory(string destination, string inventory);
|
||||
void llGiveInventoryList(string destination, string category, LSL_List inventory);
|
||||
LSL_Integer llGiveMoney(string destination, int amount);
|
||||
void llGiveMoney(string destination, int amount);
|
||||
LSL_String llTransferLindenDollars(string destination, int amount);
|
||||
void llGodLikeRezObject(string inventory, LSL_Vector pos);
|
||||
LSL_Float llGround(LSL_Vector offset);
|
||||
LSL_Vector llGroundContour(LSL_Vector offset);
|
||||
|
|
|
@ -556,6 +556,23 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
|
|||
public const int OBJECT_SERVER_COST = 14;
|
||||
public const int OBJECT_STREAMING_COST = 15;
|
||||
public const int OBJECT_PHYSICS_COST = 16;
|
||||
public const int OBJECT_CHARACTER_TIME = 17;
|
||||
public const int OBJECT_ROOT = 18;
|
||||
public const int OBJECT_ATTACHED_POINT = 19;
|
||||
public const int OBJECT_PATHFINDING_TYPE = 20;
|
||||
public const int OBJECT_PHYSICS = 21;
|
||||
public const int OBJECT_PHANTOM = 22;
|
||||
public const int OBJECT_TEMP_ON_REZ = 23;
|
||||
|
||||
// Pathfinding types
|
||||
public const int OPT_OTHER = -1;
|
||||
public const int OPT_LEGACY_LINKSET = 0;
|
||||
public const int OPT_AVATAR = 1;
|
||||
public const int OPT_CHARACTER = 2;
|
||||
public const int OPT_WALKABLE = 3;
|
||||
public const int OPT_STATIC_OBSTACLE = 4;
|
||||
public const int OPT_MATERIAL_VOLUME = 5;
|
||||
public const int OPT_EXCLUSION_VOLUME = 6;
|
||||
|
||||
// for llGetAgentList
|
||||
public const int AGENT_LIST_PARCEL = 1;
|
||||
|
|
|
@ -869,9 +869,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
|
|||
m_LSL_Functions.llGiveInventoryList(destination, category, inventory);
|
||||
}
|
||||
|
||||
public LSL_Integer llGiveMoney(string destination, int amount)
|
||||
public void llGiveMoney(string destination, int amount)
|
||||
{
|
||||
return m_LSL_Functions.llGiveMoney(destination, amount);
|
||||
m_LSL_Functions.llGiveMoney(destination, amount);
|
||||
}
|
||||
|
||||
public LSL_String llTransferLindenDollars(string destination, int amount)
|
||||
{
|
||||
return m_LSL_Functions.llTransferLindenDollars(destination, amount);
|
||||
}
|
||||
|
||||
public void llGodLikeRezObject(string inventory, LSL_Vector pos)
|
||||
|
|
|
@ -1514,7 +1514,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine
|
|||
m_MaxScriptQueue = maxScriptQueue;
|
||||
|
||||
STPStartInfo startInfo = new STPStartInfo();
|
||||
startInfo.IdleTimeout = idleTimeout*1000; // convert to seconds as stated in .ini
|
||||
startInfo.ThreadPoolName = "XEngine";
|
||||
startInfo.IdleTimeout = idleTimeout * 1000; // convert to seconds as stated in .ini
|
||||
startInfo.MaxWorkerThreads = maxThreads;
|
||||
startInfo.MinWorkerThreads = minThreads;
|
||||
startInfo.ThreadPriority = threadPriority;;
|
||||
|
@ -1522,7 +1523,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
|
|||
startInfo.StartSuspended = true;
|
||||
|
||||
m_ThreadPool = new SmartThreadPool(startInfo);
|
||||
m_ThreadPool.Name = "XEngine";
|
||||
}
|
||||
|
||||
//
|
||||
|
|
10
TESTING.txt
10
TESTING.txt
|
@ -1,4 +1,4 @@
|
|||
===== The Quick Guide to OpenSim Unit Testing ===
|
||||
= The Quick Guide to OpenSim Unit Testing =
|
||||
|
||||
== Running Tests ==
|
||||
|
||||
|
@ -27,7 +27,7 @@ must list it in both ".nant/local.include"
|
|||
for it to be accessible to Linux users and to the continuous
|
||||
integration system.
|
||||
|
||||
==TESTING ON WINDOWS==
|
||||
== TESTING ON WINDOWS ==
|
||||
|
||||
To use nunit testing on opensim code, you have a variety of methods. The
|
||||
easiast methods involve using IDE capabilities to test code. Using
|
||||
|
@ -58,14 +58,14 @@ Nunit console allows you to execute the nunit tests of assemblies via console.
|
|||
Its output will show test failures and successes and a summary of what
|
||||
happened. This is very useful for a quick overview and/or automated testing.
|
||||
|
||||
Windows
|
||||
=== Windows ===
|
||||
Windows version of nunit-console is by default .Net 2.0 if you downloaded the
|
||||
.Net 2.0 version of Nunit. Be sure to setup your PATH environment variable.
|
||||
|
||||
Linux & OSX
|
||||
=== Linux & OSX ===
|
||||
On these operating systems you will have to use the command "nunit-console2"
|
||||
|
||||
Example
|
||||
=== Example ===
|
||||
|
||||
nunit-console2 OpenSim.Framework.Tests.dll (on linux)
|
||||
nunit-console OpenSim.Framework.Tests.dll (on windows)
|
||||
|
|
|
@ -32,6 +32,11 @@ namespace Amib.Threading
|
|||
/// </summary>
|
||||
private ThreadPriority _threadPriority;
|
||||
|
||||
/// <summary>
|
||||
/// The thread pool name. Threads will get names depending on this.
|
||||
/// </summary>
|
||||
private string _threadPoolName;
|
||||
|
||||
/// <summary>
|
||||
/// If this field is not null then the performance counters are enabled
|
||||
/// and use the string as the name of the instance.
|
||||
|
@ -46,6 +51,7 @@ namespace Amib.Threading
|
|||
_minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
|
||||
_maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
|
||||
_threadPriority = SmartThreadPool.DefaultThreadPriority;
|
||||
_threadPoolName = SmartThreadPool.DefaultThreadPoolName;
|
||||
_pcInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
|
||||
_stackSize = SmartThreadPool.DefaultStackSize;
|
||||
}
|
||||
|
@ -56,6 +62,7 @@ namespace Amib.Threading
|
|||
_minWorkerThreads = stpStartInfo._minWorkerThreads;
|
||||
_maxWorkerThreads = stpStartInfo._maxWorkerThreads;
|
||||
_threadPriority = stpStartInfo._threadPriority;
|
||||
_threadPoolName = stpStartInfo._threadPoolName;
|
||||
_pcInstanceName = stpStartInfo._pcInstanceName;
|
||||
_stackSize = stpStartInfo._stackSize;
|
||||
}
|
||||
|
@ -84,6 +91,13 @@ namespace Amib.Threading
|
|||
set { _threadPriority = value; }
|
||||
}
|
||||
|
||||
public virtual string ThreadPoolName
|
||||
{
|
||||
get { return _threadPoolName; }
|
||||
set { _threadPoolName = value; }
|
||||
}
|
||||
|
||||
|
||||
public string PerformanceCounterInstanceName
|
||||
{
|
||||
get { return _pcInstanceName; }
|
||||
|
|
|
@ -135,6 +135,11 @@ namespace Amib.Threading
|
|||
/// </summary>
|
||||
public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal;
|
||||
|
||||
/// <summary>
|
||||
/// The default thread pool name
|
||||
/// </summary>
|
||||
public const string DefaultThreadPoolName = "SmartThreadPool";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Member Variables
|
||||
|
@ -143,7 +148,7 @@ namespace Amib.Threading
|
|||
/// Contains the name of this instance of SmartThreadPool.
|
||||
/// Can be changed by the user.
|
||||
/// </summary>
|
||||
private string _name = "SmartThreadPool";
|
||||
private string _name = DefaultThreadPoolName;
|
||||
|
||||
/// <summary>
|
||||
/// Hashtable of all the threads in the thread pool.
|
||||
|
@ -307,6 +312,7 @@ namespace Amib.Threading
|
|||
|
||||
private void Initialize()
|
||||
{
|
||||
Name = _stpStartInfo.ThreadPoolName;
|
||||
ValidateSTPStartInfo();
|
||||
|
||||
if (null != _stpStartInfo.PerformanceCounterInstanceName)
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -908,6 +908,19 @@
|
|||
|
||||
[BulletSim]
|
||||
; World parameters
|
||||
|
||||
; There are two bullet physics libraries, bulletunmanaged is the default and is a native c++ dll
|
||||
; bulletxna is a managed C# dll. They have comparible functionality.. the c++ is much faster.
|
||||
|
||||
BulletEngine = "bulletunmanaged"
|
||||
; BulletEngine = "bulletxna"
|
||||
|
||||
; Terrain Implementation {1|0} 0 for HeightField, 1 for Mesh terrain. If you're using the bulletxna engine,
|
||||
; you will want to switch to the heightfield option
|
||||
|
||||
TerrainImplementation = 1
|
||||
; TerrainImplementation = 0
|
||||
|
||||
DefaultFriction = 0.20
|
||||
DefaultDensity = 10.000006836
|
||||
DefaultRestitution = 0.0
|
||||
|
@ -960,6 +973,7 @@
|
|||
PhysicsLoggingDir = "."
|
||||
VehicleLoggingEnabled = False
|
||||
|
||||
|
||||
[RemoteAdmin]
|
||||
enabled = false
|
||||
|
||||
|
@ -1109,6 +1123,17 @@
|
|||
|
||||
;exclude_list=User 1,User 2,User 3
|
||||
|
||||
;;Shows modal alertbox for entering agent on IRC enabled regions
|
||||
;;
|
||||
;; Enable Alert, default = false
|
||||
;alert_show = false
|
||||
;;
|
||||
;; Show IRC serverinfo, default = true
|
||||
;alert_show_serverinfo = true
|
||||
;;
|
||||
;alert_msg_pre = "This region is linked to Irc."
|
||||
;alert_msg_post = "Everything you say in public chat can be listened."
|
||||
|
||||
|
||||
; The following settings control the progression of daytime
|
||||
; in the Sim. The defaults are the same as the commented out settings
|
||||
|
|
36
prebuild.xml
36
prebuild.xml
|
@ -1727,45 +1727,13 @@
|
|||
<Configuration name="Debug">
|
||||
<Options>
|
||||
<OutputPath>../../../../bin/Physics/</OutputPath>
|
||||
<AllowUnsafe>true</AllowUnsafe>
|
||||
</Options>
|
||||
</Configuration>
|
||||
<Configuration name="Release">
|
||||
<Options>
|
||||
<OutputPath>../../../../bin/Physics/</OutputPath>
|
||||
</Options>
|
||||
</Configuration>
|
||||
|
||||
<ReferencePath>../../../../bin/</ReferencePath>
|
||||
<Reference name="System"/>
|
||||
<Reference name="System.Core"/>
|
||||
<Reference name="System.Xml"/>
|
||||
<Reference name="OpenMetaverseTypes" path="../../../../bin/"/>
|
||||
<Reference name="Nini.dll" path="../../../../bin/"/>
|
||||
<Reference name="OpenSim.Framework"/>
|
||||
<Reference name="OpenSim.Region.Framework"/>
|
||||
<Reference name="OpenSim.Region.CoreModules"/>
|
||||
<Reference name="OpenSim.Framework.Console"/>
|
||||
<Reference name="OpenSim.Region.Physics.Manager"/>
|
||||
<Reference name="OpenSim.Region.Physics.ConvexDecompositionDotNet"/>
|
||||
<Reference name="BulletXNA.dll" path="../../../../bin/"/>
|
||||
<Reference name="log4net.dll" path="../../../../bin/"/>
|
||||
|
||||
<Files>
|
||||
<Match pattern="*.cs" recurse="true">
|
||||
<Exclude name="Tests" pattern="Tests"/>
|
||||
</Match>
|
||||
</Files>
|
||||
</Project>
|
||||
|
||||
<Project frameworkVersion="v3_5" name="OpenSim.Region.Physics.BulletSNPlugin" path="OpenSim/Region/Physics/BulletSNPlugin" type="Library">
|
||||
<Configuration name="Debug">
|
||||
<Options>
|
||||
<OutputPath>../../../../bin/Physics/</OutputPath>
|
||||
</Options>
|
||||
</Configuration>
|
||||
<Configuration name="Release">
|
||||
<Options>
|
||||
<OutputPath>../../../../bin/Physics/</OutputPath>
|
||||
<AllowUnsafe>true</AllowUnsafe>
|
||||
</Options>
|
||||
</Configuration>
|
||||
|
||||
|
|
Loading…
Reference in New Issue