Merge branch 'master' into careminster
Conflicts: OpenSim/Region/Framework/Scenes/SceneObjectPart.cs OpenSim/Region/Physics/BulletSNPlugin/BSShapeCollection.csavinationmerge
commit
cc4cfd9fa8
|
@ -1670,8 +1670,13 @@ namespace OpenSim.Framework
|
||||||
if (m_ThreadPool != null)
|
if (m_ThreadPool != null)
|
||||||
throw new InvalidOperationException("SmartThreadPool is already initialized");
|
throw new InvalidOperationException("SmartThreadPool is already initialized");
|
||||||
|
|
||||||
m_ThreadPool = new SmartThreadPool(2000, maxThreads, 2);
|
STPStartInfo startInfo = new STPStartInfo();
|
||||||
m_ThreadPool.Name = "Util";
|
startInfo.ThreadPoolName = "Util";
|
||||||
|
startInfo.IdleTimeout = 2000;
|
||||||
|
startInfo.MaxWorkerThreads = maxThreads;
|
||||||
|
startInfo.MinWorkerThreads = 2;
|
||||||
|
|
||||||
|
m_ThreadPool = new SmartThreadPool(startInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int FireAndForgetCount()
|
public static int FireAndForgetCount()
|
||||||
|
|
|
@ -2470,18 +2470,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
return new Vector3(0, 0, 0);
|
return new Vector3(0, 0, 0);
|
||||||
|
|
||||||
return ParentGroup.GetGeometricCenter();
|
return ParentGroup.GetGeometricCenter();
|
||||||
|
|
||||||
/*
|
|
||||||
PhysicsActor pa = PhysActor;
|
|
||||||
|
|
||||||
if (pa != null)
|
|
||||||
{
|
|
||||||
Vector3 vtmp = pa.CenterOfMass;
|
|
||||||
return vtmp;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return new Vector3(0, 0, 0);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float GetMass()
|
public float GetMass()
|
||||||
|
|
|
@ -2197,8 +2197,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
if (ParentID != 0)
|
if (ParentID != 0)
|
||||||
{
|
{
|
||||||
var targetPart = m_scene.GetSceneObjectPart(targetID);
|
if (ParentPart.UUID == targetID)
|
||||||
if (targetPart != null && targetPart.LocalId == ParentID)
|
|
||||||
return; // already sitting here, ignore
|
return; // already sitting here, ignore
|
||||||
|
|
||||||
StandUp();
|
StandUp();
|
||||||
|
|
|
@ -55,35 +55,35 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
// These are the IRC Connector configurable parameters with hard-wired
|
// These are the IRC Connector configurable parameters with hard-wired
|
||||||
// default values (retained for compatability).
|
// default values (retained for compatability).
|
||||||
|
|
||||||
internal string Server = null;
|
internal string Server = null;
|
||||||
internal string Password = null;
|
internal string Password = null;
|
||||||
internal string IrcChannel = null;
|
internal string IrcChannel = null;
|
||||||
internal string BaseNickname = "OSimBot";
|
internal string BaseNickname = "OSimBot";
|
||||||
internal uint Port = 6667;
|
internal uint Port = 6667;
|
||||||
internal string User = null;
|
internal string User = null;
|
||||||
|
|
||||||
internal bool ClientReporting = true;
|
internal bool ClientReporting = true;
|
||||||
internal bool RelayChat = true;
|
internal bool RelayChat = true;
|
||||||
internal bool RelayPrivateChannels = false;
|
internal bool RelayPrivateChannels = false;
|
||||||
internal int RelayChannel = 1;
|
internal int RelayChannel = 1;
|
||||||
internal List<int> ValidInWorldChannels = new List<int>();
|
internal List<int> ValidInWorldChannels = new List<int>();
|
||||||
|
|
||||||
// Connector agnostic parameters. These values are NOT shared with the
|
// Connector agnostic parameters. These values are NOT shared with the
|
||||||
// connector and do not differentiate at an IRC level
|
// connector and do not differentiate at an IRC level
|
||||||
|
|
||||||
internal string PrivateMessageFormat = "PRIVMSG {0} :<{2}> {1} {3}";
|
internal string PrivateMessageFormat = "PRIVMSG {0} :<{2}> {1} {3}";
|
||||||
internal string NoticeMessageFormat = "PRIVMSG {0} :<{2}> {3}";
|
internal string NoticeMessageFormat = "PRIVMSG {0} :<{2}> {3}";
|
||||||
internal int RelayChannelOut = -1;
|
internal int RelayChannelOut = -1;
|
||||||
internal bool RandomizeNickname = true;
|
internal bool RandomizeNickname = true;
|
||||||
internal bool CommandsEnabled = false;
|
internal bool CommandsEnabled = false;
|
||||||
internal int CommandChannel = -1;
|
internal int CommandChannel = -1;
|
||||||
internal int ConnectDelay = 10;
|
internal int ConnectDelay = 10;
|
||||||
internal int PingDelay = 15;
|
internal int PingDelay = 15;
|
||||||
internal string DefaultZone = "Sim";
|
internal string DefaultZone = "Sim";
|
||||||
|
|
||||||
internal string _accessPassword = String.Empty;
|
internal string _accessPassword = String.Empty;
|
||||||
internal Regex AccessPasswordRegex = null;
|
internal Regex AccessPasswordRegex = null;
|
||||||
internal List<string> ExcludeList = new List<string>();
|
internal List<string> ExcludeList = new List<string>();
|
||||||
internal string AccessPassword
|
internal string AccessPassword
|
||||||
{
|
{
|
||||||
get { return _accessPassword; }
|
get { return _accessPassword; }
|
||||||
|
@ -99,9 +99,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
// IRC connector reference
|
// IRC connector reference
|
||||||
|
|
||||||
internal IRCConnector irc = null;
|
internal IRCConnector irc = null;
|
||||||
|
|
||||||
internal int idn = _idk_++;
|
internal int idn = _idk_++;
|
||||||
|
|
||||||
// List of regions dependent upon this connection
|
// List of regions dependent upon this connection
|
||||||
|
|
||||||
|
@ -119,27 +119,27 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
internal ChannelState(ChannelState model)
|
internal ChannelState(ChannelState model)
|
||||||
{
|
{
|
||||||
Server = model.Server;
|
Server = model.Server;
|
||||||
Password = model.Password;
|
Password = model.Password;
|
||||||
IrcChannel = model.IrcChannel;
|
IrcChannel = model.IrcChannel;
|
||||||
Port = model.Port;
|
Port = model.Port;
|
||||||
BaseNickname = model.BaseNickname;
|
BaseNickname = model.BaseNickname;
|
||||||
RandomizeNickname = model.RandomizeNickname;
|
RandomizeNickname = model.RandomizeNickname;
|
||||||
User = model.User;
|
User = model.User;
|
||||||
CommandsEnabled = model.CommandsEnabled;
|
CommandsEnabled = model.CommandsEnabled;
|
||||||
CommandChannel = model.CommandChannel;
|
CommandChannel = model.CommandChannel;
|
||||||
RelayChat = model.RelayChat;
|
RelayChat = model.RelayChat;
|
||||||
RelayPrivateChannels = model.RelayPrivateChannels;
|
RelayPrivateChannels = model.RelayPrivateChannels;
|
||||||
RelayChannelOut = model.RelayChannelOut;
|
RelayChannelOut = model.RelayChannelOut;
|
||||||
RelayChannel = model.RelayChannel;
|
RelayChannel = model.RelayChannel;
|
||||||
ValidInWorldChannels = model.ValidInWorldChannels;
|
ValidInWorldChannels = model.ValidInWorldChannels;
|
||||||
PrivateMessageFormat = model.PrivateMessageFormat;
|
PrivateMessageFormat = model.PrivateMessageFormat;
|
||||||
NoticeMessageFormat = model.NoticeMessageFormat;
|
NoticeMessageFormat = model.NoticeMessageFormat;
|
||||||
ClientReporting = model.ClientReporting;
|
ClientReporting = model.ClientReporting;
|
||||||
AccessPassword = model.AccessPassword;
|
AccessPassword = model.AccessPassword;
|
||||||
DefaultZone = model.DefaultZone;
|
DefaultZone = model.DefaultZone;
|
||||||
ConnectDelay = model.ConnectDelay;
|
ConnectDelay = model.ConnectDelay;
|
||||||
PingDelay = model.PingDelay;
|
PingDelay = model.PingDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the configuration file, performing variable substitution and any
|
// Read the configuration file, performing variable substitution and any
|
||||||
|
@ -160,54 +160,54 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] Initial request by Region {1} to connect to IRC", cs.idn, rs.Region);
|
m_log.DebugFormat("[IRC-Channel-{0}] Initial request by Region {1} to connect to IRC", cs.idn, rs.Region);
|
||||||
|
|
||||||
cs.Server = Substitute(rs, config.GetString("server", null));
|
cs.Server = Substitute(rs, config.GetString("server", null));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] Server : <{1}>", cs.idn, cs.Server);
|
m_log.DebugFormat("[IRC-Channel-{0}] Server : <{1}>", cs.idn, cs.Server);
|
||||||
cs.Password = Substitute(rs, config.GetString("password", null));
|
cs.Password = Substitute(rs, config.GetString("password", null));
|
||||||
// probably not a good idea to put a password in the log file
|
// probably not a good idea to put a password in the log file
|
||||||
cs.User = Substitute(rs, config.GetString("user", null));
|
cs.User = Substitute(rs, config.GetString("user", null));
|
||||||
cs.IrcChannel = Substitute(rs, config.GetString("channel", null));
|
cs.IrcChannel = Substitute(rs, config.GetString("channel", null));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] IrcChannel : <{1}>", cs.idn, cs.IrcChannel);
|
m_log.DebugFormat("[IRC-Channel-{0}] IrcChannel : <{1}>", cs.idn, cs.IrcChannel);
|
||||||
cs.Port = Convert.ToUInt32(Substitute(rs, config.GetString("port", Convert.ToString(cs.Port))));
|
cs.Port = Convert.ToUInt32(Substitute(rs, config.GetString("port", Convert.ToString(cs.Port))));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] Port : <{1}>", cs.idn, cs.Port);
|
m_log.DebugFormat("[IRC-Channel-{0}] Port : <{1}>", cs.idn, cs.Port);
|
||||||
cs.BaseNickname = Substitute(rs, config.GetString("nick", cs.BaseNickname));
|
cs.BaseNickname = Substitute(rs, config.GetString("nick", cs.BaseNickname));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] BaseNickname : <{1}>", cs.idn, cs.BaseNickname);
|
m_log.DebugFormat("[IRC-Channel-{0}] BaseNickname : <{1}>", cs.idn, cs.BaseNickname);
|
||||||
cs.RandomizeNickname = Convert.ToBoolean(Substitute(rs, config.GetString("randomize_nick", Convert.ToString(cs.RandomizeNickname))));
|
cs.RandomizeNickname = Convert.ToBoolean(Substitute(rs, config.GetString("randomize_nick", Convert.ToString(cs.RandomizeNickname))));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] RandomizeNickname : <{1}>", cs.idn, cs.RandomizeNickname);
|
m_log.DebugFormat("[IRC-Channel-{0}] RandomizeNickname : <{1}>", cs.idn, cs.RandomizeNickname);
|
||||||
cs.RandomizeNickname = Convert.ToBoolean(Substitute(rs, config.GetString("nicknum", Convert.ToString(cs.RandomizeNickname))));
|
cs.RandomizeNickname = Convert.ToBoolean(Substitute(rs, config.GetString("nicknum", Convert.ToString(cs.RandomizeNickname))));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] RandomizeNickname : <{1}>", cs.idn, cs.RandomizeNickname);
|
m_log.DebugFormat("[IRC-Channel-{0}] RandomizeNickname : <{1}>", cs.idn, cs.RandomizeNickname);
|
||||||
cs.User = Substitute(rs, config.GetString("username", cs.User));
|
cs.User = Substitute(rs, config.GetString("username", cs.User));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] User : <{1}>", cs.idn, cs.User);
|
m_log.DebugFormat("[IRC-Channel-{0}] User : <{1}>", cs.idn, cs.User);
|
||||||
cs.CommandsEnabled = Convert.ToBoolean(Substitute(rs, config.GetString("commands_enabled", Convert.ToString(cs.CommandsEnabled))));
|
cs.CommandsEnabled = Convert.ToBoolean(Substitute(rs, config.GetString("commands_enabled", Convert.ToString(cs.CommandsEnabled))));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] CommandsEnabled : <{1}>", cs.idn, cs.CommandsEnabled);
|
m_log.DebugFormat("[IRC-Channel-{0}] CommandsEnabled : <{1}>", cs.idn, cs.CommandsEnabled);
|
||||||
cs.CommandChannel = Convert.ToInt32(Substitute(rs, config.GetString("commandchannel", Convert.ToString(cs.CommandChannel))));
|
cs.CommandChannel = Convert.ToInt32(Substitute(rs, config.GetString("commandchannel", Convert.ToString(cs.CommandChannel))));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] CommandChannel : <{1}>", cs.idn, cs.CommandChannel);
|
m_log.DebugFormat("[IRC-Channel-{0}] CommandChannel : <{1}>", cs.idn, cs.CommandChannel);
|
||||||
cs.CommandChannel = Convert.ToInt32(Substitute(rs, config.GetString("command_channel", Convert.ToString(cs.CommandChannel))));
|
cs.CommandChannel = Convert.ToInt32(Substitute(rs, config.GetString("command_channel", Convert.ToString(cs.CommandChannel))));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] CommandChannel : <{1}>", cs.idn, cs.CommandChannel);
|
m_log.DebugFormat("[IRC-Channel-{0}] CommandChannel : <{1}>", cs.idn, cs.CommandChannel);
|
||||||
cs.RelayChat = Convert.ToBoolean(Substitute(rs, config.GetString("relay_chat", Convert.ToString(cs.RelayChat))));
|
cs.RelayChat = Convert.ToBoolean(Substitute(rs, config.GetString("relay_chat", Convert.ToString(cs.RelayChat))));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] RelayChat : <{1}>", cs.idn, cs.RelayChat);
|
m_log.DebugFormat("[IRC-Channel-{0}] RelayChat : <{1}>", cs.idn, cs.RelayChat);
|
||||||
cs.RelayPrivateChannels = Convert.ToBoolean(Substitute(rs, config.GetString("relay_private_channels", Convert.ToString(cs.RelayPrivateChannels))));
|
cs.RelayPrivateChannels = Convert.ToBoolean(Substitute(rs, config.GetString("relay_private_channels", Convert.ToString(cs.RelayPrivateChannels))));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] RelayPrivateChannels : <{1}>", cs.idn, cs.RelayPrivateChannels);
|
m_log.DebugFormat("[IRC-Channel-{0}] RelayPrivateChannels : <{1}>", cs.idn, cs.RelayPrivateChannels);
|
||||||
cs.RelayPrivateChannels = Convert.ToBoolean(Substitute(rs, config.GetString("useworldcomm", Convert.ToString(cs.RelayPrivateChannels))));
|
cs.RelayPrivateChannels = Convert.ToBoolean(Substitute(rs, config.GetString("useworldcomm", Convert.ToString(cs.RelayPrivateChannels))));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] RelayPrivateChannels : <{1}>", cs.idn, cs.RelayPrivateChannels);
|
m_log.DebugFormat("[IRC-Channel-{0}] RelayPrivateChannels : <{1}>", cs.idn, cs.RelayPrivateChannels);
|
||||||
cs.RelayChannelOut = Convert.ToInt32(Substitute(rs, config.GetString("relay_private_channel_out", Convert.ToString(cs.RelayChannelOut))));
|
cs.RelayChannelOut = Convert.ToInt32(Substitute(rs, config.GetString("relay_private_channel_out", Convert.ToString(cs.RelayChannelOut))));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] RelayChannelOut : <{1}>", cs.idn, cs.RelayChannelOut);
|
m_log.DebugFormat("[IRC-Channel-{0}] RelayChannelOut : <{1}>", cs.idn, cs.RelayChannelOut);
|
||||||
cs.RelayChannel = Convert.ToInt32(Substitute(rs, config.GetString("relay_private_channel_in", Convert.ToString(cs.RelayChannel))));
|
cs.RelayChannel = Convert.ToInt32(Substitute(rs, config.GetString("relay_private_channel_in", Convert.ToString(cs.RelayChannel))));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] RelayChannel : <{1}>", cs.idn, cs.RelayChannel);
|
m_log.DebugFormat("[IRC-Channel-{0}] RelayChannel : <{1}>", cs.idn, cs.RelayChannel);
|
||||||
cs.RelayChannel = Convert.ToInt32(Substitute(rs, config.GetString("inchannel", Convert.ToString(cs.RelayChannel))));
|
cs.RelayChannel = Convert.ToInt32(Substitute(rs, config.GetString("inchannel", Convert.ToString(cs.RelayChannel))));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] RelayChannel : <{1}>", cs.idn, cs.RelayChannel);
|
m_log.DebugFormat("[IRC-Channel-{0}] RelayChannel : <{1}>", cs.idn, cs.RelayChannel);
|
||||||
cs.PrivateMessageFormat = Substitute(rs, config.GetString("msgformat", cs.PrivateMessageFormat));
|
cs.PrivateMessageFormat = Substitute(rs, config.GetString("msgformat", cs.PrivateMessageFormat));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] PrivateMessageFormat : <{1}>", cs.idn, cs.PrivateMessageFormat);
|
m_log.DebugFormat("[IRC-Channel-{0}] PrivateMessageFormat : <{1}>", cs.idn, cs.PrivateMessageFormat);
|
||||||
cs.NoticeMessageFormat = Substitute(rs, config.GetString("noticeformat", cs.NoticeMessageFormat));
|
cs.NoticeMessageFormat = Substitute(rs, config.GetString("noticeformat", cs.NoticeMessageFormat));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] NoticeMessageFormat : <{1}>", cs.idn, 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);
|
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))));
|
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);
|
m_log.DebugFormat("[IRC-Channel-{0}] ClientReporting : <{1}>", cs.idn, cs.ClientReporting);
|
||||||
cs.DefaultZone = Substitute(rs, config.GetString("fallback_region", cs.DefaultZone));
|
cs.DefaultZone = Substitute(rs, config.GetString("fallback_region", cs.DefaultZone));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] DefaultZone : <{1}>", cs.idn, cs.DefaultZone);
|
m_log.DebugFormat("[IRC-Channel-{0}] DefaultZone : <{1}>", cs.idn, cs.DefaultZone);
|
||||||
cs.ConnectDelay = Convert.ToInt32(Substitute(rs, config.GetString("connect_delay", Convert.ToString(cs.ConnectDelay))));
|
cs.ConnectDelay = Convert.ToInt32(Substitute(rs, config.GetString("connect_delay", Convert.ToString(cs.ConnectDelay))));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] ConnectDelay : <{1}>", cs.idn, cs.ConnectDelay);
|
m_log.DebugFormat("[IRC-Channel-{0}] ConnectDelay : <{1}>", cs.idn, cs.ConnectDelay);
|
||||||
cs.PingDelay = Convert.ToInt32(Substitute(rs, config.GetString("ping_delay", Convert.ToString(cs.PingDelay))));
|
cs.PingDelay = Convert.ToInt32(Substitute(rs, config.GetString("ping_delay", Convert.ToString(cs.PingDelay))));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] PingDelay : <{1}>", cs.idn, cs.PingDelay);
|
m_log.DebugFormat("[IRC-Channel-{0}] PingDelay : <{1}>", cs.idn, cs.PingDelay);
|
||||||
cs.AccessPassword = Substitute(rs, config.GetString("access_password", cs.AccessPassword));
|
cs.AccessPassword = Substitute(rs, config.GetString("access_password", cs.AccessPassword));
|
||||||
m_log.DebugFormat("[IRC-Channel-{0}] AccessPassword : <{1}>", cs.idn, cs.AccessPassword);
|
m_log.DebugFormat("[IRC-Channel-{0}] AccessPassword : <{1}>", cs.idn, cs.AccessPassword);
|
||||||
|
@ -473,27 +473,27 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
{
|
{
|
||||||
|
|
||||||
string vvar = arg.Match(result).ToString();
|
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())
|
switch (var.ToLower())
|
||||||
{
|
{
|
||||||
case "%region" :
|
case "%region":
|
||||||
result = result.Replace(vvar, rs.Region);
|
result = result.Replace(vvar, rs.Region);
|
||||||
break;
|
break;
|
||||||
case "%host" :
|
case "%host":
|
||||||
result = result.Replace(vvar, rs.Host);
|
result = result.Replace(vvar, rs.Host);
|
||||||
break;
|
break;
|
||||||
case "%locx" :
|
case "%locx":
|
||||||
result = result.Replace(vvar, rs.LocX);
|
result = result.Replace(vvar, rs.LocX);
|
||||||
break;
|
break;
|
||||||
case "%locy" :
|
case "%locy":
|
||||||
result = result.Replace(vvar, rs.LocY);
|
result = result.Replace(vvar, rs.LocY);
|
||||||
break;
|
break;
|
||||||
case "%k" :
|
case "%k":
|
||||||
result = result.Replace(vvar, rs.IDK);
|
result = result.Replace(vvar, rs.IDK);
|
||||||
break;
|
break;
|
||||||
default :
|
default:
|
||||||
result = result.Replace(vvar, rs.config.GetString(var,var));
|
result = result.Replace(vvar, rs.config.GetString(var, var));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// m_log.DebugFormat("[IRC-Channel] Parse[2]: {0}", result);
|
// m_log.DebugFormat("[IRC-Channel] Parse[2]: {0}", result);
|
||||||
|
|
|
@ -46,11 +46,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
{
|
{
|
||||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
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 IConfig m_config = null;
|
||||||
|
|
||||||
internal static List<ChannelState> m_channels = new List<ChannelState>();
|
internal static List<ChannelState> m_channels = new List<ChannelState>();
|
||||||
internal static List<RegionState> m_regions = new List<RegionState>();
|
internal static List<RegionState> m_regions = new List<RegionState>();
|
||||||
|
|
||||||
internal static string m_password = String.Empty;
|
internal static string m_password = String.Empty;
|
||||||
internal RegionState m_region = null;
|
internal RegionState m_region = null;
|
||||||
|
@ -72,13 +72,13 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
m_config = config.Configs["IRC"];
|
m_config = config.Configs["IRC"];
|
||||||
if (m_config == null)
|
if (m_config == null)
|
||||||
{
|
{
|
||||||
// m_log.InfoFormat("[IRC-Bridge] module not configured");
|
// m_log.InfoFormat("[IRC-Bridge] module not configured");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_config.GetBoolean("enabled", false))
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,19 +87,22 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
m_password = config.Configs["RemoteAdmin"].GetString("access_password", m_password);
|
m_password = config.Configs["RemoteAdmin"].GetString("access_password", m_password);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_pluginEnabled = true;
|
Enabled = true;
|
||||||
m_log.InfoFormat("[IRC-Bridge]: Module enabled");
|
|
||||||
|
m_log.InfoFormat("[IRC-Bridge]: Module is enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddRegion(Scene scene)
|
public void AddRegion(Scene scene)
|
||||||
{
|
{
|
||||||
if (m_pluginEnabled)
|
if (Enabled)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
m_log.InfoFormat("[IRC-Bridge] Connecting region {0}", scene.RegionInfo.RegionName);
|
m_log.InfoFormat("[IRC-Bridge] Connecting region {0}", scene.RegionInfo.RegionName);
|
||||||
|
|
||||||
if (!String.IsNullOrEmpty(m_password))
|
if (!String.IsNullOrEmpty(m_password))
|
||||||
MainServer.Instance.AddXmlRPCHandler("irc_admin", XmlRpcAdminMethod, false);
|
MainServer.Instance.AddXmlRPCHandler("irc_admin", XmlRpcAdminMethod, false);
|
||||||
|
|
||||||
m_region = new RegionState(scene, m_config);
|
m_region = new RegionState(scene, m_config);
|
||||||
lock (m_regions) m_regions.Add(m_region);
|
lock (m_regions) m_regions.Add(m_region);
|
||||||
m_region.Open();
|
m_region.Open();
|
||||||
|
@ -123,7 +126,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
public void RemoveRegion(Scene scene)
|
public void RemoveRegion(Scene scene)
|
||||||
{
|
{
|
||||||
if (!m_pluginEnabled)
|
if (!Enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_region == null)
|
if (m_region == null)
|
||||||
|
@ -150,12 +153,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
m_log.Debug("[IRC-Bridge]: XML RPC Admin Entry");
|
m_log.Debug("[IRC-Bridge]: XML RPC Admin Entry");
|
||||||
|
|
||||||
XmlRpcResponse response = new XmlRpcResponse();
|
XmlRpcResponse response = new XmlRpcResponse();
|
||||||
Hashtable responseData = new Hashtable();
|
Hashtable responseData = new Hashtable();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Hashtable requestData = (Hashtable)request.Params[0];
|
Hashtable requestData = (Hashtable)request.Params[0];
|
||||||
bool found = false;
|
bool found = false;
|
||||||
string region = String.Empty;
|
string region = String.Empty;
|
||||||
|
|
||||||
if (m_password != String.Empty)
|
if (m_password != String.Empty)
|
||||||
|
@ -174,13 +177,13 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
{
|
{
|
||||||
if (rs.Region == region)
|
if (rs.Region == region)
|
||||||
{
|
{
|
||||||
responseData["server"] = rs.cs.Server;
|
responseData["server"] = rs.cs.Server;
|
||||||
responseData["port"] = (int)rs.cs.Port;
|
responseData["port"] = (int)rs.cs.Port;
|
||||||
responseData["user"] = rs.cs.User;
|
responseData["user"] = rs.cs.User;
|
||||||
responseData["channel"] = rs.cs.IrcChannel;
|
responseData["channel"] = rs.cs.IrcChannel;
|
||||||
responseData["enabled"] = rs.cs.irc.Enabled;
|
responseData["enabled"] = rs.cs.irc.Enabled;
|
||||||
responseData["connected"] = rs.cs.irc.Connected;
|
responseData["connected"] = rs.cs.irc.Connected;
|
||||||
responseData["nickname"] = rs.cs.irc.Nick;
|
responseData["nickname"] = rs.cs.irc.Nick;
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -195,7 +198,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
m_log.ErrorFormat("[IRC-Bridge] XML RPC Admin request failed : {0}", e.Message);
|
m_log.ErrorFormat("[IRC-Bridge] XML RPC Admin request failed : {0}", e.Message);
|
||||||
|
|
||||||
responseData["success"] = "false";
|
responseData["success"] = "false";
|
||||||
responseData["error"] = e.Message;
|
responseData["error"] = e.Message;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
|
@ -53,16 +53,16 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
// Local constants
|
// Local constants
|
||||||
|
|
||||||
private static readonly Vector3 CenterOfRegion = new Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 20);
|
private static readonly Vector3 CenterOfRegion = new Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 20);
|
||||||
private static readonly char[] CS_SPACE = { ' ' };
|
private static readonly char[] CS_SPACE = { ' ' };
|
||||||
|
|
||||||
private const int WD_INTERVAL = 1000; // base watchdog interval
|
private const int WD_INTERVAL = 1000; // base watchdog interval
|
||||||
private static int PING_PERIOD = 15; // WD intervals per PING
|
private static int PING_PERIOD = 15; // WD intervals per PING
|
||||||
private static int ICCD_PERIOD = 10; // WD intervals between Connects
|
private static int ICCD_PERIOD = 10; // WD intervals between Connects
|
||||||
private static int L_TIMEOUT = 25; // Login time out interval
|
private static int L_TIMEOUT = 25; // Login time out interval
|
||||||
|
|
||||||
private static int _idk_ = 0; // core connector identifier
|
private static int _idk_ = 0; // core connector identifier
|
||||||
private static int _pdk_ = 0; // ping interval counter
|
private static int _pdk_ = 0; // ping interval counter
|
||||||
private static int _icc_ = ICCD_PERIOD; // IRC connect counter
|
private static int _icc_ = ICCD_PERIOD; // IRC connect counter
|
||||||
|
|
||||||
// List of configured connectors
|
// List of configured connectors
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
private Object msyncConnect = new Object();
|
private Object msyncConnect = new Object();
|
||||||
|
|
||||||
internal bool m_randomizeNick = true; // add random suffix
|
internal bool m_randomizeNick = true; // add random suffix
|
||||||
internal string m_baseNick = null; // base name for randomizing
|
internal string m_baseNick = null; // base name for randomizing
|
||||||
internal string m_nick = null; // effective nickname
|
internal string m_nick = null; // effective nickname
|
||||||
|
|
||||||
|
@ -130,8 +130,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool m_connected = false; // connection status
|
private bool m_connected = false; // connection status
|
||||||
private bool m_pending = false; // login disposition
|
private bool m_pending = false; // login disposition
|
||||||
private int m_timeout = L_TIMEOUT; // login timeout counter
|
private int m_timeout = L_TIMEOUT; // login timeout counter
|
||||||
public bool Connected
|
public bool Connected
|
||||||
{
|
{
|
||||||
get { return m_connected; }
|
get { return m_connected; }
|
||||||
|
@ -172,10 +172,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
// Network interface
|
// Network interface
|
||||||
|
|
||||||
private TcpClient m_tcp;
|
private TcpClient m_tcp;
|
||||||
private NetworkStream m_stream = null;
|
private NetworkStream m_stream = null;
|
||||||
private StreamReader m_reader;
|
private StreamReader m_reader;
|
||||||
private StreamWriter m_writer;
|
private StreamWriter m_writer;
|
||||||
|
|
||||||
// Channel characteristic info (if available)
|
// Channel characteristic info (if available)
|
||||||
|
|
||||||
|
@ -193,26 +193,26 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
// Prepare network interface
|
// Prepare network interface
|
||||||
|
|
||||||
m_tcp = null;
|
m_tcp = null;
|
||||||
m_writer = null;
|
m_writer = null;
|
||||||
m_reader = null;
|
m_reader = null;
|
||||||
|
|
||||||
// Setup IRC session parameters
|
// Setup IRC session parameters
|
||||||
|
|
||||||
m_server = cs.Server;
|
m_server = cs.Server;
|
||||||
m_password = cs.Password;
|
m_password = cs.Password;
|
||||||
m_baseNick = cs.BaseNickname;
|
m_baseNick = cs.BaseNickname;
|
||||||
m_randomizeNick = cs.RandomizeNickname;
|
m_randomizeNick = cs.RandomizeNickname;
|
||||||
m_ircChannel = cs.IrcChannel;
|
m_ircChannel = cs.IrcChannel;
|
||||||
m_port = cs.Port;
|
m_port = cs.Port;
|
||||||
m_user = cs.User;
|
m_user = cs.User;
|
||||||
|
|
||||||
if (m_watchdog == null)
|
if (m_watchdog == null)
|
||||||
{
|
{
|
||||||
// Non-differentiating
|
// Non-differentiating
|
||||||
|
|
||||||
ICCD_PERIOD = cs.ConnectDelay;
|
ICCD_PERIOD = cs.ConnectDelay;
|
||||||
PING_PERIOD = cs.PingDelay;
|
PING_PERIOD = cs.PingDelay;
|
||||||
|
|
||||||
// Smaller values are not reasonable
|
// Smaller values are not reasonable
|
||||||
|
|
||||||
|
@ -295,15 +295,19 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
m_nick, m_ircChannel, m_server));
|
m_nick, m_ircChannel, m_server));
|
||||||
m_writer.Flush();
|
m_writer.Flush();
|
||||||
}
|
}
|
||||||
catch (Exception) {}
|
catch (Exception) { }
|
||||||
|
|
||||||
|
|
||||||
m_connected = false;
|
m_connected = false;
|
||||||
|
|
||||||
try { m_writer.Close(); } catch (Exception) {}
|
try { m_writer.Close(); }
|
||||||
try { m_reader.Close(); } catch (Exception) {}
|
catch (Exception) { }
|
||||||
try { m_stream.Close(); } catch (Exception) {}
|
try { m_reader.Close(); }
|
||||||
try { m_tcp.Close(); } catch (Exception) {}
|
catch (Exception) { }
|
||||||
|
try { m_stream.Close(); }
|
||||||
|
catch (Exception) { }
|
||||||
|
try { m_tcp.Close(); }
|
||||||
|
catch (Exception) { }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,10 +351,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
if (m_connected) return;
|
if (m_connected) return;
|
||||||
|
|
||||||
m_connected = true;
|
m_connected = true;
|
||||||
m_pending = true;
|
m_pending = true;
|
||||||
m_timeout = L_TIMEOUT;
|
m_timeout = L_TIMEOUT;
|
||||||
|
|
||||||
m_tcp = new TcpClient(m_server, (int)m_port);
|
m_tcp = new TcpClient(m_server, (int)m_port);
|
||||||
m_stream = m_tcp.GetStream();
|
m_stream = m_tcp.GetStream();
|
||||||
m_reader = new StreamReader(m_stream);
|
m_reader = new StreamReader(m_stream);
|
||||||
m_writer = new StreamWriter(m_stream);
|
m_writer = new StreamWriter(m_stream);
|
||||||
|
@ -418,12 +422,15 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
// the socket and it will disappear of its own accord, once this
|
// the socket and it will disappear of its own accord, once this
|
||||||
// processing is completed.
|
// processing is completed.
|
||||||
|
|
||||||
try { m_writer.Close(); } catch (Exception) {}
|
try { m_writer.Close(); }
|
||||||
try { m_reader.Close(); } catch (Exception) {}
|
catch (Exception) { }
|
||||||
try { m_tcp.Close(); } catch (Exception) {}
|
try { m_reader.Close(); }
|
||||||
|
catch (Exception) { }
|
||||||
|
try { m_tcp.Close(); }
|
||||||
|
catch (Exception) { }
|
||||||
|
|
||||||
m_connected = false;
|
m_connected = false;
|
||||||
m_pending = false;
|
m_pending = false;
|
||||||
m_resetk++;
|
m_resetk++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -495,7 +502,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
{
|
{
|
||||||
|
|
||||||
string inputLine;
|
string inputLine;
|
||||||
int resetk = m_resetk;
|
int resetk = m_resetk;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -617,8 +624,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
string[] commArgs;
|
string[] commArgs;
|
||||||
string c_server = m_server;
|
string c_server = m_server;
|
||||||
|
|
||||||
string pfx = String.Empty;
|
string pfx = String.Empty;
|
||||||
string cmd = String.Empty;
|
string cmd = String.Empty;
|
||||||
string parms = String.Empty;
|
string parms = String.Empty;
|
||||||
|
|
||||||
// ":" indicates that a prefix is present
|
// ":" indicates that a prefix is present
|
||||||
|
@ -627,15 +634,15 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
// ":" indicates that the remainder of the
|
// ":" indicates that the remainder of the
|
||||||
// line is a single parameter value.
|
// line is a single parameter value.
|
||||||
|
|
||||||
commArgs = command.Split(CS_SPACE,2);
|
commArgs = command.Split(CS_SPACE, 2);
|
||||||
|
|
||||||
if (commArgs[0].StartsWith(":"))
|
if (commArgs[0].StartsWith(":"))
|
||||||
{
|
{
|
||||||
pfx = commArgs[0].Substring(1);
|
pfx = commArgs[0].Substring(1);
|
||||||
commArgs = commArgs[1].Split(CS_SPACE,2);
|
commArgs = commArgs[1].Split(CS_SPACE, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = commArgs[0];
|
cmd = commArgs[0];
|
||||||
parms = commArgs[1];
|
parms = commArgs[1];
|
||||||
|
|
||||||
// m_log.DebugFormat("[IRC-Connector-{0}] prefix = <{1}> cmd = <{2}>", idn, pfx, cmd);
|
// m_log.DebugFormat("[IRC-Connector-{0}] prefix = <{1}> cmd = <{2}>", idn, pfx, cmd);
|
||||||
|
@ -646,44 +653,44 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
// Messages 001-004 are always sent
|
// Messages 001-004 are always sent
|
||||||
// following signon.
|
// following signon.
|
||||||
|
|
||||||
case "001" : // Welcome ...
|
case "001": // Welcome ...
|
||||||
case "002" : // Server information
|
case "002": // Server information
|
||||||
case "003" : // Welcome ...
|
case "003": // Welcome ...
|
||||||
break;
|
break;
|
||||||
case "004" : // Server information
|
case "004": // Server information
|
||||||
m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
|
m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
|
||||||
commArgs = parms.Split(CS_SPACE);
|
commArgs = parms.Split(CS_SPACE);
|
||||||
c_server = commArgs[1];
|
c_server = commArgs[1];
|
||||||
m_server = c_server;
|
m_server = c_server;
|
||||||
version = commArgs[2];
|
version = commArgs[2];
|
||||||
usermod = commArgs[3];
|
usermod = commArgs[3];
|
||||||
chanmod = commArgs[4];
|
chanmod = commArgs[4];
|
||||||
break;
|
break;
|
||||||
case "005" : // Server information
|
case "005": // Server information
|
||||||
break;
|
break;
|
||||||
case "042" :
|
case "042":
|
||||||
case "250" :
|
case "250":
|
||||||
case "251" :
|
case "251":
|
||||||
case "252" :
|
case "252":
|
||||||
case "254" :
|
case "254":
|
||||||
case "255" :
|
case "255":
|
||||||
case "265" :
|
case "265":
|
||||||
case "266" :
|
case "266":
|
||||||
case "332" : // Subject
|
case "332": // Subject
|
||||||
case "333" : // Subject owner (?)
|
case "333": // Subject owner (?)
|
||||||
case "353" : // Name list
|
case "353": // Name list
|
||||||
case "366" : // End-of-Name list marker
|
case "366": // End-of-Name list marker
|
||||||
case "372" : // MOTD body
|
case "372": // MOTD body
|
||||||
case "375" : // MOTD start
|
case "375": // MOTD start
|
||||||
// m_log.InfoFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]);
|
// m_log.InfoFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]);
|
||||||
break;
|
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]);
|
// m_log.InfoFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]);
|
||||||
motd = true;
|
motd = true;
|
||||||
break;
|
break;
|
||||||
case "451" : // Not registered
|
case "451": // Not registered
|
||||||
break;
|
break;
|
||||||
case "433" : // Nickname in use
|
case "433": // Nickname in use
|
||||||
// Gen a new name
|
// Gen a new name
|
||||||
m_nick = m_baseNick + Util.RandomClass.Next(1, 99);
|
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);
|
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.WriteLine(String.Format("JOIN {0}", m_ircChannel));
|
||||||
m_writer.Flush();
|
m_writer.Flush();
|
||||||
break;
|
break;
|
||||||
case "479" : // Bad channel name, etc. This will never work, so disable the connection
|
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}] {2}", idn, cmd, parms.Split(CS_SPACE, 2)[1]);
|
||||||
m_log.ErrorFormat("[IRC-Connector-{0}] [{1}] Connector disabled", idn, cmd);
|
m_log.ErrorFormat("[IRC-Connector-{0}] [{1}] Connector disabled", idn, cmd);
|
||||||
m_enabled = false;
|
m_enabled = false;
|
||||||
m_connected = false;
|
m_connected = false;
|
||||||
m_pending = false;
|
m_pending = false;
|
||||||
break;
|
break;
|
||||||
case "NOTICE" :
|
case "NOTICE":
|
||||||
// m_log.WarnFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]);
|
// m_log.WarnFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]);
|
||||||
break;
|
break;
|
||||||
case "ERROR" :
|
case "ERROR":
|
||||||
m_log.ErrorFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE,2)[1]);
|
m_log.ErrorFormat("[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE, 2)[1]);
|
||||||
if (parms.Contains("reconnect too fast"))
|
if (parms.Contains("reconnect too fast"))
|
||||||
ICCD_PERIOD++;
|
ICCD_PERIOD++;
|
||||||
m_pending = false;
|
m_pending = false;
|
||||||
Reconnect();
|
Reconnect();
|
||||||
break;
|
break;
|
||||||
case "PING" :
|
case "PING":
|
||||||
m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
|
m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
|
||||||
m_writer.WriteLine(String.Format("PONG {0}", parms));
|
m_writer.WriteLine(String.Format("PONG {0}", parms));
|
||||||
m_writer.Flush();
|
m_writer.Flush();
|
||||||
break;
|
break;
|
||||||
case "PONG" :
|
case "PONG":
|
||||||
break;
|
break;
|
||||||
case "JOIN":
|
case "JOIN":
|
||||||
if (m_pending)
|
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);
|
m_log.DebugFormat("[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
|
||||||
eventIrcQuit(pfx, cmd, parms);
|
eventIrcQuit(pfx, cmd, parms);
|
||||||
break;
|
break;
|
||||||
default :
|
default:
|
||||||
m_log.DebugFormat("[IRC-Connector-{0}] Command '{1}' ignored, parms = {2}", idn, cmd, parms);
|
m_log.DebugFormat("[IRC-Connector-{0}] Command '{1}' ignored, parms = {2}", idn, cmd, parms);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -759,8 +766,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
public void eventIrcJoin(string prefix, string command, string parms)
|
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 IrcUser = prefix.Split('!')[0];
|
||||||
string IrcChannel = args[0];
|
string IrcChannel = args[0];
|
||||||
|
|
||||||
if (IrcChannel.StartsWith(":"))
|
if (IrcChannel.StartsWith(":"))
|
||||||
|
@ -772,8 +779,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
public void eventIrcPart(string prefix, string command, string parms)
|
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 IrcUser = prefix.Split('!')[0];
|
||||||
string IrcChannel = args[0];
|
string IrcChannel = args[0];
|
||||||
|
|
||||||
m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCPart {1}:{2}", idn, m_server, m_ircChannel);
|
m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCPart {1}:{2}", idn, m_server, m_ircChannel);
|
||||||
|
@ -782,7 +789,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
public void eventIrcMode(string prefix, string command, string parms)
|
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];
|
string UserMode = args[1];
|
||||||
|
|
||||||
m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCMode {1}:{2}", idn, m_server, m_ircChannel);
|
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)
|
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 UserOldNick = prefix.Split('!')[0];
|
||||||
string UserNewNick = args[0].Remove(0, 1);
|
string UserNewNick = args[0].Remove(0, 1);
|
||||||
|
|
||||||
|
@ -804,10 +811,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
public void eventIrcKick(string prefix, string command, string parms)
|
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 UserKicker = prefix.Split('!')[0];
|
||||||
string IrcChannel = args[0];
|
string IrcChannel = args[0];
|
||||||
string UserKicked = args[1];
|
string UserKicked = args[1];
|
||||||
string KickMessage = args[2];
|
string KickMessage = args[2];
|
||||||
|
|
||||||
m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCKick {1}:{2}", idn, m_server, m_ircChannel);
|
m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCKick {1}:{2}", idn, m_server, m_ircChannel);
|
||||||
|
@ -822,7 +829,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
public void eventIrcQuit(string prefix, string command, string parms)
|
public void eventIrcQuit(string prefix, string command, string parms)
|
||||||
{
|
{
|
||||||
string IrcUser = prefix.Split('!')[0];
|
string IrcUser = prefix.Split('!')[0];
|
||||||
string QuitMessage = parms;
|
string QuitMessage = parms;
|
||||||
|
|
||||||
m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCQuit {1}:{2}", idn, m_server, m_ircChannel);
|
m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCQuit {1}:{2}", idn, m_server, m_ircChannel);
|
||||||
|
@ -842,65 +849,65 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
// m_log.InfoFormat("[IRC-Watchdog] Status scan, pdk = {0}, icc = {1}", _pdk_, _icc_);
|
// 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
|
_icc_++; // increment the inter-consecutive-connect-delay counter
|
||||||
|
|
||||||
lock (m_connectors)
|
lock (m_connectors)
|
||||||
foreach (IRCConnector connector in m_connectors)
|
foreach (IRCConnector connector in m_connectors)
|
||||||
{
|
|
||||||
|
|
||||||
// m_log.InfoFormat("[IRC-Watchdog] Scanning {0}", connector);
|
|
||||||
|
|
||||||
if (connector.Enabled)
|
|
||||||
{
|
{
|
||||||
if (!connector.Connected)
|
|
||||||
|
// m_log.InfoFormat("[IRC-Watchdog] Scanning {0}", connector);
|
||||||
|
|
||||||
|
if (connector.Enabled)
|
||||||
{
|
{
|
||||||
try
|
if (!connector.Connected)
|
||||||
{
|
|
||||||
// m_log.DebugFormat("[IRC-Watchdog] Connecting {1}:{2}", connector.idn, connector.m_server, connector.m_ircChannel);
|
|
||||||
connector.Connect();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
m_log.ErrorFormat("[IRC-Watchdog] Exception on connector {0}: {1} ", connector.idn, e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
if (connector.m_pending)
|
|
||||||
{
|
|
||||||
if (connector.m_timeout == 0)
|
|
||||||
{
|
|
||||||
m_log.ErrorFormat("[IRC-Watchdog] Login timed-out for connector {0}, reconnecting", connector.idn);
|
|
||||||
connector.Reconnect();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
connector.m_timeout--;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Being marked connected is not enough to ping. Socket establishment can sometimes take a long
|
|
||||||
// time, in which case the watch dog might try to ping the server before the socket has been
|
|
||||||
// set up, with nasty side-effects.
|
|
||||||
|
|
||||||
else if (_pdk_ == 0)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
connector.m_writer.WriteLine(String.Format("PING :{0}", connector.m_server));
|
// m_log.DebugFormat("[IRC-Watchdog] Connecting {1}:{2}", connector.idn, connector.m_server, connector.m_ircChannel);
|
||||||
connector.m_writer.Flush();
|
connector.Connect();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
m_log.ErrorFormat("[IRC-PingRun] Exception on connector {0}: {1} ", connector.idn, e.Message);
|
m_log.ErrorFormat("[IRC-Watchdog] Exception on connector {0}: {1} ", connector.idn, e.Message);
|
||||||
m_log.Debug(e);
|
|
||||||
connector.Reconnect();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
if (connector.m_pending)
|
||||||
|
{
|
||||||
|
if (connector.m_timeout == 0)
|
||||||
|
{
|
||||||
|
m_log.ErrorFormat("[IRC-Watchdog] Login timed-out for connector {0}, reconnecting", connector.idn);
|
||||||
|
connector.Reconnect();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
connector.m_timeout--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Being marked connected is not enough to ping. Socket establishment can sometimes take a long
|
||||||
|
// time, in which case the watch dog might try to ping the server before the socket has been
|
||||||
|
// set up, with nasty side-effects.
|
||||||
|
|
||||||
|
else if (_pdk_ == 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
connector.m_writer.WriteLine(String.Format("PING :{0}", connector.m_server));
|
||||||
|
connector.m_writer.Flush();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
m_log.ErrorFormat("[IRC-PingRun] Exception on connector {0}: {1} ", connector.idn, e.Message);
|
||||||
|
m_log.Debug(e);
|
||||||
|
connector.Reconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// m_log.InfoFormat("[IRC-Watchdog] Status scan completed");
|
// m_log.InfoFormat("[IRC-Watchdog] Status scan completed");
|
||||||
|
|
||||||
|
|
|
@ -41,49 +41,71 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
internal class RegionState
|
internal class RegionState
|
||||||
{
|
{
|
||||||
|
|
||||||
private static readonly ILog m_log =
|
private static readonly ILog m_log =
|
||||||
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
private static readonly OpenMetaverse.Vector3 CenterOfRegion = new OpenMetaverse.Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 20);
|
private static readonly OpenMetaverse.Vector3 CenterOfRegion = new OpenMetaverse.Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 20);
|
||||||
private const int DEBUG_CHANNEL = 2147483647;
|
private const int DEBUG_CHANNEL = 2147483647;
|
||||||
|
|
||||||
private static int _idk_ = 0;
|
private static int _idk_ = 0;
|
||||||
|
|
||||||
// Runtime variables; these values are assigned when the
|
// Runtime variables; these values are assigned when the
|
||||||
// IrcState is created and remain constant thereafter.
|
// IrcState is created and remain constant thereafter.
|
||||||
|
|
||||||
internal string Region = String.Empty;
|
internal string Region = String.Empty;
|
||||||
internal string Host = String.Empty;
|
internal string Host = String.Empty;
|
||||||
internal string LocX = String.Empty;
|
internal string LocX = String.Empty;
|
||||||
internal string LocY = String.Empty;
|
internal string LocY = String.Empty;
|
||||||
internal string IDK = String.Empty;
|
internal string IDK = String.Empty;
|
||||||
|
|
||||||
// System values - used only be the IRC classes themselves
|
// System values - used only be the IRC classes themselves
|
||||||
|
|
||||||
internal ChannelState cs = null; // associated IRC configuration
|
internal ChannelState cs = null; // associated IRC configuration
|
||||||
internal Scene scene = null; // associated scene
|
internal Scene scene = null; // associated scene
|
||||||
internal IConfig config = null; // configuration file reference
|
internal IConfig config = null; // configuration file reference
|
||||||
internal bool enabled = true;
|
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
|
// This list is used to keep track of who is here, and by
|
||||||
// implication, who is not.
|
// implication, who is not.
|
||||||
|
|
||||||
internal List<IClientAPI> clients = new List<IClientAPI>();
|
internal List<IClientAPI> clients = new List<IClientAPI>();
|
||||||
|
|
||||||
// Setup runtime variable values
|
// Setup runtime variable values
|
||||||
|
|
||||||
public RegionState(Scene p_scene, IConfig p_config)
|
public RegionState(Scene p_scene, IConfig p_config)
|
||||||
{
|
{
|
||||||
|
scene = p_scene;
|
||||||
scene = p_scene;
|
|
||||||
config = p_config;
|
config = p_config;
|
||||||
|
|
||||||
Region = scene.RegionInfo.RegionName;
|
Region = scene.RegionInfo.RegionName;
|
||||||
Host = scene.RegionInfo.ExternalHostName;
|
Host = scene.RegionInfo.ExternalHostName;
|
||||||
LocX = Convert.ToString(scene.RegionInfo.RegionLocX);
|
LocX = Convert.ToString(scene.RegionInfo.RegionLocX);
|
||||||
LocY = Convert.ToString(scene.RegionInfo.RegionLocY);
|
LocY = Convert.ToString(scene.RegionInfo.RegionLocY);
|
||||||
IDK = Convert.ToString(_idk_++);
|
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
|
// OpenChannel conditionally establishes a connection to the
|
||||||
// IRC server. The request will either succeed, or it will
|
// IRC server. The request will either succeed, or it will
|
||||||
|
@ -93,9 +115,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
// Connect channel to world events
|
// Connect channel to world events
|
||||||
|
|
||||||
scene.EventManager.OnChatFromWorld += OnSimChat;
|
scene.EventManager.OnChatFromWorld += OnSimChat;
|
||||||
scene.EventManager.OnChatFromClient += OnSimChat;
|
scene.EventManager.OnChatFromClient += OnSimChat;
|
||||||
scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
|
scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
|
||||||
scene.EventManager.OnMakeChildAgent += OnMakeChildAgent;
|
scene.EventManager.OnMakeChildAgent += OnMakeChildAgent;
|
||||||
|
|
||||||
m_log.InfoFormat("[IRC-Region {0}] Initialization complete", Region);
|
m_log.InfoFormat("[IRC-Region {0}] Initialization complete", Region);
|
||||||
|
@ -106,8 +128,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
~RegionState()
|
~RegionState()
|
||||||
{
|
{
|
||||||
if (cs != null)
|
if (cs != null)
|
||||||
cs.RemoveRegion(this);
|
cs.RemoveRegion(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by PostInitialize after all regions have been created
|
// Called by PostInitialize after all regions have been created
|
||||||
|
@ -147,7 +169,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
cs.irc.PrivMsg(cs.NoticeMessageFormat, cs.irc.Nick, Region, String.Format("{0} has left", client.Name));
|
cs.irc.PrivMsg(cs.NoticeMessageFormat, cs.irc.Nick, Region, String.Format("{0} has left", client.Name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client.OnLogout -= OnClientLoggedOut;
|
client.OnLogout -= OnClientLoggedOut;
|
||||||
client.OnConnectionClosed -= OnClientLoggedOut;
|
client.OnConnectionClosed -= OnClientLoggedOut;
|
||||||
clients.Remove(client);
|
clients.Remove(client);
|
||||||
}
|
}
|
||||||
|
@ -177,7 +199,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
m_log.DebugFormat("[IRC-Region {0}] {1} has left", Region, clientName);
|
m_log.DebugFormat("[IRC-Region {0}] {1} has left", Region, clientName);
|
||||||
cs.irc.PrivMsg(cs.NoticeMessageFormat, cs.irc.Nick, Region, String.Format("{0} has left", clientName));
|
cs.irc.PrivMsg(cs.NoticeMessageFormat, cs.irc.Nick, Region, String.Format("{0} has left", clientName));
|
||||||
}
|
}
|
||||||
client.OnLogout -= OnClientLoggedOut;
|
client.OnLogout -= OnClientLoggedOut;
|
||||||
client.OnConnectionClosed -= OnClientLoggedOut;
|
client.OnConnectionClosed -= OnClientLoggedOut;
|
||||||
clients.Remove(client);
|
clients.Remove(client);
|
||||||
}
|
}
|
||||||
|
@ -195,14 +217,13 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
|
|
||||||
private void OnMakeRootAgent(ScenePresence presence)
|
private void OnMakeRootAgent(ScenePresence presence)
|
||||||
{
|
{
|
||||||
|
|
||||||
IClientAPI client = presence.ControllingClient;
|
IClientAPI client = presence.ControllingClient;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!clients.Contains(client))
|
if (!clients.Contains(client))
|
||||||
{
|
{
|
||||||
client.OnLogout += OnClientLoggedOut;
|
client.OnLogout += OnClientLoggedOut;
|
||||||
client.OnConnectionClosed += OnClientLoggedOut;
|
client.OnConnectionClosed += OnClientLoggedOut;
|
||||||
clients.Add(client);
|
clients.Add(client);
|
||||||
if (enabled && (cs.irc.Enabled) && (cs.irc.Connected) && (cs.ClientReporting))
|
if (enabled && (cs.irc.Enabled) && (cs.irc.Connected) && (cs.ClientReporting))
|
||||||
|
@ -216,17 +237,18 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dialogModule != null && showAlert)
|
||||||
|
dialogModule.SendAlertToUser(client, alertMessage, true);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
m_log.ErrorFormat("[IRC-Region {0}]: MakeRootAgent exception: {1}", Region, ex.Message);
|
m_log.ErrorFormat("[IRC-Region {0}]: MakeRootAgent exception: {1}", Region, ex.Message);
|
||||||
m_log.Debug(ex);
|
m_log.Debug(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This handler detects chat events int he virtual world.
|
// This handler detects chat events int he virtual world.
|
||||||
|
|
||||||
public void OnSimChat(Object sender, OSChatMessage msg)
|
public void OnSimChat(Object sender, OSChatMessage msg)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -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
|
@ -56,6 +56,7 @@ public sealed class BSCharacter : BSPhysObject
|
||||||
private int _physicsActorType;
|
private int _physicsActorType;
|
||||||
private bool _isPhysical;
|
private bool _isPhysical;
|
||||||
private bool _flying;
|
private bool _flying;
|
||||||
|
private bool _wasWalking; // 'true' if the avatar was walking/moving last frame
|
||||||
private bool _setAlwaysRun;
|
private bool _setAlwaysRun;
|
||||||
private bool _throttleUpdates;
|
private bool _throttleUpdates;
|
||||||
private bool _floatOnWater;
|
private bool _floatOnWater;
|
||||||
|
@ -83,6 +84,7 @@ public sealed class BSCharacter : BSPhysObject
|
||||||
_position = pos;
|
_position = pos;
|
||||||
|
|
||||||
_flying = isFlying;
|
_flying = isFlying;
|
||||||
|
_wasWalking = true; // causes first step to initialize standing
|
||||||
_orientation = OMV.Quaternion.Identity;
|
_orientation = OMV.Quaternion.Identity;
|
||||||
_velocity = OMV.Vector3.Zero;
|
_velocity = OMV.Vector3.Zero;
|
||||||
_buoyancy = ComputeBuoyancyFromFlying(isFlying);
|
_buoyancy = ComputeBuoyancyFromFlying(isFlying);
|
||||||
|
@ -198,25 +200,68 @@ public sealed class BSCharacter : BSPhysObject
|
||||||
// TODO: Decide if the step parameters should be changed depending on the avatar's
|
// 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.
|
// 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.
|
||||||
|
|
||||||
// If falling, we keep the world's downward vector no matter what the other axis specify.
|
_velocityMotor.Step(timeStep);
|
||||||
if (!Flying && !IsColliding)
|
|
||||||
|
// If we're not supposed to be moving, make sure things are zero.
|
||||||
|
if (_velocityMotor.ErrorIsZero() && _velocityMotor.TargetValue == OMV.Vector3.Zero && IsColliding)
|
||||||
{
|
{
|
||||||
stepVelocity.Z = _velocity.Z;
|
// The avatar shouldn't be moving
|
||||||
// DetailLog("{0},BSCharacter.MoveMotor,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity);
|
_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;
|
||||||
|
|
||||||
// 'stepVelocity' is now the speed we'd like the avatar to move in. Turn that into an instantanous force.
|
if (_currentFriction != BSParam.AvatarFriction)
|
||||||
OMV.Vector3 moveForce = (stepVelocity - _velocity) * Mass;
|
{
|
||||||
|
// Probably starting up walking. Set friction to moving friction.
|
||||||
|
_currentFriction = BSParam.AvatarFriction;
|
||||||
|
PhysicsScene.PE.SetFriction(PhysBody, _currentFriction);
|
||||||
|
}
|
||||||
|
|
||||||
// Should we check for move force being small and forcing velocity to zero?
|
// If falling, we keep the world's downward vector no matter what the other axis specify.
|
||||||
|
if (!Flying && !IsColliding)
|
||||||
|
{
|
||||||
|
stepVelocity.Z = _velocity.Z;
|
||||||
|
// DetailLog("{0},BSCharacter.MoveMotor,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity);
|
||||||
|
}
|
||||||
|
|
||||||
// Add special movement force to allow avatars to walk up stepped surfaces.
|
// 'stepVelocity' is now the speed we'd like the avatar to move in. Turn that into an instantanous force.
|
||||||
moveForce += WalkUpStairs();
|
OMV.Vector3 moveForce = (stepVelocity - _velocity) * Mass;
|
||||||
|
|
||||||
// DetailLog("{0},BSCharacter.MoveMotor,move,stepVel={1},vel={2},mass={3},moveForce={4}", LocalID, stepVelocity, _velocity, Mass, moveForce);
|
// Should we check for move force being small and forcing velocity to zero?
|
||||||
PhysicsScene.PE.ApplyCentralImpulse(PhysBody, moveForce);
|
|
||||||
|
// 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);
|
||||||
|
PhysicsScene.PE.ApplyCentralImpulse(PhysBody, moveForce);
|
||||||
|
_wasWalking = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,27 +604,6 @@ public sealed class BSCharacter : BSPhysObject
|
||||||
PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity");
|
PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity");
|
||||||
|
|
||||||
_velocity = value;
|
_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.SetLinearVelocity(PhysBody, _velocity);
|
||||||
PhysicsScene.PE.Activate(PhysBody, true);
|
PhysicsScene.PE.Activate(PhysBody, true);
|
||||||
}
|
}
|
||||||
|
@ -854,7 +878,7 @@ public sealed class BSCharacter : BSPhysObject
|
||||||
_position = entprop.Position;
|
_position = entprop.Position;
|
||||||
_orientation = entprop.Rotation;
|
_orientation = entprop.Rotation;
|
||||||
|
|
||||||
// Smooth velocity. OpenSimulator is very sensitive to changes in velocity of the avatar
|
// 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
|
// 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
|
// 0.001m/s. Bullet introduces a lot of jitter in the velocity which causes many
|
||||||
// extra updates.
|
// extra updates.
|
||||||
|
@ -875,7 +899,7 @@ public sealed class BSCharacter : BSPhysObject
|
||||||
CurrentEntityProperties = entprop;
|
CurrentEntityProperties = entprop;
|
||||||
|
|
||||||
// Tell the linkset about value changes
|
// 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.
|
// Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
|
||||||
// base.RequestPhysicsterseUpdate();
|
// base.RequestPhysicsterseUpdate();
|
||||||
|
|
|
@ -231,6 +231,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
break;
|
break;
|
||||||
case Vehicle.ANGULAR_MOTOR_DIRECTION:
|
case Vehicle.ANGULAR_MOTOR_DIRECTION:
|
||||||
m_angularMotorDirection = new Vector3(pValue, pValue, pValue);
|
m_angularMotorDirection = new Vector3(pValue, pValue, pValue);
|
||||||
|
m_angularMotor.Zero();
|
||||||
m_angularMotor.SetTarget(m_angularMotorDirection);
|
m_angularMotor.SetTarget(m_angularMotorDirection);
|
||||||
break;
|
break;
|
||||||
case Vehicle.LINEAR_FRICTION_TIMESCALE:
|
case Vehicle.LINEAR_FRICTION_TIMESCALE:
|
||||||
|
@ -264,6 +265,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
pValue.Y = ClampInRange(-12.56f, pValue.Y, 12.56f);
|
pValue.Y = ClampInRange(-12.56f, pValue.Y, 12.56f);
|
||||||
pValue.Z = ClampInRange(-12.56f, pValue.Z, 12.56f);
|
pValue.Z = ClampInRange(-12.56f, pValue.Z, 12.56f);
|
||||||
m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
|
m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
|
||||||
|
m_angularMotor.Zero();
|
||||||
m_angularMotor.SetTarget(m_angularMotorDirection);
|
m_angularMotor.SetTarget(m_angularMotorDirection);
|
||||||
break;
|
break;
|
||||||
case Vehicle.LINEAR_FRICTION_TIMESCALE:
|
case Vehicle.LINEAR_FRICTION_TIMESCALE:
|
||||||
|
@ -720,10 +722,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
|
|
||||||
// Since the computation of terrain height can be a little involved, this routine
|
// 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.
|
// is used to fetch the height only once for each vehicle simulation step.
|
||||||
|
Vector3 lastRememberedHeightPos;
|
||||||
private float GetTerrainHeight(Vector3 pos)
|
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_knownTerrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos);
|
||||||
m_knownHas |= m_knownChangedTerrainHeight;
|
m_knownHas |= m_knownChangedTerrainHeight;
|
||||||
}
|
}
|
||||||
|
@ -943,10 +947,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
// Clamp high or low velocities
|
// Clamp high or low velocities
|
||||||
float newVelocityLengthSq = VehicleVelocity.LengthSquared();
|
float newVelocityLengthSq = VehicleVelocity.LengthSquared();
|
||||||
if (newVelocityLengthSq > 1000f)
|
if (newVelocityLengthSq > BSParam.VehicleMaxLinearVelocity)
|
||||||
{
|
{
|
||||||
VehicleVelocity /= VehicleVelocity.Length();
|
VehicleVelocity /= VehicleVelocity.Length();
|
||||||
VehicleVelocity *= 1000f;
|
VehicleVelocity *= BSParam.VehicleMaxLinearVelocity;
|
||||||
}
|
}
|
||||||
else if (newVelocityLengthSq < 0.001f)
|
else if (newVelocityLengthSq < 0.001f)
|
||||||
VehicleVelocity = Vector3.Zero;
|
VehicleVelocity = Vector3.Zero;
|
||||||
|
@ -957,39 +961,25 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
|
|
||||||
public void ComputeLinearVelocity(float pTimestep)
|
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 linearMotorCorrection = m_linearMotor.Step(pTimestep, currentVel);
|
||||||
|
|
||||||
// The movement computed in the linear motor is relative to the vehicle
|
// Motor is vehicle coordinates. Rotate it to world coordinates
|
||||||
// coordinates. Rotate the movement to world coordinates.
|
Vector3 linearMotorVelocity = linearMotorCorrection * VehicleOrientation;
|
||||||
Vector3 linearMotorVelocity = linearMotorStep * VehicleOrientation;
|
|
||||||
|
|
||||||
// If we're a ground vehicle, don't loose any Z action (like gravity acceleration).
|
// If we're a ground vehicle, don't add any upward Z movement
|
||||||
float mixFactor = 1f; // 1 means use all linear motor Z value, 0 means use all existing Z
|
|
||||||
if ((m_flags & VehicleFlag.LIMIT_MOTOR_UP) != 0)
|
if ((m_flags & VehicleFlag.LIMIT_MOTOR_UP) != 0)
|
||||||
{
|
{
|
||||||
if (!Prim.IsColliding)
|
if (linearMotorVelocity.Z > 0f)
|
||||||
{
|
linearMotorVelocity.Z = 0f;
|
||||||
// If a ground vehicle and not on the ground, I want gravity effect
|
|
||||||
mixFactor = 0.2f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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
|
// Add this correction to the velocity to make it faster/slower.
|
||||||
Vector3 linearMotorForce = linearMotorVelocity - VehicleVelocity;
|
VehicleVelocity += linearMotorVelocity;
|
||||||
|
|
||||||
// Act against the inertia of the vehicle
|
VDetailLog("{0}, MoveLinear,velocity,vehVel={1},correction={2},force={3}",
|
||||||
linearMotorForce *= m_vehicleMass;
|
Prim.LocalID, VehicleVelocity, linearMotorCorrection, linearMotorVelocity);
|
||||||
|
|
||||||
VehicleAddForceImpulse(linearMotorForce * pTimestep);
|
|
||||||
|
|
||||||
VDetailLog("{0}, MoveLinear,velocity,vehVel={1},step={2},stepVel={3},mix={4},force={5}",
|
|
||||||
Prim.LocalID, VehicleVelocity, linearMotorStep, linearMotorVelocity, mixFactor, linearMotorForce);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ComputeLinearTerrainHeightCorrection(float pTimestep)
|
public void ComputeLinearTerrainHeightCorrection(float pTimestep)
|
||||||
|
@ -1202,62 +1192,27 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
// set directly on the vehicle.
|
// set directly on the vehicle.
|
||||||
private void MoveAngular(float pTimestep)
|
private void MoveAngular(float pTimestep)
|
||||||
{
|
{
|
||||||
// The user wants this many radians per second angular change?
|
// VehicleRotationalVelocity = Vector3.Zero;
|
||||||
Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep);
|
|
||||||
|
ComputeAngularTurning(pTimestep);
|
||||||
|
|
||||||
|
ComputeAngularVerticalAttraction();
|
||||||
|
|
||||||
|
ComputeAngularDeflection();
|
||||||
|
|
||||||
|
ComputeAngularBanking();
|
||||||
|
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
// From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
|
if (VehicleRotationalVelocity.ApproxEquals(Vector3.Zero, 0.01f))
|
||||||
// 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
|
|
||||||
{
|
{
|
||||||
// The vehicle is not adding anything angular wise.
|
// The vehicle is not adding anything angular wise.
|
||||||
VehicleRotationalVelocity = Vector3.Zero;
|
VehicleRotationalVelocity = Vector3.Zero;
|
||||||
VDetailLog("{0}, MoveAngular,done,zero", Prim.LocalID);
|
VDetailLog("{0}, MoveAngular,done,zero", Prim.LocalID);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VDetailLog("{0}, MoveAngular,done,nonZero,angVel={1}", Prim.LocalID, VehicleRotationalVelocity);
|
||||||
|
}
|
||||||
|
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
//Offset section
|
//Offset section
|
||||||
|
@ -1291,6 +1246,31 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ComputeAngularTurning(float pTimestep)
|
||||||
|
{
|
||||||
|
// The user wants this many radians per second angular change?
|
||||||
|
Vector3 currentAngular = VehicleRotationalVelocity * Quaternion.Inverse(VehicleOrientation);
|
||||||
|
Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep, currentAngular);
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
VehicleRotationalVelocity += angularMotorContribution * VehicleOrientation;
|
||||||
|
VDetailLog("{0}, MoveAngular,angularTurning,angularMotorContrib={1}", Prim.LocalID, angularMotorContribution);
|
||||||
|
}
|
||||||
|
|
||||||
// From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
|
// 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
|
// 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
|
// enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to
|
||||||
|
@ -1299,13 +1279,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
// and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An
|
// 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 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.
|
// 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 vertical attaction timescale is reasonable
|
||||||
if (enableAngularVerticalAttraction && m_verticalAttractionTimescale < m_verticalAttractionCutoff)
|
if (enableAngularVerticalAttraction && m_verticalAttractionTimescale < m_verticalAttractionCutoff)
|
||||||
{
|
{
|
||||||
|
Vector3 vertContribution = Vector3.Zero;
|
||||||
|
|
||||||
// Take a vector pointing up and convert it from world to vehicle relative coords.
|
// Take a vector pointing up and convert it from world to vehicle relative coords.
|
||||||
Vector3 verticalError = Vector3.UnitZ * VehicleOrientation;
|
Vector3 verticalError = Vector3.UnitZ * VehicleOrientation;
|
||||||
|
|
||||||
|
@ -1319,37 +1299,36 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
|
|
||||||
// Y error means needed rotation around X axis and visa versa.
|
// 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.
|
// Since the error goes from zero to one, the asin is the corresponding angle.
|
||||||
ret.X = (float)Math.Asin(verticalError.Y);
|
vertContribution.X = (float)Math.Asin(verticalError.Y);
|
||||||
// (Tilt forward (positive X) needs to tilt back (rotate negative) around Y axis.)
|
// (Tilt forward (positive X) needs to tilt back (rotate negative) around Y axis.)
|
||||||
ret.Y = -(float)Math.Asin(verticalError.X);
|
vertContribution.Y = -(float)Math.Asin(verticalError.X);
|
||||||
|
|
||||||
// If verticalError.Z is negative, the vehicle is upside down. Add additional push.
|
// If verticalError.Z is negative, the vehicle is upside down. Add additional push.
|
||||||
if (verticalError.Z < 0f)
|
if (verticalError.Z < 0f)
|
||||||
{
|
{
|
||||||
ret.X += PIOverFour;
|
vertContribution.X += PIOverFour;
|
||||||
ret.Y += 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.
|
// Correction happens over a number of seconds.
|
||||||
Vector3 unscaledContrib = ret;
|
Vector3 unscaledContrib = vertContribution; // DEBUG DEBUG
|
||||||
ret /= m_verticalAttractionTimescale;
|
vertContribution /= m_verticalAttractionTimescale;
|
||||||
|
|
||||||
|
VehicleRotationalVelocity += vertContribution * VehicleOrientation;
|
||||||
|
|
||||||
VDetailLog("{0}, MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},eff={3},ts={4},vertAttr={5}",
|
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, vertContribution);
|
||||||
}
|
}
|
||||||
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 direction is should want to be pointing.
|
||||||
// The vehicle is moving in some direction and correct its orientation to it is pointing
|
// The vehicle is moving in some direction and correct its orientation to it is pointing
|
||||||
// in that direction.
|
// in that direction.
|
||||||
// TODO: implement reference frame.
|
// TODO: implement reference frame.
|
||||||
public Vector3 ComputeAngularDeflection()
|
public void ComputeAngularDeflection()
|
||||||
{
|
{
|
||||||
Vector3 ret = Vector3.Zero;
|
|
||||||
|
|
||||||
// Since angularMotorUp and angularDeflection are computed independently, they will calculate
|
// Since angularMotorUp and angularDeflection are computed independently, they will calculate
|
||||||
// approximately the same X or Y correction. When added together (when contributions are combined)
|
// 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.
|
// this creates an over-correction and then wabbling as the target is overshot.
|
||||||
|
@ -1357,6 +1336,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
|
|
||||||
if (enableAngularDeflection && m_angularDeflectionEfficiency != 0 && VehicleForwardSpeed > 0.2)
|
if (enableAngularDeflection && m_angularDeflectionEfficiency != 0 && VehicleForwardSpeed > 0.2)
|
||||||
{
|
{
|
||||||
|
Vector3 deflectContribution = Vector3.Zero;
|
||||||
|
|
||||||
// The direction the vehicle is moving
|
// The direction the vehicle is moving
|
||||||
Vector3 movingDirection = VehicleVelocity;
|
Vector3 movingDirection = VehicleVelocity;
|
||||||
movingDirection.Normalize();
|
movingDirection.Normalize();
|
||||||
|
@ -1382,18 +1363,19 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
// ret = m_angularDeflectionCorrectionMotor(1f, deflectionError);
|
// ret = m_angularDeflectionCorrectionMotor(1f, deflectionError);
|
||||||
|
|
||||||
// Scale the correction by recovery timescale and efficiency
|
// Scale the correction by recovery timescale and efficiency
|
||||||
ret = (-deflectionError) * m_angularDeflectionEfficiency;
|
deflectContribution = (-deflectionError) * m_angularDeflectionEfficiency;
|
||||||
ret /= m_angularDeflectionTimescale;
|
deflectContribution /= m_angularDeflectionTimescale;
|
||||||
|
|
||||||
|
VehicleRotationalVelocity += deflectContribution * VehicleOrientation;
|
||||||
|
|
||||||
VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}",
|
VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}",
|
||||||
Prim.LocalID, movingDirection, pointingDirection, deflectionError, ret);
|
Prim.LocalID, movingDirection, pointingDirection, deflectionError, deflectContribution);
|
||||||
VDetailLog("{0}, MoveAngular,Deflection,fwdSpd={1},defEff={2},defTS={3}",
|
VDetailLog("{0}, MoveAngular,Deflection,fwdSpd={1},defEff={2},defTS={3}",
|
||||||
Prim.LocalID, VehicleForwardSpeed, m_angularDeflectionEfficiency, m_angularDeflectionTimescale);
|
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.
|
// is tipped around the X axis.
|
||||||
// From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
|
// From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
|
||||||
// The vertical attractor feature must be enabled in order for the banking behavior to
|
// The vertical attractor feature must be enabled in order for the banking behavior to
|
||||||
|
@ -1424,12 +1406,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
// world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to
|
// 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
|
// 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.
|
// 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)
|
if (enableAngularBanking && m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff)
|
||||||
{
|
{
|
||||||
|
Vector3 bankingContribution = Vector3.Zero;
|
||||||
|
|
||||||
// Rotate a UnitZ vector (pointing up) to how the vehicle is oriented.
|
// 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
|
// 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)
|
// zero (straight up) to 1 or -1 (full tilt right or left)
|
||||||
|
@ -1446,15 +1428,16 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
mixedYawAngle = ClampInRange(-20f, mixedYawAngle, 20f);
|
mixedYawAngle = ClampInRange(-20f, mixedYawAngle, 20f);
|
||||||
|
|
||||||
// Build the force vector to change rotation from what it is to what it should be
|
// Build the force vector to change rotation from what it is to what it should be
|
||||||
ret.Z = -mixedYawAngle;
|
bankingContribution.Z = -mixedYawAngle;
|
||||||
|
|
||||||
// Don't do it all at once.
|
// Don't do it all at once.
|
||||||
ret /= m_bankingTimescale;
|
bankingContribution /= m_bankingTimescale;
|
||||||
|
|
||||||
|
VehicleRotationalVelocity += bankingContribution * VehicleOrientation;
|
||||||
|
|
||||||
VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},rollComp={3},yAng={4},mYAng={5},ret={6}",
|
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, bankingContribution);
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is from previous instantiations of XXXDynamics.cs.
|
// This is from previous instantiations of XXXDynamics.cs.
|
||||||
|
|
|
@ -252,7 +252,7 @@ public abstract class BSLinkset
|
||||||
// of the linkset is received.
|
// of the linkset is received.
|
||||||
// Passed flag is update came from physics engine (true) or the user (false).
|
// Passed flag is update came from physics engine (true) or the user (false).
|
||||||
// Called at taint-time!!
|
// 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
|
// Routine used when rebuilding the body of the root of the linkset
|
||||||
// Destroy all the constraints have have been made to root.
|
// Destroy all the constraints have have been made to root.
|
||||||
|
|
|
@ -51,6 +51,21 @@ sealed class BSLinksetCompoundInfo : BSLinksetInfo
|
||||||
OffsetFromCenterOfMass = p;
|
OffsetFromCenterOfMass = p;
|
||||||
OffsetRot = r;
|
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()
|
public override void Clear()
|
||||||
{
|
{
|
||||||
Index = 0;
|
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.
|
// 'physicalUpdate' is true if these changes came directly from the physics engine. Don't need to rebuild then.
|
||||||
// Called at taint-time.
|
// 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
|
// 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
|
// 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.
|
// 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
|
// What would cause an unnecessary rebuild so we make sure the linkset is in a
|
||||||
// region before bothering to do a rebuild.
|
// region before bothering to do a rebuild.
|
||||||
if (!IsRoot(updated)
|
if (!IsRoot(updated) && PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
|
||||||
&& !physicalUpdate
|
|
||||||
&& PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
|
|
||||||
{
|
{
|
||||||
// TODO: replace this with are calculation of the child prim's orientation and pos.
|
// If a child of the linkset is updating only the position or rotation, that can be done
|
||||||
// TODO: for the moment, don't rebuild the compound shape.
|
// without rebuilding the linkset.
|
||||||
// This is often just the car turning its wheels. When we can just reorient the one
|
// If a handle for the child can be fetch, we update the child here. If a rebuild was
|
||||||
// member shape of the compound shape, the overhead of rebuilding won't be a problem.
|
// scheduled by someone else, the rebuild will just replace this setting.
|
||||||
// updated.LinksetInfo = null;
|
|
||||||
// ScheduleRebuild(updated);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,15 +434,7 @@ public sealed class BSLinksetCompound : BSLinkset
|
||||||
BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo;
|
BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo;
|
||||||
if (lci == null)
|
if (lci == null)
|
||||||
{
|
{
|
||||||
// Each child position and rotation is given relative to the center-of-mass.
|
lci = new BSLinksetCompoundInfo(memberIndex, LinksetRoot, cPrim, centerDisplacement);
|
||||||
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;
|
|
||||||
cPrim.LinksetInfo = lci;
|
cPrim.LinksetInfo = lci;
|
||||||
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci);
|
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci);
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ public sealed class BSLinksetConstraints : BSLinkset
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called at taint-time!!
|
// 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
|
// Nothing to do for constraints on property updates
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,8 @@ public class BSVMotor : BSMotor
|
||||||
CurrentValue = TargetValue = Vector3.Zero;
|
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)
|
public virtual Vector3 Step(float timeStep)
|
||||||
{
|
{
|
||||||
if (!Enabled) return TargetValue;
|
if (!Enabled) return TargetValue;
|
||||||
|
@ -148,9 +149,10 @@ public class BSVMotor : BSMotor
|
||||||
|
|
||||||
Vector3 correction = Vector3.Zero;
|
Vector3 correction = Vector3.Zero;
|
||||||
Vector3 error = TargetValue - CurrentValue;
|
Vector3 error = TargetValue - CurrentValue;
|
||||||
|
LastError = error;
|
||||||
if (!ErrorIsZero(error))
|
if (!ErrorIsZero(error))
|
||||||
{
|
{
|
||||||
correction = Step(timeStep, error);
|
correction = StepError(timeStep, error);
|
||||||
|
|
||||||
CurrentValue += correction;
|
CurrentValue += correction;
|
||||||
|
|
||||||
|
@ -187,20 +189,31 @@ public class BSVMotor : BSMotor
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Difference between what we have and target is small. Motor is done.
|
// 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;
|
CurrentValue = TargetValue;
|
||||||
MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}",
|
MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},currTgt={4},currCurr={5}",
|
||||||
BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue);
|
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;
|
if (!Enabled) return Vector3.Zero;
|
||||||
|
|
||||||
LastError = error;
|
|
||||||
Vector3 returnCorrection = Vector3.Zero;
|
Vector3 returnCorrection = Vector3.Zero;
|
||||||
if (!ErrorIsZero())
|
if (!ErrorIsZero(error))
|
||||||
{
|
{
|
||||||
// correction = error / secondsItShouldTakeToCorrect
|
// correction = error / secondsItShouldTakeToCorrect
|
||||||
Vector3 correctionAmount;
|
Vector3 correctionAmount;
|
||||||
|
@ -302,9 +315,10 @@ public class BSFMotor : BSMotor
|
||||||
|
|
||||||
float correction = 0f;
|
float correction = 0f;
|
||||||
float error = TargetValue - CurrentValue;
|
float error = TargetValue - CurrentValue;
|
||||||
|
LastError = error;
|
||||||
if (!ErrorIsZero(error))
|
if (!ErrorIsZero(error))
|
||||||
{
|
{
|
||||||
correction = Step(timeStep, error);
|
correction = StepError(timeStep, error);
|
||||||
|
|
||||||
CurrentValue += correction;
|
CurrentValue += correction;
|
||||||
|
|
||||||
|
@ -339,6 +353,12 @@ public class BSFMotor : BSMotor
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Difference between what we have and target is small. Motor is done.
|
// 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;
|
CurrentValue = TargetValue;
|
||||||
MDetailLog("{0}, BSFMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}",
|
MDetailLog("{0}, BSFMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}",
|
||||||
BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue);
|
BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue);
|
||||||
|
@ -347,13 +367,12 @@ public class BSFMotor : BSMotor
|
||||||
return CurrentValue;
|
return CurrentValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual float Step(float timeStep, float error)
|
public virtual float StepError(float timeStep, float error)
|
||||||
{
|
{
|
||||||
if (!Enabled) return 0f;
|
if (!Enabled) return 0f;
|
||||||
|
|
||||||
LastError = error;
|
|
||||||
float returnCorrection = 0f;
|
float returnCorrection = 0f;
|
||||||
if (!ErrorIsZero())
|
if (!ErrorIsZero(error))
|
||||||
{
|
{
|
||||||
// correction = error / secondsItShouldTakeToCorrect
|
// correction = error / secondsItShouldTakeToCorrect
|
||||||
float correctionAmount;
|
float correctionAmount;
|
||||||
|
@ -440,8 +459,8 @@ public class BSPIDVMotor : BSVMotor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore Current and Target Values and just advance the PID computation on this error.
|
// Advance the PID computation on this error.
|
||||||
public override Vector3 Step(float timeStep, Vector3 error)
|
public override Vector3 StepError(float timeStep, Vector3 error)
|
||||||
{
|
{
|
||||||
if (!Enabled) return Vector3.Zero;
|
if (!Enabled) return Vector3.Zero;
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,9 @@ public static class BSParam
|
||||||
|
|
||||||
public static float MinimumObjectMass { get; private set; }
|
public static float MinimumObjectMass { get; private set; }
|
||||||
public static float MaximumObjectMass { 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 LinearDamping { get; private set; }
|
||||||
public static float AngularDamping { 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 AvatarStepApproachFactor { get; private set; }
|
||||||
public static float AvatarStepForceFactor { 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 VehicleAngularDamping { get; private set; }
|
||||||
public static float VehicleDebuggingEnabled { 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 MaxDensity = 22587f;
|
||||||
public const float MinRestitution = 0f;
|
public const float MinRestitution = 0f;
|
||||||
public const float MaxRestitution = 1f;
|
public const float MaxRestitution = 1f;
|
||||||
public const float MaxAddForceMagnitude = 20f;
|
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
public delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
|
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,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); },
|
||||||
(s) => { return (float)s.m_maxUpdatesPerFrame; },
|
(s) => { return (float)s.m_maxUpdatesPerFrame; },
|
||||||
(s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
|
(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)",
|
new ParameterDefn("MinObjectMass", "Minimum object mass (0.0001)",
|
||||||
0.0001f,
|
0.0001f,
|
||||||
(s,cf,p,v) => { MinimumObjectMass = cf.GetFloat(p, v); },
|
(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,cf,p,v) => { MaximumObjectMass = cf.GetFloat(p, v); },
|
||||||
(s) => { return (float)MaximumObjectMass; },
|
(s) => { return (float)MaximumObjectMass; },
|
||||||
(s,p,l,v) => { MaximumObjectMass = v; } ),
|
(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",
|
new ParameterDefn("PID_D", "Derivitive factor for motion smoothing",
|
||||||
2200f,
|
2200f,
|
||||||
|
@ -423,8 +439,18 @@ public static class BSParam
|
||||||
(s) => { return AvatarStepForceFactor; },
|
(s) => { return AvatarStepForceFactor; },
|
||||||
(s,p,l,v) => { AvatarStepForceFactor = v; } ),
|
(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)",
|
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,cf,p,v) => { VehicleAngularDamping = cf.GetFloat(p, v); },
|
||||||
(s) => { return VehicleAngularDamping; },
|
(s) => { return VehicleAngularDamping; },
|
||||||
(s,p,l,v) => { VehicleAngularDamping = v; } ),
|
(s,p,l,v) => { VehicleAngularDamping = v; } ),
|
||||||
|
|
|
@ -55,6 +55,16 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
||||||
* BS.ApplyCentralForce BS.ApplyTorque
|
* 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
|
public abstract class BSPhysObject : PhysicsActor
|
||||||
{
|
{
|
||||||
protected BSPhysObject()
|
protected BSPhysObject()
|
||||||
|
|
|
@ -311,13 +311,14 @@ public sealed class BSPrim : BSPhysObject
|
||||||
_position = value;
|
_position = value;
|
||||||
PositionSanityCheck(false);
|
PositionSanityCheck(false);
|
||||||
|
|
||||||
// A linkset might need to know if a component information changed.
|
|
||||||
Linkset.UpdateProperties(this, false);
|
|
||||||
|
|
||||||
PhysicsScene.TaintedObject("BSPrim.setPosition", delegate()
|
PhysicsScene.TaintedObject("BSPrim.setPosition", delegate()
|
||||||
{
|
{
|
||||||
DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
|
DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
|
||||||
ForcePosition = _position;
|
ForcePosition = _position;
|
||||||
|
|
||||||
|
// A linkset might need to know if a component information changed.
|
||||||
|
Linkset.UpdateProperties(UpdatedProperties.Position, this);
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -682,12 +683,13 @@ public sealed class BSPrim : BSPhysObject
|
||||||
return;
|
return;
|
||||||
_orientation = value;
|
_orientation = value;
|
||||||
|
|
||||||
// A linkset might need to know if a component information changed.
|
|
||||||
Linkset.UpdateProperties(this, false);
|
|
||||||
|
|
||||||
PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate()
|
PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate()
|
||||||
{
|
{
|
||||||
ForceOrientation = _orientation;
|
ForceOrientation = _orientation;
|
||||||
|
|
||||||
|
// A linkset might need to know if a component information changed.
|
||||||
|
Linkset.UpdateProperties(UpdatedProperties.Orientation, this);
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -989,10 +991,10 @@ public sealed class BSPrim : BSPhysObject
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
_rotationalVelocity = value;
|
_rotationalVelocity = value;
|
||||||
|
Util.ClampV(_rotationalVelocity, BSParam.MaxAngularVelocity);
|
||||||
// m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity);
|
// m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity);
|
||||||
PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
|
PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
|
||||||
{
|
{
|
||||||
DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
|
|
||||||
ForceRotationalVelocity = _rotationalVelocity;
|
ForceRotationalVelocity = _rotationalVelocity;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1005,7 +1007,9 @@ public sealed class BSPrim : BSPhysObject
|
||||||
_rotationalVelocity = value;
|
_rotationalVelocity = value;
|
||||||
if (PhysBody.HasPhysicalBody)
|
if (PhysBody.HasPhysicalBody)
|
||||||
{
|
{
|
||||||
|
DetailLog("{0},BSPrim.ForceRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
|
||||||
PhysicsScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity);
|
PhysicsScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity);
|
||||||
|
// PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity);
|
||||||
ActivateIfPhysical(false);
|
ActivateIfPhysical(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1082,7 +1086,7 @@ public sealed class BSPrim : BSPhysObject
|
||||||
OMV.Vector3 origPosition = RawPosition; // DEBUG DEBUG (for printout below)
|
OMV.Vector3 origPosition = RawPosition; // DEBUG DEBUG (for printout below)
|
||||||
|
|
||||||
// 'movePosition' is where we'd like the prim to be at this moment.
|
// '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 we are very close to our target, turn off the movement motor.
|
||||||
if (_targetMotor.ErrorIsZero())
|
if (_targetMotor.ErrorIsZero())
|
||||||
|
@ -1193,10 +1197,14 @@ public sealed class BSPrim : BSPhysObject
|
||||||
public override float APIDDamping { set { return; } }
|
public override float APIDDamping { set { return; } }
|
||||||
|
|
||||||
public override void AddForce(OMV.Vector3 force, bool pushforce) {
|
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.
|
// Since this force is being applied in only one step, make this a force per second.
|
||||||
OMV.Vector3 addForce = force / PhysicsScene.LastTimeStep;
|
addForce /= PhysicsScene.LastTimeStep;
|
||||||
AddForce(addForce, pushforce, false);
|
AddForce(addForce, pushforce, false /* inTaintTime */);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Applying a force just adds this to the total force on the object.
|
// Applying a force just adds this to the total force on the object.
|
||||||
// This added force will only last the next simulation tick.
|
// This added force will only last the next simulation tick.
|
||||||
public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
|
public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
|
||||||
|
@ -1205,9 +1213,9 @@ public sealed class BSPrim : BSPhysObject
|
||||||
{
|
{
|
||||||
if (force.IsFinite())
|
if (force.IsFinite())
|
||||||
{
|
{
|
||||||
OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude);
|
|
||||||
// DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce);
|
// DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce);
|
||||||
|
|
||||||
|
OMV.Vector3 addForce = force;
|
||||||
PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate()
|
PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate()
|
||||||
{
|
{
|
||||||
// Bullet adds this central force to the total force for this tick
|
// Bullet adds this central force to the total force for this tick
|
||||||
|
@ -1642,7 +1650,7 @@ public sealed class BSPrim : BSPhysObject
|
||||||
// TODO: handle physics introduced by Bullet with computed vehicle physics.
|
// TODO: handle physics introduced by Bullet with computed vehicle physics.
|
||||||
if (_vehicle.IsActive)
|
if (_vehicle.IsActive)
|
||||||
{
|
{
|
||||||
entprop.RotationalVelocity = OMV.Vector3.Zero;
|
// entprop.RotationalVelocity = OMV.Vector3.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign directly to the local variables so the normal set actions do not happen
|
// Assign directly to the local variables so the normal set actions do not happen
|
||||||
|
@ -1681,7 +1689,7 @@ public sealed class BSPrim : BSPhysObject
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// The linkset implimentation might want to know about this.
|
// 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 long m_simulationStep = 0;
|
||||||
internal float NominalFrameRate { get; set; }
|
internal float NominalFrameRate { get; set; }
|
||||||
public long SimulationStep { get { return m_simulationStep; } }
|
public long SimulationStep { get { return m_simulationStep; } }
|
||||||
internal int m_taintsToProcessPerStep;
|
|
||||||
internal float LastTimeStep { get; private set; }
|
internal float LastTimeStep { get; private set; }
|
||||||
|
|
||||||
// Physical objects can register for prestep or poststep events
|
// Physical objects can register for prestep or poststep events
|
||||||
|
@ -847,8 +846,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
|
|
||||||
#endregion // Taints
|
#endregion // Taints
|
||||||
|
|
||||||
#region INI and command line parameter processing
|
|
||||||
|
|
||||||
#region IPhysicsParameters
|
#region IPhysicsParameters
|
||||||
// Get the list of parameters this physics engine supports
|
// Get the list of parameters this physics engine supports
|
||||||
public PhysParameterEntry[] GetParameterList()
|
public PhysParameterEntry[] GetParameterList()
|
||||||
|
@ -945,8 +942,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
|
|
||||||
#endregion IPhysicsParameters
|
#endregion IPhysicsParameters
|
||||||
|
|
||||||
#endregion Runtime settable parameters
|
|
||||||
|
|
||||||
// Invoke the detailed logger and output something if it's enabled.
|
// Invoke the detailed logger and output something if it's enabled.
|
||||||
public void DetailLog(string msg, params Object[] args)
|
public void DetailLog(string msg, params Object[] args)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,23 +1,20 @@
|
||||||
CURRENT PRIORITIES
|
CURRENT PRIORITIES
|
||||||
=================================================
|
=================================================
|
||||||
Mantis 6040 script http://opensimulator.org/mantis/view.php?id=6040
|
Child movement in linkset (don't rebuild linkset)
|
||||||
Msg Kayaker on OSGrid when working
|
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
|
Teravus llMoveToTarget script debug
|
||||||
Mixing of hover, buoyancy/gravity, moveToTarget, into one force
|
Mixing of hover, buoyancy/gravity, moveToTarget, into one force
|
||||||
Boats floating at proper level
|
|
||||||
Nebadon vehicles turning funny in arena
|
Nebadon vehicles turning funny in arena
|
||||||
limitMotorUp calibration (more down?)
|
limitMotorUp calibration (more down?)
|
||||||
llRotLookAt
|
llRotLookAt
|
||||||
llLookAt
|
llLookAt
|
||||||
Vehicle angular vertical attraction
|
|
||||||
Vehicle angular deflection
|
|
||||||
Preferred orientation angular correction fix
|
|
||||||
vehicle angular banking
|
|
||||||
Avatars walking up stairs (HALF DONE)
|
Avatars walking up stairs (HALF DONE)
|
||||||
Radius of the capsule affects ability to climb edges.
|
|
||||||
Vehicle movement on terrain smoothness
|
|
||||||
When is force introduced by SetForce removed? The prestep action could go forever.
|
|
||||||
Boats float low in the water (DONE)
|
|
||||||
Avatar movement
|
Avatar movement
|
||||||
flying into a wall doesn't stop avatar who keeps appearing to move through the obstacle (DONE)
|
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)
|
walking up stairs is not calibrated correctly (stairs out of Kepler cabin)
|
||||||
|
@ -73,8 +70,12 @@ Incorporate inter-relationship of angular corrections. For instance, angularDefl
|
||||||
|
|
||||||
GENERAL TODO LIST:
|
GENERAL TODO LIST:
|
||||||
=================================================
|
=================================================
|
||||||
|
llMoveToTarget objects are not effected by gravity until target is removed.
|
||||||
Implement llSetPhysicalMaterial.
|
Implement llSetPhysicalMaterial.
|
||||||
|
extend it with Center-of-mass, rolling friction, density
|
||||||
Implement llSetForceAndTorque.
|
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.
|
Implement an avatar mesh shape. The Bullet capsule is way too limited.
|
||||||
Consider just hand creating a vertex/index array in a new BSShapeAvatar.
|
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.
|
Verify/fix phantom, volume-detect objects do not fall to infinity. Should stop at terrain.
|
||||||
|
@ -311,3 +312,11 @@ Remove HeightmapInfo from terrain specification (DONE)
|
||||||
Surfboard go wonky when turning (DONE)
|
Surfboard go wonky when turning (DONE)
|
||||||
Angular motor direction is global coordinates rather than local coordinates?
|
Angular motor direction is global coordinates rather than local coordinates?
|
||||||
(Resolution: made angular motor direction correct coordinate system)
|
(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)
|
|
@ -1574,7 +1574,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine
|
||||||
m_MaxScriptQueue = maxScriptQueue;
|
m_MaxScriptQueue = maxScriptQueue;
|
||||||
|
|
||||||
STPStartInfo startInfo = new STPStartInfo();
|
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.MaxWorkerThreads = maxThreads;
|
||||||
startInfo.MinWorkerThreads = minThreads;
|
startInfo.MinWorkerThreads = minThreads;
|
||||||
startInfo.ThreadPriority = threadPriority;;
|
startInfo.ThreadPriority = threadPriority;;
|
||||||
|
@ -1582,7 +1583,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
|
||||||
startInfo.StartSuspended = true;
|
startInfo.StartSuspended = true;
|
||||||
|
|
||||||
m_ThreadPool = new SmartThreadPool(startInfo);
|
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 ==
|
== 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
|
for it to be accessible to Linux users and to the continuous
|
||||||
integration system.
|
integration system.
|
||||||
|
|
||||||
==TESTING ON WINDOWS==
|
== TESTING ON WINDOWS ==
|
||||||
|
|
||||||
To use nunit testing on opensim code, you have a variety of methods. The
|
To use nunit testing on opensim code, you have a variety of methods. The
|
||||||
easiast methods involve using IDE capabilities to test code. Using
|
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
|
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.
|
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
|
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.
|
.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"
|
On these operating systems you will have to use the command "nunit-console2"
|
||||||
|
|
||||||
Example
|
=== Example ===
|
||||||
|
|
||||||
nunit-console2 OpenSim.Framework.Tests.dll (on linux)
|
nunit-console2 OpenSim.Framework.Tests.dll (on linux)
|
||||||
nunit-console OpenSim.Framework.Tests.dll (on windows)
|
nunit-console OpenSim.Framework.Tests.dll (on windows)
|
||||||
|
|
|
@ -32,6 +32,11 @@ namespace Amib.Threading
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private ThreadPriority _threadPriority;
|
private ThreadPriority _threadPriority;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The thread pool name. Threads will get names depending on this.
|
||||||
|
/// </summary>
|
||||||
|
private string _threadPoolName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If this field is not null then the performance counters are enabled
|
/// If this field is not null then the performance counters are enabled
|
||||||
/// and use the string as the name of the instance.
|
/// and use the string as the name of the instance.
|
||||||
|
@ -46,6 +51,7 @@ namespace Amib.Threading
|
||||||
_minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
|
_minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
|
||||||
_maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
|
_maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
|
||||||
_threadPriority = SmartThreadPool.DefaultThreadPriority;
|
_threadPriority = SmartThreadPool.DefaultThreadPriority;
|
||||||
|
_threadPoolName = SmartThreadPool.DefaultThreadPoolName;
|
||||||
_pcInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
|
_pcInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
|
||||||
_stackSize = SmartThreadPool.DefaultStackSize;
|
_stackSize = SmartThreadPool.DefaultStackSize;
|
||||||
}
|
}
|
||||||
|
@ -56,6 +62,7 @@ namespace Amib.Threading
|
||||||
_minWorkerThreads = stpStartInfo._minWorkerThreads;
|
_minWorkerThreads = stpStartInfo._minWorkerThreads;
|
||||||
_maxWorkerThreads = stpStartInfo._maxWorkerThreads;
|
_maxWorkerThreads = stpStartInfo._maxWorkerThreads;
|
||||||
_threadPriority = stpStartInfo._threadPriority;
|
_threadPriority = stpStartInfo._threadPriority;
|
||||||
|
_threadPoolName = stpStartInfo._threadPoolName;
|
||||||
_pcInstanceName = stpStartInfo._pcInstanceName;
|
_pcInstanceName = stpStartInfo._pcInstanceName;
|
||||||
_stackSize = stpStartInfo._stackSize;
|
_stackSize = stpStartInfo._stackSize;
|
||||||
}
|
}
|
||||||
|
@ -84,6 +91,13 @@ namespace Amib.Threading
|
||||||
set { _threadPriority = value; }
|
set { _threadPriority = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual string ThreadPoolName
|
||||||
|
{
|
||||||
|
get { return _threadPoolName; }
|
||||||
|
set { _threadPoolName = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public string PerformanceCounterInstanceName
|
public string PerformanceCounterInstanceName
|
||||||
{
|
{
|
||||||
get { return _pcInstanceName; }
|
get { return _pcInstanceName; }
|
||||||
|
|
|
@ -135,6 +135,11 @@ namespace Amib.Threading
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal;
|
public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The default thread pool name
|
||||||
|
/// </summary>
|
||||||
|
public const string DefaultThreadPoolName = "SmartThreadPool";
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Member Variables
|
#region Member Variables
|
||||||
|
@ -143,7 +148,7 @@ namespace Amib.Threading
|
||||||
/// Contains the name of this instance of SmartThreadPool.
|
/// Contains the name of this instance of SmartThreadPool.
|
||||||
/// Can be changed by the user.
|
/// Can be changed by the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string _name = "SmartThreadPool";
|
private string _name = DefaultThreadPoolName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Hashtable of all the threads in the thread pool.
|
/// Hashtable of all the threads in the thread pool.
|
||||||
|
@ -307,6 +312,7 @@ namespace Amib.Threading
|
||||||
|
|
||||||
private void Initialize()
|
private void Initialize()
|
||||||
{
|
{
|
||||||
|
Name = _stpStartInfo.ThreadPoolName;
|
||||||
ValidateSTPStartInfo();
|
ValidateSTPStartInfo();
|
||||||
|
|
||||||
if (null != _stpStartInfo.PerformanceCounterInstanceName)
|
if (null != _stpStartInfo.PerformanceCounterInstanceName)
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -904,6 +904,19 @@
|
||||||
|
|
||||||
[BulletSim]
|
[BulletSim]
|
||||||
; World parameters
|
; 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
|
DefaultFriction = 0.20
|
||||||
DefaultDensity = 10.000006836
|
DefaultDensity = 10.000006836
|
||||||
DefaultRestitution = 0.0
|
DefaultRestitution = 0.0
|
||||||
|
@ -956,6 +969,7 @@
|
||||||
PhysicsLoggingDir = "."
|
PhysicsLoggingDir = "."
|
||||||
VehicleLoggingEnabled = False
|
VehicleLoggingEnabled = False
|
||||||
|
|
||||||
|
|
||||||
[RemoteAdmin]
|
[RemoteAdmin]
|
||||||
enabled = false
|
enabled = false
|
||||||
|
|
||||||
|
@ -1105,6 +1119,17 @@
|
||||||
|
|
||||||
;exclude_list=User 1,User 2,User 3
|
;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
|
; The following settings control the progression of daytime
|
||||||
; in the Sim. The defaults are the same as the commented out settings
|
; in the Sim. The defaults are the same as the commented out settings
|
||||||
|
|
36
prebuild.xml
36
prebuild.xml
|
@ -1850,45 +1850,13 @@
|
||||||
<Configuration name="Debug">
|
<Configuration name="Debug">
|
||||||
<Options>
|
<Options>
|
||||||
<OutputPath>../../../../bin/Physics/</OutputPath>
|
<OutputPath>../../../../bin/Physics/</OutputPath>
|
||||||
|
<AllowUnsafe>true</AllowUnsafe>
|
||||||
</Options>
|
</Options>
|
||||||
</Configuration>
|
</Configuration>
|
||||||
<Configuration name="Release">
|
<Configuration name="Release">
|
||||||
<Options>
|
<Options>
|
||||||
<OutputPath>../../../../bin/Physics/</OutputPath>
|
<OutputPath>../../../../bin/Physics/</OutputPath>
|
||||||
</Options>
|
<AllowUnsafe>true</AllowUnsafe>
|
||||||
</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>
|
|
||||||
</Options>
|
</Options>
|
||||||
</Configuration>
|
</Configuration>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue