diff --git a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs
index 04cc33aa51..b497fdef46 100644
--- a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs
+++ b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs
@@ -246,6 +246,11 @@ namespace OpenSim.Capabilities.Handlers
}
else
{
+ // Handle the case where no second range value was given. This is equivalent to requesting
+ // the rest of the entity.
+ if (end == -1)
+ end = int.MaxValue;
+
end = Utils.Clamp(end, 0, texture.Data.Length - 1);
start = Utils.Clamp(start, 0, end);
int len = end - start + 1;
@@ -299,15 +304,43 @@ namespace OpenSim.Capabilities.Handlers
// texture.FullID, range, response.StatusCode, response.ContentLength, texture.Data.Length);
}
+ ///
+ /// Parse a range header.
+ ///
+ ///
+ /// As per http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html,
+ /// this obeys range headers with two values (e.g. 533-4165) and no second value (e.g. 533-).
+ /// Where there is no value, -1 is returned.
+ /// FIXME: Need to cover the case where only a second value is specified (e.g. -4165), probably by returning -1
+ /// for start.
+ ///
+ ///
+ /// Start of the range. Undefined if this was not a number.
+ /// End of the range. Will be -1 if no end specified. Undefined if there was a raw string but this was not a number.
private bool TryParseRange(string header, out int start, out int end)
{
+ start = end = 0;
+
if (header.StartsWith("bytes="))
{
string[] rangeValues = header.Substring(6).Split('-');
+
if (rangeValues.Length == 2)
{
- if (Int32.TryParse(rangeValues[0], out start) && Int32.TryParse(rangeValues[1], out end))
+ if (!Int32.TryParse(rangeValues[0], out start))
+ return false;
+
+ string rawEnd = rangeValues[1];
+
+ if (rawEnd == "")
+ {
+ end = -1;
return true;
+ }
+ else if (Int32.TryParse(rawEnd, out end))
+ {
+ return true;
+ }
}
}
diff --git a/OpenSim/Data/Tests/BasicDataServiceTest.cs b/OpenSim/Data/Tests/BasicDataServiceTest.cs
index d8019ba9b4..69b79bf6e0 100644
--- a/OpenSim/Data/Tests/BasicDataServiceTest.cs
+++ b/OpenSim/Data/Tests/BasicDataServiceTest.cs
@@ -44,9 +44,15 @@ namespace OpenSim.Data.Tests
/// This is a base class for testing any Data service for any DBMS.
/// Requires NUnit 2.5 or better (to support the generics).
///
+ ///
+ /// FIXME: Should extend OpenSimTestCase but compile on mono 2.4.3 currently fails with
+ /// AssetTests`2 : System.MemberAccessException : Cannot create an instance of OpenSim.Data.Tests.AssetTests`2[TConn,TAssetData] because Type.ContainsGenericParameters is true.
+ /// and similar on EstateTests, InventoryTests and RegionTests.
+ /// Runs fine with mono 2.10.8.1, so easiest thing is to wait until min Mono version uplifts.
+ ///
///
///
- public class BasicDataServiceTest : OpenSimTestCase
+ public class BasicDataServiceTest
where TConn : DbConnection, new()
where TService : class, new()
{
diff --git a/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs b/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs
new file mode 100644
index 0000000000..9056548a51
--- /dev/null
+++ b/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs
@@ -0,0 +1,508 @@
+/*
+ * Copyright (c) 2008, openmetaverse.org, http://opensimulator.org/
+ * All rights reserved.
+ *
+ * - 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.
+ * - Neither the name of the openmetaverse.org 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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.Threading;
+using System.Collections.Generic;
+
+namespace OpenSim.Framework
+{
+ ///
+ /// A double dictionary that is thread abort safe.
+ ///
+ ///
+ /// This adapts OpenMetaverse.DoubleDictionary to be thread-abort safe by acquiring ReaderWriterLockSlim within
+ /// a finally section (which can't be interrupted by Thread.Abort()).
+ ///
+ public class DoubleDictionaryThreadAbortSafe
+ {
+ Dictionary Dictionary1;
+ Dictionary Dictionary2;
+ ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
+
+ public DoubleDictionaryThreadAbortSafe()
+ {
+ Dictionary1 = new Dictionary();
+ Dictionary2 = new Dictionary();
+ }
+
+ public DoubleDictionaryThreadAbortSafe(int capacity)
+ {
+ Dictionary1 = new Dictionary(capacity);
+ Dictionary2 = new Dictionary(capacity);
+ }
+
+ public void Add(TKey1 key1, TKey2 key2, TValue value)
+ {
+ bool gotLock = false;
+
+ try
+ {
+ // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
+ // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
+ // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
+ try {}
+ finally
+ {
+ rwLock.EnterWriteLock();
+ gotLock = true;
+ }
+
+ if (Dictionary1.ContainsKey(key1))
+ {
+ if (!Dictionary2.ContainsKey(key2))
+ throw new ArgumentException("key1 exists in the dictionary but not key2");
+ }
+ else if (Dictionary2.ContainsKey(key2))
+ {
+ if (!Dictionary1.ContainsKey(key1))
+ throw new ArgumentException("key2 exists in the dictionary but not key1");
+ }
+
+ Dictionary1[key1] = value;
+ Dictionary2[key2] = value;
+ }
+ finally
+ {
+ if (gotLock)
+ rwLock.ExitWriteLock();
+ }
+ }
+
+ public bool Remove(TKey1 key1, TKey2 key2)
+ {
+ bool success;
+ bool gotLock = false;
+
+ try
+ {
+ // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
+ // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
+ // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
+ try {}
+ finally
+ {
+ rwLock.EnterWriteLock();
+ gotLock = true;
+ }
+
+ Dictionary1.Remove(key1);
+ success = Dictionary2.Remove(key2);
+ }
+ finally
+ {
+ if (gotLock)
+ rwLock.ExitWriteLock();
+ }
+
+ return success;
+ }
+
+ public bool Remove(TKey1 key1)
+ {
+ bool found = false;
+ bool gotLock = false;
+
+ try
+ {
+ // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
+ // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
+ // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
+ try {}
+ finally
+ {
+ rwLock.EnterWriteLock();
+ gotLock = true;
+ }
+
+ // This is an O(n) operation!
+ TValue value;
+ if (Dictionary1.TryGetValue(key1, out value))
+ {
+ foreach (KeyValuePair kvp in Dictionary2)
+ {
+ if (kvp.Value.Equals(value))
+ {
+ Dictionary1.Remove(key1);
+ Dictionary2.Remove(kvp.Key);
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ finally
+ {
+ if (gotLock)
+ rwLock.ExitWriteLock();
+ }
+
+ return found;
+ }
+
+ public bool Remove(TKey2 key2)
+ {
+ bool found = false;
+ bool gotLock = false;
+
+ try
+ {
+ // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
+ // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
+ // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
+ try {}
+ finally
+ {
+ rwLock.EnterWriteLock();
+ gotLock = true;
+ }
+
+ // This is an O(n) operation!
+ TValue value;
+ if (Dictionary2.TryGetValue(key2, out value))
+ {
+ foreach (KeyValuePair kvp in Dictionary1)
+ {
+ if (kvp.Value.Equals(value))
+ {
+ Dictionary2.Remove(key2);
+ Dictionary1.Remove(kvp.Key);
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ finally
+ {
+ if (gotLock)
+ rwLock.ExitWriteLock();
+ }
+
+ return found;
+ }
+
+ public void Clear()
+ {
+ bool gotLock = false;
+
+ try
+ {
+ // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
+ // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
+ // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
+ try {}
+ finally
+ {
+ rwLock.EnterWriteLock();
+ gotLock = true;
+ }
+
+ Dictionary1.Clear();
+ Dictionary2.Clear();
+ }
+ finally
+ {
+ if (gotLock)
+ rwLock.ExitWriteLock();
+ }
+ }
+
+ public int Count
+ {
+ get { return Dictionary1.Count; }
+ }
+
+ public bool ContainsKey(TKey1 key)
+ {
+ return Dictionary1.ContainsKey(key);
+ }
+
+ public bool ContainsKey(TKey2 key)
+ {
+ return Dictionary2.ContainsKey(key);
+ }
+
+ public bool TryGetValue(TKey1 key, out TValue value)
+ {
+ bool success;
+ bool gotLock = false;
+
+ try
+ {
+ // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
+ // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
+ // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
+ try {}
+ finally
+ {
+ rwLock.EnterReadLock();
+ gotLock = true;
+ }
+
+ success = Dictionary1.TryGetValue(key, out value);
+ }
+ finally
+ {
+ if (gotLock)
+ rwLock.ExitReadLock();
+ }
+
+ return success;
+ }
+
+ public bool TryGetValue(TKey2 key, out TValue value)
+ {
+ bool success;
+ bool gotLock = false;
+
+ try
+ {
+ // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
+ // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
+ // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
+ try {}
+ finally
+ {
+ rwLock.EnterReadLock();
+ gotLock = true;
+ }
+
+ success = Dictionary2.TryGetValue(key, out value);
+ }
+ finally
+ {
+ if (gotLock)
+ rwLock.ExitReadLock();
+ }
+
+ return success;
+ }
+
+ public void ForEach(Action action)
+ {
+ bool gotLock = false;
+
+ try
+ {
+ // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
+ // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
+ // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
+ try {}
+ finally
+ {
+ rwLock.EnterReadLock();
+ gotLock = true;
+ }
+
+ foreach (TValue value in Dictionary1.Values)
+ action(value);
+ }
+ finally
+ {
+ if (gotLock)
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public void ForEach(Action> action)
+ {
+ bool gotLock = false;
+
+ try
+ {
+ // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
+ // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
+ // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
+ try {}
+ finally
+ {
+ rwLock.EnterReadLock();
+ gotLock = true;
+ }
+
+ foreach (KeyValuePair entry in Dictionary1)
+ action(entry);
+ }
+ finally
+ {
+ if (gotLock)
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public void ForEach(Action> action)
+ {
+ bool gotLock = false;
+
+ try
+ {
+ // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
+ // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
+ // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
+ try {}
+ finally
+ {
+ rwLock.EnterReadLock();
+ gotLock = true;
+ }
+
+ foreach (KeyValuePair entry in Dictionary2)
+ action(entry);
+ }
+ finally
+ {
+ if (gotLock)
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public TValue FindValue(Predicate predicate)
+ {
+ bool gotLock = false;
+
+ try
+ {
+ // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
+ // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
+ // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
+ try {}
+ finally
+ {
+ rwLock.EnterReadLock();
+ gotLock = true;
+ }
+
+ foreach (TValue value in Dictionary1.Values)
+ {
+ if (predicate(value))
+ return value;
+ }
+ }
+ finally
+ {
+ if (gotLock)
+ rwLock.ExitReadLock();
+ }
+
+ return default(TValue);
+ }
+
+ public IList FindAll(Predicate predicate)
+ {
+ IList list = new List();
+ bool gotLock = false;
+
+ try
+ {
+ // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
+ // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
+ // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
+ try {}
+ finally
+ {
+ rwLock.EnterReadLock();
+ gotLock = true;
+ }
+
+ foreach (TValue value in Dictionary1.Values)
+ {
+ if (predicate(value))
+ list.Add(value);
+ }
+ }
+ finally
+ {
+ if (gotLock)
+ rwLock.ExitReadLock();
+ }
+
+ return list;
+ }
+
+ public int RemoveAll(Predicate predicate)
+ {
+ IList list = new List();
+ bool gotUpgradeableLock = false;
+
+ try
+ {
+ // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
+ // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
+ // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
+ try {}
+ finally
+ {
+ rwLock.EnterUpgradeableReadLock();
+ gotUpgradeableLock = true;
+ }
+
+ foreach (KeyValuePair kvp in Dictionary1)
+ {
+ if (predicate(kvp.Value))
+ list.Add(kvp.Key);
+ }
+
+ IList list2 = new List(list.Count);
+ foreach (KeyValuePair kvp in Dictionary2)
+ {
+ if (predicate(kvp.Value))
+ list2.Add(kvp.Key);
+ }
+
+ bool gotWriteLock = false;
+
+ try
+ {
+ try {}
+ finally
+ {
+ rwLock.EnterUpgradeableReadLock();
+ gotWriteLock = true;
+ }
+
+ for (int i = 0; i < list.Count; i++)
+ Dictionary1.Remove(list[i]);
+
+ for (int i = 0; i < list2.Count; i++)
+ Dictionary2.Remove(list2[i]);
+ }
+ finally
+ {
+ if (gotWriteLock)
+ rwLock.ExitWriteLock();
+ }
+ }
+ finally
+ {
+ if (gotUpgradeableLock)
+ rwLock.ExitUpgradeableReadLock();
+ }
+
+ return list.Count;
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenSim/Framework/PrimitiveBaseShape.cs b/OpenSim/Framework/PrimitiveBaseShape.cs
index 76dcfca726..4c36819bb2 100644
--- a/OpenSim/Framework/PrimitiveBaseShape.cs
+++ b/OpenSim/Framework/PrimitiveBaseShape.cs
@@ -192,18 +192,7 @@ namespace OpenSim.Framework
public PrimitiveBaseShape()
{
- PCode = (byte) PCodeEnum.Primitive;
- ExtraParams = new byte[1];
- m_textureEntry = DEFAULT_TEXTURE;
- }
-
- public PrimitiveBaseShape(bool noShape)
- {
- if (noShape)
- return;
-
PCode = (byte)PCodeEnum.Primitive;
- ExtraParams = new byte[1];
m_textureEntry = DEFAULT_TEXTURE;
}
@@ -216,7 +205,6 @@ namespace OpenSim.Framework
// m_log.DebugFormat("[PRIMITIVE BASE SHAPE]: Creating from {0}", prim.ID);
PCode = (byte)prim.PrimData.PCode;
- ExtraParams = new byte[1];
State = prim.PrimData.State;
PathBegin = Primitive.PackBeginCut(prim.PrimData.PathBegin);
@@ -248,7 +236,10 @@ namespace OpenSim.Framework
SculptTexture = prim.Sculpt.SculptTexture;
SculptType = (byte)prim.Sculpt.Type;
}
- else SculptType = (byte)OpenMetaverse.SculptType.None;
+ else
+ {
+ SculptType = (byte)OpenMetaverse.SculptType.None;
+ }
}
[XmlIgnore]
@@ -340,9 +331,9 @@ namespace OpenSim.Framework
_scale = new Vector3(side, side, side);
}
- public void SetHeigth(float heigth)
+ public void SetHeigth(float height)
{
- _scale.Z = heigth;
+ _scale.Z = height;
}
public void SetRadius(float radius)
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
index 2cd626fa44..8a0340f7cb 100644
--- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
+++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
@@ -436,7 +436,7 @@ namespace OpenSim.Framework.Servers.HttpServer
// reqnum = String.Format("{0}:{1}",request.RemoteIPEndPoint,request.Headers["opensim-request-id"]);
//m_log.DebugFormat("[BASE HTTP SERVER]: <{0}> handle request for {1}",reqnum,request.RawUrl);
- Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", true);
+ Culture.SetCurrentCulture();
// // This is the REST agent interface. We require an agent to properly identify
// // itself. If the REST handler recognizes the prefix it will attempt to
diff --git a/OpenSim/Framework/Tests/MundaneFrameworkTests.cs b/OpenSim/Framework/Tests/MundaneFrameworkTests.cs
index 47fe59921a..1dc80533d8 100644
--- a/OpenSim/Framework/Tests/MundaneFrameworkTests.cs
+++ b/OpenSim/Framework/Tests/MundaneFrameworkTests.cs
@@ -303,10 +303,6 @@ namespace OpenSim.Framework.Tests
Culture.SetCurrentCulture();
Assert.That(Thread.CurrentThread.CurrentCulture.Name == ci.Name, "SetCurrentCulture failed to set thread culture to en-US");
- }
-
-
-
+ }
}
-}
-
+}
\ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs b/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs
index 0872cc8ee9..fd02b08e1b 100644
--- a/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs
+++ b/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs
@@ -53,8 +53,10 @@ namespace OpenSim.Region.CoreModules.Asset.Tests
protected FlotsamAssetCache m_cache;
[SetUp]
- public void SetUp()
+ public override void SetUp()
{
+ base.SetUp();
+
IConfigSource config = new IniConfigSource();
config.AddConfig("Modules");
diff --git a/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs b/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs
index af54c1a1de..b735c611bb 100644
--- a/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs
@@ -81,7 +81,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Groups
}
if (groupsConfig.GetString("Module", "Default") != "Default")
+ {
+ m_Enabled = false;
return;
+ }
}
}
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs
index 39833695e6..fa935cdf3e 100644
--- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs
@@ -146,7 +146,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
if (sp != null && !sp.IsChildAgent)
{
// Local message
- m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to root agent {0} {1}", sp.Name, toAgentID);
+// m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to root agent {0} {1}", sp.Name, toAgentID);
sp.ControllingClient.SendInstantMessage(im);
@@ -159,14 +159,14 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
// try child avatar second
foreach (Scene scene in m_Scenes)
{
- m_log.DebugFormat(
- "[INSTANT MESSAGE]: Looking for child of {0} in {1}", toAgentID, scene.RegionInfo.RegionName);
+// m_log.DebugFormat(
+// "[INSTANT MESSAGE]: Looking for child of {0} in {1}", toAgentID, scene.RegionInfo.RegionName);
ScenePresence sp = scene.GetScenePresence(toAgentID);
if (sp != null)
{
// Local message
- m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to child agent {0} {1}", sp.Name, toAgentID);
+// m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to child agent {0} {1}", sp.Name, toAgentID);
sp.ControllingClient.SendInstantMessage(im);
@@ -176,7 +176,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
}
}
- m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to {0} via XMLRPC", im.toAgentID);
+// m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to {0} via XMLRPC", im.toAgentID);
SendGridInstantMessageViaXMLRPC(im, result);
}
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs
index 4aaf1feab1..7d763faf10 100644
--- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs
@@ -186,17 +186,21 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
foreach (GridInstantMessage im in msglist)
{
- // client.SendInstantMessage(im);
-
- // Send through scene event manager so all modules get a chance
- // to look at this message before it gets delivered.
- //
- // Needed for proper state management for stored group
- // invitations
- //
- Scene s = FindScene(client.AgentId);
- if (s != null)
- s.EventManager.TriggerIncomingInstantMessage(im);
+ if (im.dialog == (byte)InstantMessageDialog.InventoryOffered)
+ // send it directly or else the item will be given twice
+ client.SendInstantMessage(im);
+ else
+ {
+ // Send through scene event manager so all modules get a chance
+ // to look at this message before it gets delivered.
+ //
+ // Needed for proper state management for stored group
+ // invitations
+ //
+ Scene s = FindScene(client.AgentId);
+ if (s != null)
+ s.EventManager.TriggerIncomingInstantMessage(im);
+ }
}
}
}
@@ -215,7 +219,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
if (!m_ForwardOfflineGroupMessages)
{
if (im.dialog == (byte)InstantMessageDialog.GroupNotice ||
- im.dialog != (byte)InstantMessageDialog.GroupInvitation)
+ im.dialog == (byte)InstantMessageDialog.GroupInvitation)
return;
}
diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs
index b768257374..ac25a9346b 100644
--- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs
+++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs
@@ -57,8 +57,10 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests
protected TestClient m_tc;
[SetUp]
- public void SetUp()
+ public override void SetUp()
{
+ base.SetUp();
+
m_iam = new BasicInventoryAccessModule();
IConfigSource config = new IniConfigSource();
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs
index 7e365cace3..69bac82c55 100644
--- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs
+++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs
@@ -46,8 +46,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence.Tests
public class PresenceConnectorsTests : OpenSimTestCase
{
LocalPresenceServicesConnector m_LocalConnector;
- private void SetUp()
+
+ public override void SetUp()
{
+ base.SetUp();
+
IConfigSource config = new IniConfigSource();
config.AddConfig("Modules");
config.AddConfig("PresenceService");
diff --git a/OpenSim/Region/CoreModules/World/Land/Tests/PrimCountModuleTests.cs b/OpenSim/Region/CoreModules/World/Land/Tests/PrimCountModuleTests.cs
index 14eca42b1e..0945b4386a 100644
--- a/OpenSim/Region/CoreModules/World/Land/Tests/PrimCountModuleTests.cs
+++ b/OpenSim/Region/CoreModules/World/Land/Tests/PrimCountModuleTests.cs
@@ -60,8 +60,10 @@ namespace OpenSim.Region.CoreModules.World.Land.Tests
protected ILandObject m_lo2;
[SetUp]
- public void SetUp()
+ public override void SetUp()
{
+ base.SetUp();
+
m_pcm = new PrimCountModule();
LandManagementModule lmm = new LandManagementModule();
m_scene = new SceneHelpers().SetupScene();
diff --git a/OpenSim/Region/CoreModules/World/Media/Moap/Tests/MoapTests.cs b/OpenSim/Region/CoreModules/World/Media/Moap/Tests/MoapTests.cs
index ba4b041871..03a96a45ae 100644
--- a/OpenSim/Region/CoreModules/World/Media/Moap/Tests/MoapTests.cs
+++ b/OpenSim/Region/CoreModules/World/Media/Moap/Tests/MoapTests.cs
@@ -50,8 +50,10 @@ namespace OpenSim.Region.CoreModules.World.Media.Moap.Tests
protected MoapModule m_module;
[SetUp]
- public void SetUp()
+ public override void SetUp()
{
+ base.SetUp();
+
m_module = new MoapModule();
m_scene = new SceneHelpers().SetupScene();
SceneHelpers.SetupSceneModules(m_scene, m_module);
diff --git a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs
index ab8f14344b..7b235aef74 100644
--- a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs
+++ b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs
@@ -365,7 +365,8 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
if (mainParams.Count < 4)
{
- m_console.OutputFormat("Usage: show part id [--full] ");
+ //m_console.OutputFormat("Usage: show part id [--full] ");
+ m_console.OutputFormat("Usage: show part id ");
return;
}
@@ -405,6 +406,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
if (mainParams.Count < 5)
{
+ //m_console.OutputFormat("Usage: show part pos to ");
m_console.OutputFormat("Usage: show part pos [--full] to ");
return;
}
@@ -445,7 +447,8 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
if (mainParams.Count < 4)
{
- m_console.OutputFormat("Usage: show part name [--full] [--regex] ");
+ m_console.OutputFormat("Usage: show part name [--regex] ");
+ //m_console.OutputFormat("Usage: show part name [--full] [--regex] ");
return;
}
@@ -577,6 +580,58 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
cdl.AddRow("Link number", sop.LinkNum);
cdl.AddRow("Flags", sop.Flags);
+ if (showFull)
+ {
+ PrimitiveBaseShape s = sop.Shape;
+ cdl.AddRow("FlexiDrag", s.FlexiDrag);
+ cdl.AddRow("FlexiEntry", s.FlexiEntry);
+ cdl.AddRow("FlexiForce", string.Format("<{0},{1},{2}>", s.FlexiForceX, s.FlexiForceY, s.FlexiForceZ));
+ cdl.AddRow("FlexiGravity", s.FlexiGravity);
+ cdl.AddRow("FlexiSoftness", s.FlexiSoftness);
+ cdl.AddRow("HollowShape", s.HollowShape);
+ cdl.AddRow(
+ "LightColor",
+ string.Format("<{0},{1},{2},{3}>", s.LightColorR, s.LightColorB, s.LightColorG, s.LightColorA));
+ cdl.AddRow("FlexiDrag", s.LightCutoff);
+ cdl.AddRow("FlexiDrag", s.LightEntry);
+ cdl.AddRow("FlexiDrag", s.LightFalloff);
+ cdl.AddRow("FlexiDrag", s.LightIntensity);
+ cdl.AddRow("FlexiDrag", s.LightRadius);
+ cdl.AddRow("Media", string.Format("{0} entries", s.Media != null ? s.Media.Count.ToString() : "n/a"));
+ cdl.AddRow("PathBegin", s.PathBegin);
+ cdl.AddRow("PathEnd", s.PathEnd);
+ cdl.AddRow("PathCurve", s.PathCurve);
+ cdl.AddRow("PathRadiusOffset", s.PathRadiusOffset);
+ cdl.AddRow("PathRevolutions", s.PathRevolutions);
+ cdl.AddRow("PathScale", string.Format("<{0},{1}>", s.PathScaleX, s.PathScaleY));
+ cdl.AddRow("PathSkew", string.Format("<{0},{1}>", s.PathShearX, s.PathShearY));
+ cdl.AddRow("FlexiDrag", s.PathSkew);
+ cdl.AddRow("PathTaper", string.Format("<{0},{1}>", s.PathTaperX, s.PathTaperY));
+ cdl.AddRow("PathTwist", s.PathTwist);
+ cdl.AddRow("PathTwistBegin", s.PathTwistBegin);
+ cdl.AddRow("PCode", s.PCode);
+ cdl.AddRow("ProfileBegin", s.ProfileBegin);
+ cdl.AddRow("ProfileEnd", s.ProfileEnd);
+ cdl.AddRow("ProfileHollow", s.ProfileHollow);
+ cdl.AddRow("ProfileShape", s.ProfileShape);
+ cdl.AddRow("ProjectionAmbiance", s.ProjectionAmbiance);
+ cdl.AddRow("ProjectionEntry", s.ProjectionEntry);
+ cdl.AddRow("ProjectionFocus", s.ProjectionFocus);
+ cdl.AddRow("ProjectionFOV", s.ProjectionFOV);
+ cdl.AddRow("ProjectionTextureUUID", s.ProjectionTextureUUID);
+ cdl.AddRow("Scale", s.Scale);
+ cdl.AddRow(
+ "SculptData",
+ string.Format("{0} bytes", s.SculptData != null ? s.SculptData.Length.ToString() : "n/a"));
+ cdl.AddRow("SculptEntry", s.SculptEntry);
+ cdl.AddRow("SculptTexture", s.SculptTexture);
+ cdl.AddRow("SculptType", s.SculptType);
+ cdl.AddRow("State", s.State);
+
+ // TODO, unpack and display texture entries
+ //cdl.AddRow("Textures", string.Format("{0} entries", s.Textures.
+ }
+
object itemsOutput;
if (showFull)
{
@@ -588,7 +643,6 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
itemsOutput = sop.Inventory.Count;
}
-
cdl.AddRow("Items", itemsOutput);
return sb.Append(cdl.ToString());
diff --git a/OpenSim/Region/Framework/Interfaces/IXmlRpcRouter.cs b/OpenSim/Region/Framework/Interfaces/IXmlRpcRouter.cs
index 6db66742c0..093d3f0030 100644
--- a/OpenSim/Region/Framework/Interfaces/IXmlRpcRouter.cs
+++ b/OpenSim/Region/Framework/Interfaces/IXmlRpcRouter.cs
@@ -34,5 +34,6 @@ namespace OpenSim.Region.Framework.Interfaces
void RegisterNewReceiver(IScriptModule scriptEngine, UUID channelID, UUID objectID, UUID itemID, string url);
void ScriptRemoved(UUID itemID);
void ObjectRemoved(UUID objectID);
+ void UnRegisterReceiver(string channelID, UUID itemID);
}
}
diff --git a/OpenSim/Region/Framework/Scenes/EntityManager.cs b/OpenSim/Region/Framework/Scenes/EntityManager.cs
index b788a3ca08..7181313fbe 100644
--- a/OpenSim/Region/Framework/Scenes/EntityManager.cs
+++ b/OpenSim/Region/Framework/Scenes/EntityManager.cs
@@ -31,6 +31,7 @@ using System.Collections.Generic;
using System.Reflection;
using log4net;
using OpenMetaverse;
+using OpenSim.Framework;
namespace OpenSim.Region.Framework.Scenes
{
@@ -38,7 +39,8 @@ namespace OpenSim.Region.Framework.Scenes
{
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
- private readonly DoubleDictionary m_entities = new DoubleDictionary();
+ private readonly DoubleDictionaryThreadAbortSafe m_entities
+ = new DoubleDictionaryThreadAbortSafe();
public int Count
{
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs
index 1ad5edd296..cca295cf7d 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.cs
@@ -5638,10 +5638,17 @@ namespace OpenSim.Region.Framework.Scenes
return m_SpawnPoint - 1;
}
- // Wrappers to get physics modules retrieve assets. Has to be done this way
- // because we can't assign the asset service to physics directly - at the
- // time physics are instantiated it's not registered but it will be by
- // the time the first prim exists.
+ ///
+ /// Wrappers to get physics modules retrieve assets.
+ ///
+ ///
+ /// Has to be done this way
+ /// because we can't assign the asset service to physics directly - at the
+ /// time physics are instantiated it's not registered but it will be by
+ /// the time the first prim exists.
+ ///
+ ///
+ ///
public void PhysicsRequestAsset(UUID assetID, AssetReceivedDelegate callback)
{
AssetService.Get(assetID.ToString(), callback, PhysicsAssetReceived);
diff --git a/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcGridRouterModule.cs b/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcGridRouterModule.cs
index 6120a81b6c..709d389b75 100644
--- a/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcGridRouterModule.cs
+++ b/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcGridRouterModule.cs
@@ -46,6 +46,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.XmlRpcGridRouterModule
{
public class XmlRpcInfo
{
+ public UUID item;
public UUID channel;
public string uri;
}
@@ -88,6 +89,14 @@ namespace OpenSim.Region.OptionalModules.Scripting.XmlRpcGridRouterModule
return;
scene.RegisterModuleInterface(this);
+
+ IScriptModule scriptEngine = scene.RequestModuleInterface();
+ if ( scriptEngine != null )
+ {
+ scriptEngine.OnScriptRemoved += this.ScriptRemoved;
+ scriptEngine.OnObjectRemoved += this.ObjectRemoved;
+
+ }
}
public void RegionLoaded(Scene scene)
@@ -120,22 +129,36 @@ namespace OpenSim.Region.OptionalModules.Scripting.XmlRpcGridRouterModule
public void RegisterNewReceiver(IScriptModule scriptEngine, UUID channel, UUID objectID, UUID itemID, string uri)
{
- if (!m_Channels.ContainsKey(itemID))
+ if (!m_Enabled)
+ return;
+
+ m_log.InfoFormat("[XMLRPC GRID ROUTER]: New receiver Obj: {0} Ch: {1} ID: {2} URI: {3}",
+ objectID.ToString(), channel.ToString(), itemID.ToString(), uri);
+
+ XmlRpcInfo info = new XmlRpcInfo();
+ info.channel = channel;
+ info.uri = uri;
+ info.item = itemID;
+
+ bool success = SynchronousRestObjectRequester.MakeRequest(
+ "POST", m_ServerURI+"/RegisterChannel/", info);
+
+ if (!success)
{
- XmlRpcInfo info = new XmlRpcInfo();
- info.channel = channel;
- info.uri = uri;
-
- bool success = SynchronousRestObjectRequester.MakeRequest(
- "POST", m_ServerURI+"/RegisterChannel/", info);
-
- if (!success)
- {
- m_log.Error("[XMLRPC GRID ROUTER] Error contacting server");
- }
-
- m_Channels[itemID] = channel;
+ m_log.Error("[XMLRPC GRID ROUTER] Error contacting server");
}
+
+ m_Channels[itemID] = channel;
+
+ }
+
+ public void UnRegisterReceiver(string channelID, UUID itemID)
+ {
+ if (!m_Enabled)
+ return;
+
+ RemoveChannel(itemID);
+
}
public void ScriptRemoved(UUID itemID)
@@ -143,10 +166,33 @@ namespace OpenSim.Region.OptionalModules.Scripting.XmlRpcGridRouterModule
if (!m_Enabled)
return;
- if (m_Channels.ContainsKey(itemID))
+ RemoveChannel(itemID);
+
+ }
+
+ public void ObjectRemoved(UUID objectID)
+ {
+ // m_log.InfoFormat("[XMLRPC GRID ROUTER]: Object Removed {0}",objectID.ToString());
+ }
+
+ private bool RemoveChannel(UUID itemID)
+ {
+ if(!m_Channels.ContainsKey(itemID))
{
- bool success = SynchronousRestObjectRequester.MakeRequest(
- "POST", m_ServerURI+"/RemoveChannel/", m_Channels[itemID]);
+ m_log.InfoFormat("[XMLRPC GRID ROUTER]: Attempted to unregister non-existing Item: {0}", itemID.ToString());
+ return false;
+ }
+
+ XmlRpcInfo info = new XmlRpcInfo();
+
+ info.channel = m_Channels[itemID];
+ info.item = itemID;
+ info.uri = "http://0.0.0.0:00";
+
+ if (info != null)
+ {
+ bool success = SynchronousRestObjectRequester.MakeRequest(
+ "POST", m_ServerURI+"/RemoveChannel/", info);
if (!success)
{
@@ -154,11 +200,9 @@ namespace OpenSim.Region.OptionalModules.Scripting.XmlRpcGridRouterModule
}
m_Channels.Remove(itemID);
+ return true;
}
- }
-
- public void ObjectRemoved(UUID objectID)
- {
+ return false;
}
}
}
diff --git a/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcRouterModule.cs b/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcRouterModule.cs
index 4783f4c25a..ad0b83ddd8 100644
--- a/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcRouterModule.cs
+++ b/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcRouterModule.cs
@@ -101,12 +101,18 @@ namespace OpenSim.Region.OptionalModules.Scripting.XmlRpcRouterModule
scriptEngine.PostScriptEvent(itemID, "xmlrpc_uri", new Object[] {uri});
}
+ public void UnRegisterReceiver(string channelID, UUID itemID)
+ {
+ }
+
public void ScriptRemoved(UUID itemID)
{
+ System.Console.WriteLine("TEST Script Removed!");
}
public void ObjectRemoved(UUID objectID)
{
+ System.Console.WriteLine("TEST Obj Removed!");
}
}
}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
index 4c195e1cfc..21aa9be78b 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
@@ -165,8 +165,8 @@ public sealed class BSCharacter : BSPhysObject
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
// Do this after the object has been added to the world
- BulletSimAPI.SetCollisionFilterMask2(PhysBody.ptr,
- (uint)CollisionFilterGroups.AvatarFilter,
+ BulletSimAPI.SetCollisionGroupMask2(PhysBody.ptr,
+ (uint)CollisionFilterGroups.AvatarGroup,
(uint)CollisionFilterGroups.AvatarMask);
}
@@ -307,7 +307,7 @@ public sealed class BSCharacter : BSPhysObject
}
if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
{
- float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position);
+ float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
if (Position.Z < waterHeight)
{
_position.Z = waterHeight;
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
index 95a41347be..fa3110c39f 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
@@ -24,30 +24,17 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
-
-/* RA: June 14, 2011. Copied from ODEDynamics.cs and converted to
- * call the BulletSim system.
- */
-/* Revised Aug, Sept 2009 by Kitto Flora. ODEDynamics.cs replaces
- * ODEVehicleSettings.cs. It and ODEPrim.cs are re-organised:
- * ODEPrim.cs contains methods dealing with Prim editing, Prim
- * characteristics and Kinetic motion.
- * ODEDynamics.cs contains methods dealing with Prim Physical motion
- * (dynamics) and the associated settings. Old Linear and angular
- * motors for dynamic motion have been replace with MoveLinear()
- * and MoveAngular(); 'Physical' is used only to switch ODE dynamic
- * simualtion on/off; VEHICAL_TYPE_NONE/VEHICAL_TYPE_ is to
- * switch between 'VEHICLE' parameter use and general dynamics
- * settings use.
+ * The quotations from http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial
+ * are Copyright (c) 2009 Linden Research, Inc and are used under their license
+ * of Creative Commons Attribution-Share Alike 3.0
+ * (http://creativecommons.org/licenses/by-sa/3.0/).
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
-using log4net;
using OpenMetaverse;
-using OpenSim.Framework;
using OpenSim.Region.Physics.Manager;
namespace OpenSim.Region.Physics.BulletSPlugin
@@ -100,7 +87,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate
private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate
private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate
- private Vector3 m_lastAngularVelocity = Vector3.Zero; // what was last applied to body
+ private Vector3 m_lastAngularCorrection = Vector3.Zero;
private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body
//Deflection properties
@@ -125,8 +112,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity.
//Attractor properties
- private float m_verticalAttractionEfficiency = 1.0f; // damped
- private float m_verticalAttractionTimescale = 500f; // Timescale > 300 means no vert attractor.
+ private BSVMotor m_verticalAttractionMotor = new BSVMotor("VerticalAttraction");
+ private float m_verticalAttractionEfficiency = 1.0f; // damped
+ private float m_verticalAttractionCutoff = 500f; // per the documentation
+ // Timescale > cutoff means no vert attractor.
+ private float m_verticalAttractionTimescale = 510f;
public BSDynamics(BSScene myScene, BSPrim myPrim)
{
@@ -153,7 +143,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_angularDeflectionTimescale = Math.Max(pValue, 0.01f);
break;
case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE:
- m_angularMotorDecayTimescale = Math.Max(0.01f, Math.Min(pValue,120));
+ m_angularMotorDecayTimescale = ClampInRange(0.01f, pValue, 120);
m_angularMotor.TargetValueDecayTimeScale = m_angularMotorDecayTimescale;
break;
case Vehicle.ANGULAR_MOTOR_TIMESCALE:
@@ -161,7 +151,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_angularMotor.TimeScale = m_angularMotorTimescale;
break;
case Vehicle.BANKING_EFFICIENCY:
- m_bankingEfficiency = Math.Max(-1f, Math.Min(pValue, 1f));
+ m_bankingEfficiency = ClampInRange(-1f, pValue, 1f);
break;
case Vehicle.BANKING_MIX:
m_bankingMix = Math.Max(pValue, 0.01f);
@@ -170,10 +160,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_bankingTimescale = Math.Max(pValue, 0.01f);
break;
case Vehicle.BUOYANCY:
- m_VehicleBuoyancy = Math.Max(-1f, Math.Min(pValue, 1f));
+ m_VehicleBuoyancy = ClampInRange(-1f, pValue, 1f);
break;
case Vehicle.HOVER_EFFICIENCY:
- m_VhoverEfficiency = Math.Max(0f, Math.Min(pValue, 1f));
+ m_VhoverEfficiency = ClampInRange(0f, pValue, 1f);
break;
case Vehicle.HOVER_HEIGHT:
m_VhoverHeight = pValue;
@@ -188,7 +178,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_linearDeflectionTimescale = Math.Max(pValue, 0.01f);
break;
case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE:
- m_linearMotorDecayTimescale = Math.Max(0.01f, Math.Min(pValue,120));
+ m_linearMotorDecayTimescale = ClampInRange(0.01f, pValue, 120);
m_linearMotor.TargetValueDecayTimeScale = m_linearMotorDecayTimescale;
break;
case Vehicle.LINEAR_MOTOR_TIMESCALE:
@@ -196,10 +186,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_linearMotor.TimeScale = m_linearMotorTimescale;
break;
case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY:
- m_verticalAttractionEfficiency = Math.Max(0.1f, Math.Min(pValue, 1f));
+ m_verticalAttractionEfficiency = ClampInRange(0.1f, pValue, 1f);
+ m_verticalAttractionMotor.Efficiency = m_verticalAttractionEfficiency;
break;
case Vehicle.VERTICAL_ATTRACTION_TIMESCALE:
m_verticalAttractionTimescale = Math.Max(pValue, 0.01f);
+ m_verticalAttractionMotor.TimeScale = m_verticalAttractionTimescale;
break;
// These are vector properties but the engine lets you use a single float value to
@@ -239,9 +231,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin
break;
case Vehicle.ANGULAR_MOTOR_DIRECTION:
// Limit requested angular speed to 2 rps= 4 pi rads/sec
- pValue.X = Math.Max(-12.56f, Math.Min(pValue.X, 12.56f));
- pValue.Y = Math.Max(-12.56f, Math.Min(pValue.Y, 12.56f));
- pValue.Z = Math.Max(-12.56f, Math.Min(pValue.Z, 12.56f));
+ pValue.X = ClampInRange(-12.56f, pValue.X, 12.56f);
+ pValue.Y = ClampInRange(-12.56f, pValue.Y, 12.56f);
+ pValue.Z = ClampInRange(-12.56f, pValue.Z, 12.56f);
m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
m_angularMotor.SetTarget(m_angularMotorDirection);
break;
@@ -314,7 +306,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_VhoverEfficiency = 0;
m_VhoverTimescale = 0;
m_VehicleBuoyancy = 0;
-
+
m_linearDeflectionEfficiency = 1;
m_linearDeflectionTimescale = 1;
@@ -363,13 +355,14 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_bankingMix = 1;
m_referenceFrame = Quaternion.Identity;
- m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
- | VehicleFlag.HOVER_TERRAIN_ONLY
+ m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
+ | VehicleFlag.HOVER_TERRAIN_ONLY
| VehicleFlag.HOVER_GLOBAL_HEIGHT
| VehicleFlag.HOVER_UP_ONLY);
m_flags |= (VehicleFlag.NO_DEFLECTION_UP
| VehicleFlag.LIMIT_ROLL_ONLY
| VehicleFlag.LIMIT_MOTOR_UP);
+
break;
case Vehicle.TYPE_CAR:
m_linearMotorDirection = Vector3.Zero;
@@ -513,6 +506,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_bankingEfficiency = 0;
m_bankingMix = 0.7f;
m_bankingTimescale = 5;
+
m_referenceFrame = Quaternion.Identity;
m_referenceFrame = Quaternion.Identity;
@@ -530,13 +524,21 @@ namespace OpenSim.Region.Physics.BulletSPlugin
Refresh();
m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale,
- m_linearMotorDecayTimescale, m_linearFrictionTimescale, 1f);
+ m_linearMotorDecayTimescale, m_linearFrictionTimescale,
+ 1f);
m_linearMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
+
m_angularMotor = new BSVMotor("AngularMotor", m_angularMotorTimescale,
- m_angularMotorDecayTimescale, m_angularFrictionTimescale, 1f);
+ m_angularMotorDecayTimescale, m_angularFrictionTimescale,
+ 1f);
m_angularMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
- // m_bankingMotor = new BSVMotor("BankingMotor", ...);
+ m_verticalAttractionMotor = new BSVMotor("VerticalAttraction", m_verticalAttractionTimescale,
+ BSMotor.Infinite, BSMotor.InfiniteVector,
+ m_verticalAttractionEfficiency);
+ // Z goes away and we keep X and Y
+ m_verticalAttractionMotor.FrictionTimescale = new Vector3(BSMotor.Infinite, BSMotor.Infinite, 0.1f);
+ m_verticalAttractionMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
}
// Some of the properties of this prim may have changed.
@@ -545,9 +547,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
{
if (IsActive)
{
+ // Remember the mass so we don't have to fetch it every step
m_vehicleMass = Prim.Linkset.LinksetMass;
- // Friction effects are handled by this vehicle code
+ // Friction affects are handled by this vehicle code
float friction = 0f;
BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, friction);
@@ -557,14 +560,23 @@ namespace OpenSim.Region.Physics.BulletSPlugin
float angularDamping = PhysicsScene.Params.vehicleAngularDamping;
BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping);
+ // Vehicles report collision events so we know when it's on the ground
+ BulletSimAPI.AddToCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
+
// DEBUG DEBUG DEBUG: use uniform inertia to smooth movement added by Bullet
// Vector3 localInertia = new Vector3(1f, 1f, 1f);
- Vector3 localInertia = new Vector3(m_vehicleMass, m_vehicleMass, m_vehicleMass);
+ // Vector3 localInertia = new Vector3(m_vehicleMass, m_vehicleMass, m_vehicleMass);
+ Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(Prim.PhysShape.ptr, m_vehicleMass);
BulletSimAPI.SetMassProps2(Prim.PhysBody.ptr, m_vehicleMass, localInertia);
+ BulletSimAPI.UpdateInertiaTensor2(Prim.PhysBody.ptr);
- VDetailLog("{0},BSDynamics.Refresh,frict={1},inert={2},aDamp={3}",
+ VDetailLog("{0},BSDynamics.Refresh,frict={1},inert={2},aDamp={3}",
Prim.LocalID, friction, localInertia, angularDamping);
}
+ else
+ {
+ BulletSimAPI.RemoveFromCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
+ }
}
public bool RemoveBodyDependencies(BSPhysObject prim)
@@ -585,60 +597,288 @@ namespace OpenSim.Region.Physics.BulletSPlugin
Refresh();
}
+ #region Known vehicle value functions
+ // Vehicle physical parameters that we buffer from constant getting and setting.
+ // The "m_known*" variables are initialized to 'null', fetched only if referenced
+ // and stored back into the physics engine only if updated.
+ // This does two things: 1) saves continuious calls into unmanaged code, and
+ // 2) signals when a physics property update must happen back to the simulator
+ // to update values modified for the vehicle.
+ private int m_knownChanged;
+ private float? m_knownTerrainHeight;
+ private float? m_knownWaterLevel;
+ private Vector3? m_knownPosition;
+ private Vector3? m_knownVelocity;
+ private Vector3 m_knownForce;
+ private Quaternion? m_knownOrientation;
+ private Vector3? m_knownRotationalVelocity;
+ private Vector3 m_knownRotationalForce;
+ private float? m_knownForwardSpeed;
+
+ private const int m_knownChangedPosition = 1 << 0;
+ private const int m_knownChangedVelocity = 1 << 1;
+ private const int m_knownChangedForce = 1 << 2;
+ private const int m_knownChangedOrientation = 1 << 3;
+ private const int m_knownChangedRotationalVelocity = 1 << 4;
+ private const int m_knownChangedRotationalForce = 1 << 5;
+
+ private void ForgetKnownVehicleProperties()
+ {
+ m_knownTerrainHeight = null;
+ m_knownWaterLevel = null;
+ m_knownPosition = null;
+ m_knownVelocity = null;
+ m_knownForce = Vector3.Zero;
+ m_knownOrientation = null;
+ m_knownRotationalVelocity = null;
+ m_knownRotationalForce = Vector3.Zero;
+ m_knownForwardSpeed = null;
+ m_knownChanged = 0;
+ }
+ private void PushKnownChanged()
+ {
+ if (m_knownChanged != 0)
+ {
+ if ((m_knownChanged & m_knownChangedPosition) != 0)
+ Prim.ForcePosition = VehiclePosition;
+ if ((m_knownChanged & m_knownChangedOrientation) != 0)
+ Prim.ForceOrientation = VehicleOrientation;
+ if ((m_knownChanged & m_knownChangedVelocity) != 0)
+ {
+ Prim.ForceVelocity = VehicleVelocity;
+ BulletSimAPI.SetInterpolationLinearVelocity2(Prim.PhysBody.ptr, VehicleVelocity);
+ }
+ if ((m_knownChanged & m_knownChangedForce) != 0)
+ Prim.AddForce((Vector3)m_knownForce, false, true);
+
+ if ((m_knownChanged & m_knownChangedRotationalVelocity) != 0)
+ {
+ Prim.ForceRotationalVelocity = VehicleRotationalVelocity;
+ // Fake out Bullet by making it think the velocity is the same as last time.
+ BulletSimAPI.SetInterpolationAngularVelocity2(Prim.PhysBody.ptr, VehicleRotationalVelocity);
+ }
+ if ((m_knownChanged & m_knownChangedRotationalForce) != 0)
+ Prim.AddAngularForce((Vector3)m_knownRotationalForce, false, true);
+
+ // If we set one of the values (ie, the physics engine didn't do it) we must force
+ // an UpdateProperties event to send the changes up to the simulator.
+ BulletSimAPI.PushUpdate2(Prim.PhysBody.ptr);
+ }
+ }
+
+ // Since the computation of terrain height can be a little involved, this routine
+ // is used ot fetch the height only once for each vehicle simulation step.
+ private float GetTerrainHeight(Vector3 pos)
+ {
+ if (m_knownTerrainHeight == null)
+ m_knownTerrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos);
+ return (float)m_knownTerrainHeight;
+ }
+
+ // Since the computation of water level can be a little involved, this routine
+ // is used ot fetch the level only once for each vehicle simulation step.
+ private float GetWaterLevel(Vector3 pos)
+ {
+ if (m_knownWaterLevel == null)
+ m_knownWaterLevel = Prim.PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(pos);
+ return (float)m_knownWaterLevel;
+ }
+
+ private Vector3 VehiclePosition
+ {
+ get
+ {
+ if (m_knownPosition == null)
+ m_knownPosition = Prim.ForcePosition;
+ return (Vector3)m_knownPosition;
+ }
+ set
+ {
+ m_knownPosition = value;
+ m_knownChanged |= m_knownChangedPosition;
+ }
+ }
+
+ private Quaternion VehicleOrientation
+ {
+ get
+ {
+ if (m_knownOrientation == null)
+ m_knownOrientation = Prim.ForceOrientation;
+ return (Quaternion)m_knownOrientation;
+ }
+ set
+ {
+ m_knownOrientation = value;
+ m_knownChanged |= m_knownChangedOrientation;
+ }
+ }
+
+ private Vector3 VehicleVelocity
+ {
+ get
+ {
+ if (m_knownVelocity == null)
+ m_knownVelocity = Prim.ForceVelocity;
+ return (Vector3)m_knownVelocity;
+ }
+ set
+ {
+ m_knownVelocity = value;
+ m_knownChanged |= m_knownChangedVelocity;
+ }
+ }
+
+ private void VehicleAddForce(Vector3 aForce)
+ {
+ m_knownForce += aForce;
+ m_knownChanged |= m_knownChangedForce;
+ }
+
+ private Vector3 VehicleRotationalVelocity
+ {
+ get
+ {
+ if (m_knownRotationalVelocity == null)
+ m_knownRotationalVelocity = Prim.ForceRotationalVelocity;
+ return (Vector3)m_knownRotationalVelocity;
+ }
+ set
+ {
+ m_knownRotationalVelocity = value;
+ m_knownChanged |= m_knownChangedRotationalVelocity;
+ }
+ }
+ private void VehicleAddAngularForce(Vector3 aForce)
+ {
+ m_knownRotationalForce += aForce;
+ m_knownChanged |= m_knownChangedRotationalForce;
+ }
+ private float VehicleForwardSpeed
+ {
+ get
+ {
+ if (m_knownForwardSpeed == null)
+ m_knownForwardSpeed = (VehicleVelocity * Quaternion.Inverse(VehicleOrientation)).X;
+ return (float)m_knownForwardSpeed;
+ }
+ }
+
+ #endregion // Known vehicle value functions
+
// One step of the vehicle properties for the next 'pTimestep' seconds.
internal void Step(float pTimestep)
{
if (!IsActive) return;
+ ForgetKnownVehicleProperties();
+
MoveLinear(pTimestep);
MoveAngular(pTimestep);
LimitRotation(pTimestep);
// remember the position so next step we can limit absolute movement effects
- m_lastPositionVector = Prim.ForcePosition;
+ m_lastPositionVector = VehiclePosition;
+
+ // If we forced the changing of some vehicle parameters, update the values and
+ // for the physics engine to note the changes so an UpdateProperties event will happen.
+ PushKnownChanged();
VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
- Prim.LocalID, Prim.ForcePosition, Prim.Force, Prim.ForceVelocity, Prim.RotationalVelocity);
+ Prim.LocalID, VehiclePosition, Prim.Force, VehicleVelocity, VehicleRotationalVelocity);
}
- // Apply the effect of the linear motor.
- // Also does hover and float.
+ // Apply the effect of the linear motor and other linear motions (like hover and float).
private void MoveLinear(float pTimestep)
{
Vector3 linearMotorContribution = m_linearMotor.Step(pTimestep);
- // Rotate new object velocity from vehicle relative to world coordinates
- linearMotorContribution *= Prim.ForceOrientation;
+ // The movement computed in the linear motor is relative to the vehicle
+ // coordinates. Rotate the movement to world coordinates.
+ linearMotorContribution *= VehicleOrientation;
// ==================================================================
- // Gravity and Buoyancy
- // There is some gravity, make a gravity force vector that is applied after object velocity.
+ // Buoyancy: force to overcome gravity.
// m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
- Vector3 grav = Prim.PhysicsScene.DefaultGravity * (1f - m_VehicleBuoyancy);
+ // So, if zero, don't change anything (let gravity happen). If one, negate the effect of gravity.
+ Vector3 buoyancyContribution = Prim.PhysicsScene.DefaultGravity * m_VehicleBuoyancy;
- // Current vehicle position
- Vector3 pos = Prim.ForcePosition;
+ Vector3 terrainHeightContribution = ComputeLinearTerrainHeightCorrection(pTimestep);
+
+ Vector3 hoverContribution = ComputeLinearHover(pTimestep);
+
+ ComputeLinearBlockingEndPoint(pTimestep);
+
+ Vector3 limitMotorUpContribution = ComputeLinearMotorUp(pTimestep);
// ==================================================================
- Vector3 terrainHeightContribution = Vector3.Zero;
- // If below the terrain, move us above the ground a little.
- float terrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos);
- // Taking the rotated size doesn't work here because m_prim.Size is the size of the root prim and not the linkset.
- // TODO: Add a m_prim.LinkSet.Size similar to m_prim.LinkSet.Mass.
- // Vector3 rotatedSize = m_prim.Size * m_prim.ForceOrientation;
- // if (rotatedSize.Z < terrainHeight)
- if (pos.Z < terrainHeight)
+ Vector3 newVelocity = linearMotorContribution
+ + terrainHeightContribution
+ + hoverContribution
+ + limitMotorUpContribution;
+
+ // If not changing some axis, reduce out velocity
+ if ((m_flags & (VehicleFlag.NO_X)) != 0)
+ newVelocity.X = 0;
+ if ((m_flags & (VehicleFlag.NO_Y)) != 0)
+ newVelocity.Y = 0;
+ if ((m_flags & (VehicleFlag.NO_Z)) != 0)
+ newVelocity.Z = 0;
+
+ // ==================================================================
+ // Clamp high or low velocities
+ float newVelocityLengthSq = newVelocity.LengthSquared();
+ // if (newVelocityLengthSq > 1e6f)
+ if (newVelocityLengthSq > 1000f)
{
- // TODO: correct position by applying force rather than forcing position.
- pos.Z = terrainHeight + 2;
- Prim.ForcePosition = pos;
- VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", Prim.LocalID, terrainHeight, pos);
+ newVelocity /= newVelocity.Length();
+ newVelocity *= 1000f;
+ }
+ // else if (newVelocityLengthSq < 1e-6f)
+ else if (newVelocityLengthSq < 0.001f)
+ newVelocity = Vector3.Zero;
+
+ // ==================================================================
+ // Stuff new linear velocity into the vehicle.
+ // Since the velocity is just being set, it is not scaled by pTimeStep. Bullet will do that for us.
+ VehicleVelocity = newVelocity;
+
+ // Other linear forces are applied as forces.
+ Vector3 totalDownForce = buoyancyContribution * m_vehicleMass;
+ if (!totalDownForce.ApproxEquals(Vector3.Zero, 0.01f))
+ {
+ VehicleAddForce(totalDownForce);
}
- // ==================================================================
- Vector3 hoverContribution = Vector3.Zero;
- // Check if hovering
+ VDetailLog("{0}, MoveLinear,done,newVel={1},totDown={2},IsColliding={3}",
+ Prim.LocalID, newVelocity, totalDownForce, Prim.IsColliding);
+ VDetailLog("{0}, MoveLinear,done,linContrib={1},terrContrib={2},hoverContrib={3},limitContrib={4},buoyContrib={5}",
+ Prim.LocalID,
+ linearMotorContribution, terrainHeightContribution, hoverContribution,
+ limitMotorUpContribution, buoyancyContribution
+ );
+
+ } // end MoveLinear()
+
+ public Vector3 ComputeLinearTerrainHeightCorrection(float pTimestep)
+ {
+ Vector3 ret = Vector3.Zero;
+ // If below the terrain, move us above the ground a little.
+ // TODO: Consider taking the rotated size of the object or possibly casting a ray.
+ if (VehiclePosition.Z < GetTerrainHeight(VehiclePosition))
+ {
+ // TODO: correct position by applying force rather than forcing position.
+ VehiclePosition += new Vector3(0f, 0f, GetTerrainHeight(VehiclePosition) + 2f);
+ VDetailLog("{0}, MoveLinear,terrainHeight,terrainHeight={1},pos={2}", Prim.LocalID, GetTerrainHeight(VehiclePosition), VehiclePosition);
+ }
+ return ret;
+ }
+
+ public Vector3 ComputeLinearHover(float pTimestep)
+ {
+ Vector3 ret = Vector3.Zero;
+
// m_VhoverEfficiency: 0=bouncy, 1=totally damped
// m_VhoverTimescale: time to achieve height
if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
@@ -646,11 +886,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// We should hover, get the target height
if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0)
{
- m_VhoverTargetHeight = Prim.PhysicsScene.GetWaterLevelAtXYZ(pos) + m_VhoverHeight;
+ m_VhoverTargetHeight = GetWaterLevel(VehiclePosition) + m_VhoverHeight;
}
if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
{
- m_VhoverTargetHeight = terrainHeight + m_VhoverHeight;
+ m_VhoverTargetHeight = GetTerrainHeight(VehiclePosition) + m_VhoverHeight;
}
if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
{
@@ -660,43 +900,47 @@ namespace OpenSim.Region.Physics.BulletSPlugin
if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0)
{
// If body is already heigher, use its height as target height
- if (pos.Z > m_VhoverTargetHeight)
- m_VhoverTargetHeight = pos.Z;
+ if (VehiclePosition.Z > m_VhoverTargetHeight)
+ m_VhoverTargetHeight = VehiclePosition.Z;
}
+
if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0)
{
- if (Math.Abs(pos.Z - m_VhoverTargetHeight) > 0.2f)
+ if (Math.Abs(VehiclePosition.Z - m_VhoverTargetHeight) > 0.2f)
{
+ Vector3 pos = VehiclePosition;
pos.Z = m_VhoverTargetHeight;
- Prim.ForcePosition = pos;
+ VehiclePosition = pos;
}
}
else
{
- float verticalError = pos.Z - m_VhoverTargetHeight;
- // RA: where does the 50 come from?
- float verticalCorrectionVelocity = pTimestep * ((verticalError * 50.0f) / m_VhoverTimescale);
- // Replace Vertical speed with correction figure if significant
- if (verticalError > 0.01f)
+ // Error is positive if below the target and negative if above.
+ float verticalError = m_VhoverTargetHeight - VehiclePosition.Z;
+ float verticalCorrectionVelocity = verticalError / m_VhoverTimescale;
+
+ // TODO: implement m_VhoverEfficiency correctly
+ if (Math.Abs(verticalError) > m_VhoverEfficiency)
{
- hoverContribution = new Vector3(0f, 0f, verticalCorrectionVelocity);
- //KF: m_VhoverEfficiency is not yet implemented
- }
- else if (verticalError < -0.01)
- {
- hoverContribution = new Vector3(0f, 0f, -verticalCorrectionVelocity);
+ ret = new Vector3(0f, 0f, verticalCorrectionVelocity);
}
}
- VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}",
- Prim.LocalID, pos, hoverContribution, m_VhoverHeight, m_VhoverTargetHeight);
+ VDetailLog("{0}, MoveLinear,hover,pos={1},ret={2},hoverTS={3},height={4},target={5}",
+ Prim.LocalID, VehiclePosition, ret, m_VhoverTimescale, m_VhoverHeight, m_VhoverTargetHeight);
}
- // ==================================================================
+ return ret;
+ }
+
+ public bool ComputeLinearBlockingEndPoint(float pTimestep)
+ {
+ bool changed = false;
+
+ Vector3 pos = VehiclePosition;
Vector3 posChange = pos - m_lastPositionVector;
if (m_BlockingEndPoint != Vector3.Zero)
{
- bool changed = false;
if (pos.X >= (m_BlockingEndPoint.X - (float)1))
{
pos.X -= posChange.X + 1;
@@ -724,238 +968,109 @@ namespace OpenSim.Region.Physics.BulletSPlugin
}
if (changed)
{
- Prim.ForcePosition = pos;
- VDetailLog("{0},MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}",
+ VehiclePosition = pos;
+ VDetailLog("{0}, MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}",
Prim.LocalID, m_BlockingEndPoint, posChange, pos);
}
}
+ return changed;
+ }
+
+ // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
+ // Prevent ground vehicles from motoring into the sky. This flag has a subtle effect when
+ // used with conjunction with banking: the strength of the banking will decay when the
+ // vehicle no longer experiences collisions. The decay timescale is the same as
+ // VEHICLE_BANKING_TIMESCALE. This is to help prevent ground vehicles from steering
+ // when they are in mid jump.
+ // TODO: this code is wrong. Also, what should it do for boats (height from water)?
+ // This is just using the ground and a general collision check. Should really be using
+ // a downward raycast to find what is below.
+ public Vector3 ComputeLinearMotorUp(float pTimestep)
+ {
+ Vector3 ret = Vector3.Zero;
- // ==================================================================
- Vector3 limitMotorUpContribution = Vector3.Zero;
if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
{
// If the vehicle is motoring into the sky, get it going back down.
- float distanceAboveGround = pos.Z - terrainHeight;
- if (distanceAboveGround > 1f)
+ float distanceAboveGround = VehiclePosition.Z - GetTerrainHeight(VehiclePosition);
+ // Not colliding if the vehicle is off the ground
+ if (!Prim.IsColliding)
{
// downForce = new Vector3(0, 0, (-distanceAboveGround / m_bankingTimescale) * pTimestep);
// downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale);
- limitMotorUpContribution = new Vector3(0, 0, -distanceAboveGround);
+ ret = new Vector3(0, 0, -distanceAboveGround);
}
- // TODO: this calculation is all wrong. From the description at
+ // TODO: this calculation is wrong. From the description at
// (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce
// has a decay factor. This says this force should
// be computed with a motor.
- VDetailLog("{0},MoveLinear,limitMotorUp,distAbove={1},downForce={2}",
- Prim.LocalID, distanceAboveGround, limitMotorUpContribution);
+ // TODO: add interaction with banking.
+ VDetailLog("{0}, MoveLinear,limitMotorUp,distAbove={1},downForce={2}",
+ Prim.LocalID, distanceAboveGround, ret);
}
-
- // ==================================================================
- Vector3 newVelocity = linearMotorContribution
- + terrainHeightContribution
- + hoverContribution
- + limitMotorUpContribution;
-
- // If not changing some axis, reduce out velocity
- if ((m_flags & (VehicleFlag.NO_X)) != 0)
- newVelocity.X = 0;
- if ((m_flags & (VehicleFlag.NO_Y)) != 0)
- newVelocity.Y = 0;
- if ((m_flags & (VehicleFlag.NO_Z)) != 0)
- newVelocity.Z = 0;
-
- // ==================================================================
- // Clamp REALLY high or low velocities
- float newVelocityLengthSq = newVelocity.LengthSquared();
- if (newVelocityLengthSq > 1e6f)
- {
- newVelocity /= newVelocity.Length();
- newVelocity *= 1000f;
- }
- else if (newVelocityLengthSq < 1e-6f)
- newVelocity = Vector3.Zero;
-
- // ==================================================================
- // Stuff new linear velocity into the vehicle
- Prim.ForceVelocity = newVelocity;
- // Prim.ApplyForceImpulse((m_newVelocity - Prim.Velocity) * m_vehicleMass, false); // DEBUG DEBUG
-
- // Other linear forces are applied as forces.
- Vector3 totalDownForce = grav * m_vehicleMass;
- if (totalDownForce != Vector3.Zero)
- {
- Prim.AddForce(totalDownForce, false);
- }
-
- VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},primVel={4},totalDown={5}",
- Prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector,
- newVelocity, Prim.Velocity, totalDownForce);
-
- } // end MoveLinear()
+ return ret;
+ }
// =======================================================================
// =======================================================================
// Apply the effect of the angular motor.
+ // The 'contribution' is how much angular correction velocity each function wants.
+ // All the contributions are added together and the resulting velocity is
+ // set directly on the vehicle.
private void MoveAngular(float pTimestep)
{
- // m_angularMotorDirection // angular velocity requested by LSL motor
- // m_angularMotorVelocity // current angular motor velocity (ramps up and down)
- // m_angularMotorTimescale // motor angular velocity ramp up time
- // m_angularMotorDecayTimescale // motor angular velocity decay rate
- // m_angularFrictionTimescale // body angular velocity decay rate
- // m_lastAngularVelocity // what was last applied to body
-
- if (m_angularMotorDirection.LengthSquared() > 0.0001)
- {
- Vector3 origVel = m_angularMotorVelocity;
- Vector3 origDir = m_angularMotorDirection;
-
- // new velocity += error / ( time to get there / step interval)
- // requested direction - current vehicle direction
- m_angularMotorVelocity += (m_angularMotorDirection - m_angularMotorVelocity) / (m_angularMotorTimescale / pTimestep);
- // decay requested direction
- m_angularMotorDirection *= (1.0f - (pTimestep * 1.0f/m_angularMotorDecayTimescale));
-
- VDetailLog("{0},MoveAngular,angularMotorApply,angTScale={1},timeStep={2},origvel={3},origDir={4},vel={5}",
- Prim.LocalID, m_angularMotorTimescale, pTimestep, origVel, origDir, m_angularMotorVelocity);
- }
- else
- {
- m_angularMotorVelocity = Vector3.Zero;
- }
-
+ // The user wants how many radians per second angular change?
Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep);
// ==================================================================
- Vector3 verticalAttractionContribution = Vector3.Zero;
- // If vertical attaction timescale is reasonable and we applied an angular force last time...
- if (m_verticalAttractionTimescale < 300 && m_lastAngularVelocity != Vector3.Zero)
+ // 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
+ if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
{
- float VAservo = pTimestep * 0.2f / m_verticalAttractionTimescale;
- if (Prim.IsColliding)
- VAservo = pTimestep * 0.05f / m_verticalAttractionTimescale;
-
- VAservo *= (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
-
- // Create a vector of the vehicle "up" in world coordinates
- Vector3 verticalError = Vector3.UnitZ * Prim.ForceOrientation;
- // verticalError.X and .Y are the World error amounts. They are 0 when there is no
- // error (Vehicle Body is 'vertical'), and .Z will be 1. As the body leans to its
- // side |.X| will increase to 1 and .Z fall to 0. As body inverts |.X| will fall
- // and .Z will go // negative. Similar for tilt and |.Y|. .X and .Y must be
- // modulated to prevent a stable inverted body.
-
- // Error is 0 (no error) to +/- 2 (max error)
- if (verticalError.Z < 0.0f)
- {
- verticalError.X = 2.0f - verticalError.X;
- verticalError.Y = 2.0f - verticalError.Y;
- }
- // scale it by VAservo (timestep and timescale)
- verticalError = verticalError * VAservo;
-
- // As the body rotates around the X axis, then verticalError.Y increases; Rotated around Y
- // then .X increases, so change Body angular velocity X based on Y, and Y based on X.
- // Z is not changed.
- verticalAttractionContribution.X = verticalError.Y;
- verticalAttractionContribution.Y = - verticalError.X;
- verticalAttractionContribution.Z = 0f;
-
- // scaling appears better usingsquare-law
- Vector3 angularVelocity = Prim.ForceRotationalVelocity;
- float bounce = 1.0f - (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
- verticalAttractionContribution.X += bounce * angularVelocity.X;
- verticalAttractionContribution.Y += bounce * angularVelocity.Y;
-
- VDetailLog("{0},MoveAngular,verticalAttraction,VAservo={1},effic={2},verticalError={3},bounce={4},vertattr={5}",
- Prim.LocalID, VAservo, m_verticalAttractionEfficiency, verticalError, bounce, verticalAttractionContribution);
-
+ angularMotorContribution.X = 0f;
+ angularMotorContribution.Y = 0f;
+ VDetailLog("{0}, MoveAngular,noDeflectionUp,angularMotorContrib={1}", Prim.LocalID, angularMotorContribution);
}
- // ==================================================================
- Vector3 deflectionContribution = Vector3.Zero;
- if (m_angularDeflectionEfficiency != 0)
- {
- // Compute a scaled vector that points in the preferred axis (X direction)
- Vector3 scaledDefaultDirection =
- new Vector3((pTimestep * 10 * (m_angularDeflectionEfficiency / m_angularDeflectionTimescale)), 0, 0);
- // Adding the current vehicle orientation and reference frame displaces the orientation to the frame.
- // Rotate the scaled default axix relative to the actual vehicle direction giving where it should point.
- Vector3 preferredAxisOfMotion = scaledDefaultDirection * Quaternion.Add(Prim.ForceOrientation, m_referenceFrame);
+ Vector3 verticalAttractionContribution = ComputeAngularVerticalAttraction();
- // Scale by efficiency and timescale
- deflectionContribution = (preferredAxisOfMotion * (m_angularDeflectionEfficiency) / m_angularDeflectionTimescale) * pTimestep;
+ Vector3 deflectionContribution = ComputeAngularDeflection();
- VDetailLog("{0},MoveAngular,Deflection,perfAxis={1},deflection={2}",
- Prim.LocalID, preferredAxisOfMotion, deflectionContribution);
- // This deflection computation is not correct.
- deflectionContribution = Vector3.Zero;
- }
-
- // ==================================================================
- Vector3 bankingContribution = Vector3.Zero;
- if (m_bankingEfficiency != 0)
- {
- Vector3 dir = Vector3.One * Prim.ForceOrientation;
- float mult = (m_bankingMix*m_bankingMix)*-1*(m_bankingMix < 0 ? -1 : 1);
- //Changes which way it banks in and out of turns
-
- //Use the square of the efficiency, as it looks much more how SL banking works
- float effSquared = (m_bankingEfficiency*m_bankingEfficiency);
- if (m_bankingEfficiency < 0)
- effSquared *= -1; //Keep the negative!
-
- float mix = Math.Abs(m_bankingMix);
- if (m_angularMotorVelocity.X == 0)
- {
- // The vehicle is stopped
- /*if (!parent.Orientation.ApproxEquals(this.m_referenceFrame, 0.25f))
- {
- Vector3 axisAngle;
- float angle;
- parent.Orientation.GetAxisAngle(out axisAngle, out angle);
- Vector3 rotatedVel = parent.Velocity * parent.Orientation;
- if ((rotatedVel.X < 0 && axisAngle.Y > 0) || (rotatedVel.X > 0 && axisAngle.Y < 0))
- m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (1f) * 10;
- else
- m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (-1f) * 10;
- }*/
- }
- else
- {
- bankingContribution.Z += (effSquared * (mult * mix)) * (m_angularMotorVelocity.X) * 4;
- }
-
- //If they are colliding, we probably shouldn't shove the prim around... probably
- if (!Prim.IsColliding && Math.Abs(m_angularMotorVelocity.X) > mix)
- {
- float angVelZ = m_angularMotorVelocity.X*-1;
- /*if(angVelZ > mix)
- angVelZ = mix;
- else if(angVelZ < -mix)
- angVelZ = -mix;*/
- //This controls how fast and how far the banking occurs
- Vector3 bankingRot = new Vector3(angVelZ*(effSquared*mult), 0, 0);
- if (bankingRot.X > 3)
- bankingRot.X = 3;
- else if (bankingRot.X < -3)
- bankingRot.X = -3;
- bankingRot *= Prim.ForceOrientation;
- bankingContribution += bankingRot;
- }
- m_angularMotorVelocity.X *= m_bankingEfficiency == 1 ? 0.0f : 1 - m_bankingEfficiency;
- VDetailLog("{0},MoveAngular,Banking,bEff={1},angMotVel={2},effSq={3},mult={4},mix={5},banking={6}",
- Prim.LocalID, m_bankingEfficiency, m_angularMotorVelocity, effSquared, mult, mix, bankingContribution);
- }
+ Vector3 bankingContribution = ComputeAngularBanking();
// ==================================================================
m_lastVertAttractor = verticalAttractionContribution;
- // Sum velocities
- m_lastAngularVelocity = angularMotorContribution
+ // Sum corrections
+ m_lastAngularCorrection = angularMotorContribution
+ verticalAttractionContribution
- + bankingContribution
- + deflectionContribution;
+ + deflectionContribution
+ + bankingContribution;
+
+ // ==================================================================
+ // Apply the correction velocity.
+ // TODO: Should this be applied as an angular force (torque)?
+ if (!m_lastAngularCorrection.ApproxEquals(Vector3.Zero, 0.01f))
+ {
+ Vector3 scaledCorrection = m_lastAngularCorrection * pTimestep;
+ VehicleRotationalVelocity = scaledCorrection;
+
+ VDetailLog("{0}, MoveAngular,done,nonZero,angMotorContrib={1},vertAttrContrib={2},bankContrib={3},deflectContrib={4},totalContrib={5},scaledCorr={6}",
+ Prim.LocalID,
+ angularMotorContribution, verticalAttractionContribution,
+ bankingContribution, deflectionContribution,
+ m_lastAngularCorrection, scaledCorrection
+ );
+ }
+ else
+ {
+ // The vehicle is not adding anything angular wise.
+ VehicleRotationalVelocity = Vector3.Zero;
+ VDetailLog("{0}, MoveAngular,done,zero", Prim.LocalID);
+ }
// ==================================================================
//Offset section
@@ -983,41 +1098,166 @@ namespace OpenSim.Region.Physics.BulletSPlugin
torqueFromOffset.Y = 0;
if (float.IsNaN(torqueFromOffset.Z))
torqueFromOffset.Z = 0;
- torqueFromOffset *= m_vehicleMass;
- Prim.ApplyTorqueImpulse(torqueFromOffset, true);
- VDetailLog("{0},BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset);
+
+ VehicleAddAngularForce(torqueFromOffset * m_vehicleMass);
+ VDetailLog("{0}, BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset);
}
- // ==================================================================
- // NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement
- if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
- {
- m_lastAngularVelocity.X = 0;
- m_lastAngularVelocity.Y = 0;
- VDetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity);
- }
+ }
+ // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
+ // Some vehicles, like boats, should always keep their up-side up. This can be done by
+ // enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to
+ // the world z-axis (a.k.a. "up"). To take advantage of this feature you would set the
+ // VEHICLE_VERTICAL_ATTRACTION_TIMESCALE to control the period of the spring frequency,
+ // and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An
+ // efficiency of 0.0 will cause the spring to wobble around its equilibrium, while an
+ // efficiency of 1.0 will cause the spring to reach its equilibrium with exponential decay.
+ public Vector3 ComputeAngularVerticalAttraction()
+ {
+ Vector3 ret = Vector3.Zero;
- // ==================================================================
- if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f))
+ // If vertical attaction timescale is reasonable and we applied an angular force last time...
+ if (m_verticalAttractionTimescale < m_verticalAttractionCutoff)
{
- m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero.
- Prim.ZeroAngularMotion(true);
- VDetailLog("{0},MoveAngular,zeroAngularMotion,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity);
- }
- else
- {
- // Apply to the body.
- // The above calculates the absolute angular velocity needed. Angular velocity is massless.
- // Since we are stuffing the angular velocity directly into the object, the computed
- // velocity needs to be scaled by the timestep.
- // Also remove any motion that is on the object so added motion is only from vehicle.
- Vector3 applyAngularForce = ((m_lastAngularVelocity * pTimestep)
- - Prim.ForceRotationalVelocity);
- Prim.ForceRotationalVelocity = applyAngularForce;
+ // Take a vector pointing up and convert it from world to vehicle relative coords.
+ Vector3 verticalError = Vector3.UnitZ * VehicleOrientation;
+ verticalError.Normalize();
- VDetailLog("{0},MoveAngular,done,newRotVel={1},lastAngular={2}",
- Prim.LocalID, applyAngularForce, m_lastAngularVelocity);
+ // If vertical attraction correction is needed, the vector that was pointing up (UnitZ)
+ // is now leaning to one side (rotated around the X axis) and the Y value will
+ // go from zero (nearly straight up) to one (completely to the side) or leaning
+ // front-to-back (rotated around the Y axis) and the value of X will be between
+ // zero and one.
+ // The value of Z is how far the rotation is off with 1 meaning none and 0 being 90 degrees.
+
+ // If verticalError.Z is negative, the vehicle is upside down. Add additional push.
+ if (verticalError.Z < 0f)
+ {
+ verticalError.X = 2f - verticalError.X;
+ verticalError.Y = 2f - verticalError.Y;
+ }
+
+ // Y error means needed rotation around X axis and visa versa.
+ ret.X = verticalError.Y;
+ ret.Y = - verticalError.X;
+ ret.Z = 0f;
+
+ // Scale the correction force by how far we're off from vertical.
+ // Z error of one says little error. As Z gets smaller, the vehicle is leaning farther over.
+ float clampedSqrZError = ClampInRange(0.01f, verticalError.Z * verticalError.Z, 1f);
+ float vertForce = 1f / clampedSqrZError;
+
+ ret *= vertForce;
+
+ // Correction happens over a number of seconds.
+ Vector3 unscaledContrib = ret;
+ ret /= m_verticalAttractionTimescale;
+
+ VDetailLog("{0}, MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},vertForce={3},eff={4},vertAttr={5}",
+ Prim.LocalID, verticalError, unscaledContrib, vertForce, m_verticalAttractionEfficiency, ret);
}
+ return ret;
+ }
+
+ // Return the angular correction to correct the direction the vehicle is pointing to be
+ // the direction is should want to be pointing.
+ // The vehicle is moving in some direction and correct its orientation to it is pointing
+ // in that direction.
+ // TODO: implement reference frame.
+ public Vector3 ComputeAngularDeflection()
+ {
+ Vector3 ret = Vector3.Zero;
+ return ret; // DEBUG DEBUG DEBUG debug one force at a time
+
+ if (m_angularDeflectionEfficiency != 0)
+ {
+ // The direction the vehicle is moving
+ Vector3 movingDirection = VehicleVelocity;
+ movingDirection.Normalize();
+
+ // The direction the vehicle is pointing
+ Vector3 pointingDirection = Vector3.UnitX * VehicleOrientation;
+ pointingDirection.Normalize();
+
+ // The difference between what is and what should be
+ Vector3 deflectionError = movingDirection - pointingDirection;
+
+ // Scale the correction by recovery timescale and efficiency
+ ret = (-deflectionError * VehicleForwardSpeed) * m_angularDeflectionEfficiency;
+ ret /= m_angularDeflectionTimescale;
+
+ VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}",
+ Prim.LocalID, movingDirection, pointingDirection, deflectionError, ret);
+ }
+ return ret;
+ }
+
+ // Return an angular change to rotate the vehicle around the Z axis when the vehicle
+ // is tipped around the X axis.
+ // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
+ // The vertical attractor feature must be enabled in order for the banking behavior to
+ // function. The way banking works is this: a rotation around the vehicle's roll-axis will
+ // produce a angular velocity around the yaw-axis, causing the vehicle to turn. The magnitude
+ // of the yaw effect will be proportional to the
+ // VEHICLE_BANKING_EFFICIENCY, the angle of the roll rotation, and sometimes the vehicle's
+ // velocity along its preferred axis of motion.
+ // The VEHICLE_BANKING_EFFICIENCY can vary between -1 and +1. When it is positive then any
+ // positive rotation (by the right-hand rule) about the roll-axis will effect a
+ // (negative) torque around the yaw-axis, making it turn to the right--that is the
+ // vehicle will lean into the turn, which is how real airplanes and motorcycle's work.
+ // Negating the banking coefficient will make it so that the vehicle leans to the
+ // outside of the turn (not very "physical" but might allow interesting vehicles so why not?).
+ // The VEHICLE_BANKING_MIX is a fake (i.e. non-physical) parameter that is useful for making
+ // banking vehicles do what you want rather than what the laws of physics allow.
+ // For example, consider a real motorcycle...it must be moving forward in order for
+ // it to turn while banking, however video-game motorcycles are often configured
+ // to turn in place when at a dead stop--because they are often easier to control
+ // that way using the limited interface of the keyboard or game controller. The
+ // VEHICLE_BANKING_MIX enables combinations of both realistic and non-realistic
+ // banking by functioning as a slider between a banking that is correspondingly
+ // totally static (0.0) and totally dynamic (1.0). By "static" we mean that the
+ // banking effect depends only on the vehicle's rotation about its roll-axis compared
+ // to "dynamic" where the banking is also proportional to its velocity along its
+ // roll-axis. Finding the best value of the "mixture" will probably require trial and error.
+ // The time it takes for the banking behavior to defeat a preexisting angular velocity about the
+ // world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to
+ // bank quickly then give it a banking timescale of about a second or less, otherwise you can
+ // make a sluggish vehicle by giving it a timescale of several seconds.
+ public Vector3 ComputeAngularBanking()
+ {
+ Vector3 ret = Vector3.Zero;
+
+ if (m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff)
+ {
+ // This works by rotating a unit vector to the orientation of the vehicle. The
+ // roll (tilt) will be Y component of a tilting Z vector (zero for no tilt
+ // up to one for full over).
+ Vector3 rollComponents = Vector3.UnitZ * VehicleOrientation;
+
+ // Figure out the yaw value for this much roll.
+ float turnComponent = rollComponents.Y * rollComponents.Y * m_bankingEfficiency;
+ // Keep the sign
+ if (rollComponents.Y < 0f)
+ turnComponent = -turnComponent;
+
+ // TODO: there must be a better computation of the banking force.
+ float bankingTurnForce = turnComponent;
+
+ // actual error = static turn error + dynamic turn error
+ float mixedBankingError = bankingTurnForce * (1f - m_bankingMix) + bankingTurnForce * m_bankingMix * VehicleForwardSpeed;
+ // TODO: the banking effect should not go to infinity but what to limit it to?
+ mixedBankingError = ClampInRange(-20f, mixedBankingError, 20f);
+
+ // Build the force vector to change rotation from what it is to what it should be
+ ret.Z = -mixedBankingError;
+
+ // Don't do it all at once.
+ ret /= m_bankingTimescale;
+
+ VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},turnComp={3},bankErr={4},mixedBankErr={5},ret={6}",
+ Prim.LocalID, rollComponents, VehicleForwardSpeed, turnComponent, bankingTurnForce, mixedBankingError, ret);
+ }
+ return ret;
}
// This is from previous instantiations of XXXDynamics.cs.
@@ -1026,7 +1266,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// Should this be in MoveAngular()?
internal void LimitRotation(float timestep)
{
- Quaternion rotq = Prim.ForceOrientation;
+ Quaternion rotq = VehicleOrientation;
Quaternion m_rot = rotq;
if (m_RollreferenceFrame != Quaternion.Identity)
{
@@ -1054,12 +1294,17 @@ namespace OpenSim.Region.Physics.BulletSPlugin
}
if (rotq != m_rot)
{
- Prim.ForceOrientation = m_rot;
- VDetailLog("{0},LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot);
+ VehicleOrientation = m_rot;
+ VDetailLog("{0}, LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot);
}
}
+ private float ClampInRange(float low, float val, float high)
+ {
+ return Math.Max(low, Math.Min(val, high));
+ }
+
// Invoke the detailed logger and output something if it's enabled.
private void VDetailLog(string msg, params Object[] args)
{
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs b/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs
index 663b6f44b7..390c2f9820 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs
@@ -1,191 +1,185 @@
-/*
- * 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.BulletSPlugin
-{
-
-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.
- public static string[] MaterialNames = { "Stone", "Metal", "Glass", "Wood",
- "Flesh", "Plastic", "Rubber", "Light", "Avatar" };
- public static string[] MaterialAttribs = { "Density", "Friction", "Restitution",
- "ccdMotionThreshold", "ccdSweptSphereRadius" };
-
- public MaterialAttributes(string t, float d, float f, float r, float ccdM, float ccdS)
- {
- type = t;
- density = d;
- friction = f;
- restitution = r;
- ccdMotionThreshold = ccdM;
- ccdSweptSphereRadius = ccdS;
- }
- public string type;
- public float density;
- public float friction;
- public float restitution;
- public float ccdMotionThreshold;
- public float ccdSweptSphereRadius;
-}
-
-public static class BSMaterials
-{
- public static MaterialAttributes[] Attributes;
-
- static BSMaterials()
- {
- // Attribute sets for both the non-physical and physical instances of materials.
- Attributes = new MaterialAttributes[(int)MaterialAttributes.Material.NumberOfTypes * 2];
- }
-
- // This is where all the default material attributes are defined.
- public static void InitializeFromDefaults(ConfigurationParameters parms)
- {
- // public static string[] MaterialNames = { "Stone", "Metal", "Glass", "Wood",
- // "Flesh", "Plastic", "Rubber", "Light", "Avatar" };
- float dFriction = parms.defaultFriction;
- float dRestitution = parms.defaultRestitution;
- float dDensity = parms.defaultDensity;
- float dCcdM = parms.ccdMotionThreshold;
- float dCcdS = parms.ccdSweptSphereRadius;
- Attributes[(int)MaterialAttributes.Material.Stone] =
- new MaterialAttributes("stone",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Metal] =
- new MaterialAttributes("metal",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Glass] =
- new MaterialAttributes("glass",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Wood] =
- new MaterialAttributes("wood",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Flesh] =
- new MaterialAttributes("flesh",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Plastic] =
- new MaterialAttributes("plastic",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Rubber] =
- new MaterialAttributes("rubber",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Light] =
- new MaterialAttributes("light",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Avatar] =
- new MaterialAttributes("avatar",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
-
- Attributes[(int)MaterialAttributes.Material.Stone + (int)MaterialAttributes.Material.NumberOfTypes] =
- new MaterialAttributes("stonePhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Metal + (int)MaterialAttributes.Material.NumberOfTypes] =
- new MaterialAttributes("metalPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Glass + (int)MaterialAttributes.Material.NumberOfTypes] =
- new MaterialAttributes("glassPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Wood + (int)MaterialAttributes.Material.NumberOfTypes] =
- new MaterialAttributes("woodPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Flesh + (int)MaterialAttributes.Material.NumberOfTypes] =
- new MaterialAttributes("fleshPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Plastic + (int)MaterialAttributes.Material.NumberOfTypes] =
- new MaterialAttributes("plasticPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Rubber + (int)MaterialAttributes.Material.NumberOfTypes] =
- new MaterialAttributes("rubberPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Light + (int)MaterialAttributes.Material.NumberOfTypes] =
- new MaterialAttributes("lightPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- Attributes[(int)MaterialAttributes.Material.Avatar + (int)MaterialAttributes.Material.NumberOfTypes] =
- new MaterialAttributes("avatarPhysical",dDensity,dFriction,dRestitution, dCcdM, dCcdS);
- }
-
- // Under the [BulletSim] section, one can change the individual material
- // attribute values. The format of the configuration parameter is:
- // ["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)
- {
- int matType = 0;
- foreach (string matName in MaterialAttributes.MaterialNames)
- {
- foreach (string attribName in MaterialAttributes.MaterialAttribs)
- {
- string paramName = matName + attribName;
- if (pConfig.Contains(paramName))
- {
- float paramValue = pConfig.GetFloat(paramName);
- SetAttributeValue(matType, attribName, paramValue);
- // set the physical value also
- SetAttributeValue(matType + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
- }
- paramName += "Physical";
- if (pConfig.Contains(paramName))
- {
- float paramValue = pConfig.GetFloat(paramName);
- SetAttributeValue(matType + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
- }
- }
- matType++;
- }
- }
-
- private static void SetAttributeValue(int matType, string attribName, float val)
- {
- MaterialAttributes thisAttrib = Attributes[matType];
- FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName);
- if (fieldInfo != null)
- {
- fieldInfo.SetValue(thisAttrib, val);
- Attributes[matType] = thisAttrib;
- }
- }
-
- public static MaterialAttributes GetAttributes(MaterialAttributes.Material type, bool isPhysical)
- {
- int ind = (int)type;
- if (isPhysical) ind += (int)MaterialAttributes.Material.NumberOfTypes;
- return Attributes[ind];
- }
-
-}
-}
+/*
+ * 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.BulletSPlugin
+{
+
+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.
+ public static string[] MaterialNames = { "Stone", "Metal", "Glass", "Wood",
+ "Flesh", "Plastic", "Rubber", "Light", "Avatar" };
+ public static 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
+{
+ public static MaterialAttributes[] Attributes;
+
+ static BSMaterials()
+ {
+ // Attribute sets for both the non-physical and physical instances of materials.
+ Attributes = new MaterialAttributes[(int)MaterialAttributes.Material.NumberOfTypes * 2];
+ }
+
+ // 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
+ // public static string[] MaterialNames = { "Stone", "Metal", "Glass", "Wood",
+ // "Flesh", "Plastic", "Rubber", "Light", "Avatar" };
+ float dFriction = parms.defaultFriction;
+ float dRestitution = parms.defaultRestitution;
+ float dDensity = parms.defaultDensity;
+ 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",60f, 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.8f, 0.4f);
+ Attributes[(int)MaterialAttributes.Material.Glass + (int)MaterialAttributes.Material.NumberOfTypes] =
+ new MaterialAttributes("glassPhysical",dDensity, 0.8f, 0.7f);
+ Attributes[(int)MaterialAttributes.Material.Wood + (int)MaterialAttributes.Material.NumberOfTypes] =
+ new MaterialAttributes("woodPhysical",dDensity, 0.8f, 0.5f);
+ Attributes[(int)MaterialAttributes.Material.Flesh + (int)MaterialAttributes.Material.NumberOfTypes] =
+ new MaterialAttributes("fleshPhysical",dDensity, 0.8f, 0.3f);
+ Attributes[(int)MaterialAttributes.Material.Plastic + (int)MaterialAttributes.Material.NumberOfTypes] =
+ new MaterialAttributes("plasticPhysical",dDensity, 0.8f, 0.7f);
+ Attributes[(int)MaterialAttributes.Material.Rubber + (int)MaterialAttributes.Material.NumberOfTypes] =
+ new MaterialAttributes("rubberPhysical",dDensity, 0.8f, 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",60f, 0.2f, 0f);
+ }
+
+ // Under the [BulletSim] section, one can change the individual material
+ // attribute values. The format of the configuration parameter is:
+ // ["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)
+ {
+ int matType = 0;
+ foreach (string matName in MaterialAttributes.MaterialNames)
+ {
+ foreach (string attribName in MaterialAttributes.MaterialAttribs)
+ {
+ string paramName = matName + attribName;
+ if (pConfig.Contains(paramName))
+ {
+ float paramValue = pConfig.GetFloat(paramName);
+ SetAttributeValue(matType, attribName, paramValue);
+ // set the physical value also
+ SetAttributeValue(matType + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
+ }
+ paramName += "Physical";
+ if (pConfig.Contains(paramName))
+ {
+ float paramValue = pConfig.GetFloat(paramName);
+ SetAttributeValue(matType + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
+ }
+ }
+ matType++;
+ }
+ }
+
+ private static void SetAttributeValue(int matType, string attribName, float val)
+ {
+ MaterialAttributes thisAttrib = Attributes[matType];
+ FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName);
+ if (fieldInfo != null)
+ {
+ fieldInfo.SetValue(thisAttrib, val);
+ Attributes[matType] = thisAttrib;
+ }
+ }
+
+ public static MaterialAttributes GetAttributes(MaterialAttributes.Material type, bool isPhysical)
+ {
+ int ind = (int)type;
+ if (isPhysical) ind += (int)MaterialAttributes.Material.NumberOfTypes;
+ return Attributes[ind];
+ }
+
+}
+}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs
index 480da2c9ee..851d5081ef 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs
@@ -1,3 +1,30 @@
+/*
+ * 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;
@@ -7,6 +34,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
{
public abstract class BSMotor
{
+ // Timescales and other things can be turned off by setting them to 'infinite'.
+ public const float Infinite = 12345f;
+ public readonly static Vector3 InfiniteVector = new Vector3(BSMotor.Infinite, BSMotor.Infinite, BSMotor.Infinite);
+
public BSMotor(string useName)
{
UseName = useName;
@@ -15,7 +46,9 @@ public abstract class BSMotor
public virtual void Reset() { }
public virtual void Zero() { }
+ // 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)
@@ -30,10 +63,23 @@ public abstract class BSMotor
}
}
// Can all the incremental stepping be replaced with motor classes?
+
+// Motor which moves CurrentValue to TargetValue over TimeScale seconds.
+// The TargetValue is decays in TargetValueDecayTimeScale and
+// the CurrentValue will be held back by FrictionTimeScale.
+// TimeScale and TargetDelayTimeScale may be 'infinite' which means go 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 Vector3 FrameOfReference { get; set; }
+ // public Vector3 Offset { get; set; }
public float TimeScale { get; set; }
public float TargetValueDecayTimeScale { get; set; }
@@ -46,8 +92,9 @@ public class BSVMotor : BSMotor
public BSVMotor(string useName)
: base(useName)
{
- TimeScale = TargetValueDecayTimeScale = Efficiency = 1f;
- FrictionTimescale = Vector3.Zero;
+ TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite;
+ Efficiency = 1f;
+ FrictionTimescale = BSMotor.InfiniteVector;
CurrentValue = TargetValue = Vector3.Zero;
}
public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency)
@@ -67,6 +114,14 @@ public class BSVMotor : BSMotor
{
TargetValue = target;
}
+
+ // A form of stepping that does not take the time quantum into account.
+ // The caller must do the right thing later.
+ public Vector3 Step()
+ {
+ return Step(1f);
+ }
+
public Vector3 Step(float timeStep)
{
Vector3 returnCurrent = Vector3.Zero;
@@ -78,23 +133,36 @@ public class BSVMotor : BSMotor
// Addition = (desiredVector - currentAppliedVector) / secondsItShouldTakeToComplete
Vector3 addAmount = (TargetValue - CurrentValue)/TimeScale * timeStep;
CurrentValue += addAmount;
+
returnCurrent = CurrentValue;
- // The desired value reduces to zero when also reduces the difference with current.
- float decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep;
- TargetValue *= (1f - decayFactor);
+ // 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);
+ }
Vector3 frictionFactor = Vector3.Zero;
- frictionFactor = (Vector3.One / FrictionTimescale) * timeStep;
- CurrentValue *= (Vector3.One - frictionFactor);
+ 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) * timeStep;
+ frictionFactor.Y = FrictionTimescale.Y == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.Y) * timeStep;
+ frictionFactor.Z = FrictionTimescale.Z == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.Z) * timeStep;
+ CurrentValue *= (Vector3.One - frictionFactor);
+ }
- MDetailLog("{0},BSVMotor.Step,nonZero,{1},origTarget={2},origCurr={3},timeStep={4},timeScale={5},addAmnt={6},targetDecay={7},decayFact={8},fricTS={9},frictFact={10}",
- BSScene.DetailLogZero, UseName, origTarget, origCurrVal,
+ MDetailLog("{0}, BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},timeScale={5},addAmnt={6},targetDecay={7},decayFact={8},fricTS={9},frictFact={10}",
+ BSScene.DetailLogZero, UseName, origCurrVal, origTarget,
timeStep, TimeScale, addAmount,
TargetValueDecayTimeScale, decayFactor,
FrictionTimescale, frictionFactor);
- MDetailLog("{0},BSVMotor.Step,nonZero,{1},curr={2},target={3},add={4},decay={5},frict={6},ret={7}",
- BSScene.DetailLogZero, UseName, TargetValue, CurrentValue,
+ MDetailLog("{0}, BSVMotor.Step,nonZero,{1},curr={2},target={3},add={4},decay={5},frict={6},ret={7}",
+ BSScene.DetailLogZero, UseName, CurrentValue, TargetValue,
addAmount, decayFactor, frictionFactor, returnCurrent);
}
else
@@ -103,7 +171,7 @@ public class BSVMotor : BSMotor
CurrentValue = Vector3.Zero;
TargetValue = Vector3.Zero;
- MDetailLog("{0},BSVMotor.Step,zero,{1},curr={2},target={3},ret={4}",
+ MDetailLog("{0}, BSVMotor.Step,zero,{1},curr={2},target={3},ret={4}",
BSScene.DetailLogZero, UseName, TargetValue, CurrentValue, returnCurrent);
}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
index caa6c4696f..62aaf800c6 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
@@ -253,8 +253,9 @@ public sealed class BSPrim : BSPhysObject
// Zero some other properties in the physics engine
PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
{
- BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
- BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
+ // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity);
+ BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
+ BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
});
}
@@ -329,7 +330,7 @@ public sealed class BSPrim : BSPhysObject
if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
{
- float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position);
+ float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
// TODO: a floating motor so object will bob in the water
if (Math.Abs(Position.Z - waterHeight) > 0.1f)
{
@@ -347,7 +348,9 @@ public sealed class BSPrim : BSPhysObject
if (ret)
{
// Apply upforce and overcome gravity.
- AddForce(upForce - PhysicsScene.DefaultGravity, false, inTaintTime);
+ OMV.Vector3 correctionForce = upForce - PhysicsScene.DefaultGravity;
+ DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, _position, upForce, correctionForce);
+ AddForce(correctionForce, false, inTaintTime);
}
return ret;
}
@@ -643,9 +646,13 @@ public sealed class BSPrim : BSPhysObject
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
// Collision filter can be set only when the object is in the world
- if (PhysBody.collisionFilter != 0 || PhysBody.collisionMask != 0)
+ if (PhysBody.collisionGroup != 0 || PhysBody.collisionMask != 0)
{
- BulletSimAPI.SetCollisionFilterMask2(PhysBody.ptr, (uint)PhysBody.collisionFilter, (uint)PhysBody.collisionMask);
+ if (!BulletSimAPI.SetCollisionGroupMask2(PhysBody.ptr, (uint)PhysBody.collisionGroup, (uint)PhysBody.collisionMask))
+ {
+ PhysicsScene.Logger.ErrorFormat("{0} Failure setting prim collision mask. localID={1}, grp={2:X}, mask={3:X}",
+ LogHeader, LocalID, PhysBody.collisionGroup, PhysBody.collisionMask);
+ }
}
// Recompute any linkset parameters.
@@ -684,11 +691,11 @@ public sealed class BSPrim : BSPhysObject
// There can be special things needed for implementing linksets
Linkset.MakeStatic(this);
// The activation state is 'disabled' so Bullet will not try to act on it.
- BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
+ // BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
// Start it out sleeping and physical actions could wake it up.
- // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING);
+ BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ISLAND_SLEEPING);
- PhysBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter;
+ PhysBody.collisionGroup = CollisionFilterGroups.StaticObjectGroup;
PhysBody.collisionMask = CollisionFilterGroups.StaticObjectMask;
}
else
@@ -734,7 +741,7 @@ public sealed class BSPrim : BSPhysObject
BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ACTIVE_TAG);
// BulletSimAPI.Activate2(BSBody.ptr, true);
- PhysBody.collisionFilter = CollisionFilterGroups.ObjectFilter;
+ PhysBody.collisionGroup = CollisionFilterGroups.ObjectGroup;
PhysBody.collisionMask = CollisionFilterGroups.ObjectMask;
}
}
@@ -762,7 +769,7 @@ public sealed class BSPrim : BSPhysObject
m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType);
}
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
- PhysBody.collisionFilter = CollisionFilterGroups.VolumeDetectFilter;
+ PhysBody.collisionGroup = CollisionFilterGroups.VolumeDetectGroup;
PhysBody.collisionMask = CollisionFilterGroups.VolumeDetectMask;
}
}
@@ -838,15 +845,6 @@ public sealed class BSPrim : BSPhysObject
}
public override OMV.Vector3 RotationalVelocity {
get {
- /*
- OMV.Vector3 pv = OMV.Vector3.Zero;
- // if close to zero, report zero
- // This is copied from ODE but I'm not sure why it returns zero but doesn't
- // zero the property in the physics engine.
- if (_rotationalVelocity.ApproxEquals(pv, 0.2f))
- return pv;
- */
-
return _rotationalVelocity;
}
set {
@@ -1012,6 +1010,9 @@ public sealed class BSPrim : BSPhysObject
});
}
// A torque impulse.
+ // ApplyTorqueImpulse adds torque directly to the angularVelocity.
+ // AddAngularForce accumulates the force and applied it to the angular velocity all at once.
+ // Computed as: angularVelocity += impulse * inertia;
public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime)
{
OMV.Vector3 applyImpulse = impulse;
@@ -1380,54 +1381,16 @@ public sealed class BSPrim : BSPhysObject
public override void UpdateProperties(EntityProperties entprop)
{
- /*
- UpdatedProperties changed = 0;
- // assign to the local variables so the normal set action does not happen
- // if (_position != entprop.Position)
- if (!_position.ApproxEquals(entprop.Position, POSITION_TOLERANCE))
- {
- _position = entprop.Position;
- changed |= UpdatedProperties.Position;
- }
- // if (_orientation != entprop.Rotation)
- if (!_orientation.ApproxEquals(entprop.Rotation, ROTATION_TOLERANCE))
- {
- _orientation = entprop.Rotation;
- changed |= UpdatedProperties.Rotation;
- }
- // if (_velocity != entprop.Velocity)
- if (!_velocity.ApproxEquals(entprop.Velocity, VELOCITY_TOLERANCE))
- {
- _velocity = entprop.Velocity;
- changed |= UpdatedProperties.Velocity;
- }
- // if (_acceleration != entprop.Acceleration)
- if (!_acceleration.ApproxEquals(entprop.Acceleration, ACCELERATION_TOLERANCE))
- {
- _acceleration = entprop.Acceleration;
- changed |= UpdatedProperties.Acceleration;
- }
- // if (_rotationalVelocity != entprop.RotationalVelocity)
- if (!_rotationalVelocity.ApproxEquals(entprop.RotationalVelocity, ROTATIONAL_VELOCITY_TOLERANCE))
- {
- _rotationalVelocity = entprop.RotationalVelocity;
- changed |= UpdatedProperties.RotationalVel;
- }
- if (changed != 0)
- {
- // Only update the position of single objects and linkset roots
- if (Linkset.IsRoot(this))
- {
- base.RequestPhysicsterseUpdate();
- }
- }
- */
-
- // Don't check for damping here -- it's done in BulletSim and SceneObjectPart.
-
// Updates only for individual prims and for the root object of a linkset.
if (Linkset.IsRoot(this))
{
+ // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet
+ // TODO: handle physics introduced by Bullet with computed vehicle physics.
+ if (_vehicle.IsActive)
+ {
+ entprop.RotationalVelocity = OMV.Vector3.Zero;
+ }
+
// Assign directly to the local variables so the normal set action does not happen
_position = entprop.Position;
_orientation = entprop.Rotation;
@@ -1436,7 +1399,7 @@ public sealed class BSPrim : BSPhysObject
_rotationalVelocity = entprop.RotationalVelocity;
// The sanity check can change the velocity and/or position.
- if (PositionSanityCheck(true))
+ if (IsPhysical && PositionSanityCheck(true))
{
entprop.Position = _position;
entprop.Velocity = _velocity;
@@ -1446,12 +1409,10 @@ public sealed class BSPrim : BSPhysObject
LastEntityProperties = CurrentEntityProperties;
CurrentEntityProperties = entprop;
- OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation;
+ OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; // DEBUG DEBUG DEBUG
DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}",
LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity);
- // BulletSimAPI.DumpRigidBody2(PhysicsScene.World.ptr, BSBody.ptr); // DEBUG DEBUG DEBUG
-
base.RequestPhysicsterseUpdate();
}
/*
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index 805e670c1b..f72bd74476 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -96,6 +96,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
public long SimulationStep { get { return m_simulationStep; } }
private int m_taintsToProcessPerStep;
+ public delegate void PreStepAction(float timeStep);
+ public event PreStepAction BeforeStep;
+
// 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; }
@@ -127,7 +130,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
public const uint GROUNDPLANE_ID = 1;
public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
- private float m_waterLevel;
+ public float SimpleWaterLevel { get; set; }
public BSTerrainManager TerrainManager { get; private set; }
public ConfigurationParameters Params
@@ -182,6 +185,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
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; }
@@ -290,6 +294,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
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);
@@ -485,8 +490,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
ProcessTaints();
// Some of the prims operate with special vehicle properties
- ProcessVehicles(timeStep);
- ProcessTaints(); // the vehicles might have added taints
+ DoPreStepActions(timeStep);
+
+ // the prestep actions might have added taints
+ ProcessTaints();
// step the physical world one interval
m_simulationStep++;
@@ -515,9 +522,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
collidersCount = 0;
}
- // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in
+ // 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
+ // 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.
@@ -563,6 +570,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
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)
@@ -586,9 +594,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
// 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.
- // We multiply by 55 to give a recognizable running rate (55 or less).
- return numSubSteps * m_fixedTimeStep * 1000 * 55;
- // return timeStep * 1000 * 55;
+ // Multiply by 55 to give a nominal frame rate of 55.
+ return (float)numSubSteps * m_fixedTimeStep * 1000f * 55f;
}
// Something has collided
@@ -634,12 +641,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
public override void SetWaterLevel(float baseheight)
{
- m_waterLevel = baseheight;
- }
- // Someday....
- public float GetWaterLevelAtXYZ(Vector3 loc)
- {
- return m_waterLevel;
+ SimpleWaterLevel = baseheight;
}
public override void DeleteTerrain()
@@ -910,6 +912,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
}
}
+ private void DoPreStepActions(float timeStep)
+ {
+ ProcessVehicles(timeStep);
+
+ PreStepAction actions = BeforeStep;
+ if (actions != null)
+ actions(timeStep);
+
+ }
+
// Some prims have extra vehicle actions
// Called at taint time!
private void ProcessVehicles(float timeStep)
@@ -974,6 +986,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
// 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
@@ -1172,7 +1186,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
(s) => { return s.m_params[0].avatarFriction; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ),
new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
- 0.99f,
+ 10.0f,
(s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].avatarStandingFriction; },
(s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ),
@@ -1493,7 +1507,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
{
PhysicsLogging.Write(msg, args);
// Add the Flush() if debugging crashes. Gets all the messages written out.
- // PhysicsLogging.Flush();
+ 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";
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
index 892c34bbb0..e77b6bad6d 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
@@ -65,9 +65,16 @@ public sealed class BSShapeCollection : IDisposable
private Dictionary Meshes = new Dictionary();
private Dictionary Hulls = new Dictionary();
+ private bool DDetail = false;
+
public BSShapeCollection(BSScene physScene)
{
PhysicsScene = physScene;
+ // Set the next to 'true' for very detailed shape update detailed logging (detailed details?)
+ // While detailed debugging is still active, this is better than commenting out all the
+ // DetailLog statements. When debugging slows down, this and the protected logging
+ // statements can be commented/removed.
+ DDetail = true;
}
public void Dispose()
@@ -126,13 +133,13 @@ public sealed class BSShapeCollection : IDisposable
{
lock (m_collectionActivityLock)
{
- DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body);
+ if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body);
PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.ReferenceBody", delegate()
{
if (!BulletSimAPI.IsInWorld2(body.ptr))
{
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr);
- DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
+ if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
}
});
}
@@ -149,7 +156,7 @@ public sealed class BSShapeCollection : IDisposable
{
PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceBody", delegate()
{
- DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1},inTaintTime={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1},inTaintTime={2}",
body.ID, body, inTaintTime);
// If the caller needs to know the old body is going away, pass the event up.
if (bodyCallback != null) bodyCallback(body);
@@ -157,7 +164,7 @@ public sealed class BSShapeCollection : IDisposable
if (BulletSimAPI.IsInWorld2(body.ptr))
{
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
- DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body);
+ if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body);
}
// Zero any reference to the shape so it is not freed when the body is deleted.
@@ -184,7 +191,7 @@ public sealed class BSShapeCollection : IDisposable
{
// There is an existing instance of this mesh.
meshDesc.referenceCount++;
- DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
}
else
@@ -194,7 +201,7 @@ public sealed class BSShapeCollection : IDisposable
meshDesc.shapeKey = shape.shapeKey;
// We keep a reference to the underlying IMesh data so a hull can be built
meshDesc.referenceCount = 1;
- DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
ret = true;
}
@@ -207,7 +214,7 @@ public sealed class BSShapeCollection : IDisposable
{
// There is an existing instance of this hull.
hullDesc.referenceCount++;
- DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
}
else
@@ -216,7 +223,7 @@ public sealed class BSShapeCollection : IDisposable
hullDesc.ptr = shape.ptr;
hullDesc.shapeKey = shape.shapeKey;
hullDesc.referenceCount = 1;
- DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
ret = true;
@@ -246,7 +253,7 @@ public sealed class BSShapeCollection : IDisposable
if (shape.isNativeShape)
{
// Native shapes are not tracked and are released immediately
- DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime);
if (shapeCallback != null) shapeCallback(shape);
BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
@@ -286,7 +293,7 @@ public sealed class BSShapeCollection : IDisposable
if (shapeCallback != null) shapeCallback(shape);
meshDesc.lastReferenced = System.DateTime.Now;
Meshes[shape.shapeKey] = meshDesc;
- DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}",
BSScene.DetailLogZero, shape, meshDesc.referenceCount);
}
@@ -307,7 +314,7 @@ public sealed class BSShapeCollection : IDisposable
hullDesc.lastReferenced = System.DateTime.Now;
Hulls[shape.shapeKey] = hullDesc;
- DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}",
BSScene.DetailLogZero, shape, hullDesc.referenceCount);
}
}
@@ -325,13 +332,13 @@ public sealed class BSShapeCollection : IDisposable
// Failed the sanity check!!
PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}",
LogHeader, shape.type, shape.ptr.ToString("X"));
- DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}",
BSScene.DetailLogZero, shape.type, shape.ptr.ToString("X"));
return;
}
int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr);
- DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren);
+ if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren);
for (int ii = numChildren - 1; ii >= 0; ii--)
{
@@ -379,7 +386,7 @@ public sealed class BSShapeCollection : IDisposable
}
}
- DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo);
+ if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo);
if (shapeInfo.type != BSPhysicsShapeType.SHAPE_UNKNOWN)
{
@@ -410,7 +417,7 @@ public sealed class BSShapeCollection : IDisposable
// an avatar capsule is close to a native shape (it is not shared)
ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_CAPSULE,
FixedShapeKey.KEY_CAPSULE, shapeCallback);
- DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape);
+ if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape);
ret = true;
haveShape = true;
}
@@ -420,7 +427,7 @@ public sealed class BSShapeCollection : IDisposable
if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND)
{
ret = GetReferenceToCompoundShape(prim, shapeCallback);
- DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape);
+ if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape);
haveShape = true;
}
@@ -465,7 +472,7 @@ public sealed class BSShapeCollection : IDisposable
{
ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE,
FixedShapeKey.KEY_SPHERE, shapeCallback);
- DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
prim.LocalID, forceRebuild, prim.PhysShape);
}
}
@@ -479,7 +486,7 @@ public sealed class BSShapeCollection : IDisposable
{
ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX,
FixedShapeKey.KEY_BOX, shapeCallback);
- DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
prim.LocalID, forceRebuild, prim.PhysShape);
}
}
@@ -504,13 +511,13 @@ public sealed class BSShapeCollection : IDisposable
{
// Update prim.BSShape to reference a hull of this shape.
ret = GetReferenceToHull(prim,shapeCallback);
- DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}",
prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
}
else
{
ret = GetReferenceToMesh(prim, shapeCallback);
- DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}",
prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
}
return ret;
@@ -528,7 +535,7 @@ public sealed class BSShapeCollection : IDisposable
BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey);
// Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
- DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
prim.LocalID, newShape, prim.Scale);
prim.PhysShape = newShape;
@@ -554,7 +561,7 @@ public sealed class BSShapeCollection : IDisposable
newShape = new BulletShape(
BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale)
, shapeType);
- DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
+ if (DDetail) DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
}
else
{
@@ -589,7 +596,7 @@ public sealed class BSShapeCollection : IDisposable
if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_MESH)
return false;
- DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}",
prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"));
// Since we're recreating new, get rid of the reference to the previous shape
@@ -620,8 +627,7 @@ public sealed class BSShapeCollection : IDisposable
}
else
{
- // Pass false for physicalness as this creates some sort of bounding box which we don't need
- meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
+ meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false);
if (meshData != null)
{
@@ -663,7 +669,7 @@ public sealed class BSShapeCollection : IDisposable
if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_HULL)
return false;
- DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}",
prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X"));
// Remove usage of the previous shape.
@@ -694,8 +700,8 @@ public sealed class BSShapeCollection : IDisposable
else
{
// Build a new hull in the physical world
- // Pass false for physicalness as this creates some sort of bounding box which we don't need
- IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
+ // Pass true for physicalness as this creates some sort of bounding box which we don't need
+ IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false);
if (meshData != null)
{
@@ -809,7 +815,7 @@ public sealed class BSShapeCollection : IDisposable
// Create the shape for the root prim and add it to the compound shape. Cannot be a native shape.
CreateGeomMeshOrHull(prim, shapeCallback);
BulletSimAPI.AddChildShapeToCompoundShape2(cShape.ptr, prim.PhysShape.ptr, OMV.Vector3.Zero, OMV.Quaternion.Identity);
- DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}",
+ if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}",
prim.LocalID, cShape, prim.PhysShape);
prim.PhysShape = cShape;
@@ -936,13 +942,13 @@ public sealed class BSShapeCollection : IDisposable
{
bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
prim.LocalID, prim.RawPosition, prim.RawOrientation);
- DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
+ if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
}
else
{
bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
prim.LocalID, prim.RawPosition, prim.RawOrientation);
- DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
+ if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
}
aBody = new BulletBody(prim.LocalID, bodyPtr);
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs
index 1450f66491..83b9c37104 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs
@@ -121,8 +121,8 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
// redo its bounding box now that it is in the world
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
- BulletSimAPI.SetCollisionFilterMask2(m_mapInfo.terrainBody.ptr,
- (uint)CollisionFilterGroups.TerrainFilter,
+ BulletSimAPI.SetCollisionGroupMask2(m_mapInfo.terrainBody.ptr,
+ (uint)CollisionFilterGroups.TerrainGroup,
(uint)CollisionFilterGroups.TerrainMask);
// Make it so the terrain will not move or be considered for movement.
@@ -148,7 +148,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
}
// The passed position is relative to the base of the region.
- public override float GetHeightAtXYZ(Vector3 pos)
+ public override float GetTerrainHeightAtXYZ(Vector3 pos)
{
float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
@@ -166,5 +166,11 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
}
return ret;
}
+
+ // The passed position is relative to the base of the region.
+ public override float GetWaterLevelAtXYZ(Vector3 pos)
+ {
+ return PhysicsScene.SimpleWaterLevel;
+ }
}
}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
index cd623f1398..83df360434 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
@@ -62,7 +62,8 @@ public abstract class BSTerrainPhys : IDisposable
ID = id;
}
public abstract void Dispose();
- public abstract float GetHeightAtXYZ(Vector3 pos);
+ public abstract float GetTerrainHeightAtXYZ(Vector3 pos);
+ public abstract float GetWaterLevelAtXYZ(Vector3 pos);
}
// ==========================================================================================
@@ -75,6 +76,7 @@ public sealed class BSTerrainManager
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.
@@ -138,8 +140,8 @@ public sealed class BSTerrainManager
// Ground plane does not move
BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION);
// Everything collides with the ground plane.
- BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr,
- (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask);
+ BulletSimAPI.SetCollisionGroupMask2(m_groundPlane.ptr,
+ (uint)CollisionFilterGroups.GroundPlaneGroup, (uint)CollisionFilterGroups.GroundPlaneMask);
// 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);
@@ -358,7 +360,7 @@ public sealed class BSTerrainManager
BSTerrainPhys physTerrain;
if (m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain))
{
- ret = physTerrain.GetHeightAtXYZ(loc - terrainBaseXYZ);
+ ret = physTerrain.GetTerrainHeightAtXYZ(loc - terrainBaseXYZ);
}
else
{
@@ -370,6 +372,33 @@ public sealed class BSTerrainManager
return ret;
}
+ public float GetWaterLevelAtXYZ(Vector3 pos)
+ {
+ float ret = WATER_HEIGHT_GETHEIGHT_RET;
+
+ float tX = pos.X;
+ float tY = pos.Y;
+
+ Vector3 terrainBaseXYZ = Vector3.Zero;
+ terrainBaseXYZ.X = ((int)(tX / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
+ terrainBaseXYZ.Y = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
+
+ lock (m_terrains)
+ {
+ BSTerrainPhys physTerrain;
+ if (m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain))
+ {
+ ret = physTerrain.GetWaterLevelAtXYZ(pos);
+ }
+ else
+ {
+ PhysicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
+ LogHeader, PhysicsScene.RegionName, tX, tY);
+ }
+ }
+ return ret;
+ }
+
// Although no one seems to check this, I do support combining.
public bool SupportsCombining()
{
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs
index d7afdeb038..6ce767ddad 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs
@@ -88,9 +88,11 @@ public sealed class BSTerrainMesh : BSTerrainPhys
// 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),
+ indicesCount, indices, verticesCount, vertices),
BSPhysicsShapeType.SHAPE_MESH);
if (m_terrainShape.ptr == IntPtr.Zero)
{
@@ -122,14 +124,14 @@ public sealed class BSTerrainMesh : BSTerrainPhys
// Static objects are not very massive.
BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero);
- // Return the new terrain to the world of physical objects
+ // Put the new terrain to the world of physical objects
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
- // redo its bounding box now that it is in the world
+ // Redo its bounding box now that it is in the world
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr);
- BulletSimAPI.SetCollisionFilterMask2(m_terrainBody.ptr,
- (uint)CollisionFilterGroups.TerrainFilter,
+ BulletSimAPI.SetCollisionGroupMask2(m_terrainBody.ptr,
+ (uint)CollisionFilterGroups.TerrainGroup,
(uint)CollisionFilterGroups.TerrainMask);
// Make it so the terrain will not move or be considered for movement.
@@ -146,7 +148,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
}
}
- public override float GetHeightAtXYZ(Vector3 pos)
+ 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.
@@ -167,6 +169,12 @@ public sealed class BSTerrainMesh : BSTerrainPhys
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(
@@ -188,6 +196,11 @@ public sealed class BSTerrainMesh : BSTerrainPhys
// 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.
@@ -200,16 +213,18 @@ public sealed class BSTerrainMesh : BSTerrainPhys
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 got through sizeX + 1 times
+ for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we go around sizeX + 1 times
{
int offset = yy * sizeX + xx;
- // Extend the height from the height from the last row or column
+ // 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;
@@ -222,7 +237,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
{
for (int xx = 0; xx < sizeX; xx++)
{
- int offset = yy * 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;
@@ -233,6 +248,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
indicesCount += 6;
}
}
+
ret = true;
}
catch (Exception e)
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
index 12baee9ff5..2671995dd2 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
@@ -57,12 +57,12 @@ public struct BulletBody
{
ID = id;
ptr = xx;
- collisionFilter = 0;
+ collisionGroup = 0;
collisionMask = 0;
}
public IntPtr ptr;
public uint ID;
- public CollisionFilterGroups collisionFilter;
+ public CollisionFilterGroups collisionGroup;
public CollisionFilterGroups collisionMask;
public override string ToString()
{
@@ -71,10 +71,10 @@ public struct BulletBody
buff.Append(ID.ToString());
buff.Append(",p=");
buff.Append(ptr.ToString("X"));
- if (collisionFilter != 0 || collisionMask != 0)
+ if (collisionGroup != 0 || collisionMask != 0)
{
- buff.Append(",f=");
- buff.Append(collisionFilter.ToString("X"));
+ buff.Append(",g=");
+ buff.Append(collisionGroup.ToString("X"));
buff.Append(",m=");
buff.Append(collisionMask.ToString("X"));
}
@@ -357,9 +357,10 @@ public enum CollisionFlags : uint
CF_CHARACTER_OBJECT = 1 << 4,
CF_DISABLE_VISUALIZE_OBJECT = 1 << 5,
CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6,
- // Following used by BulletSim to control collisions
+ // Following used by BulletSim to control collisions and updates
BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10,
BS_FLOATS_ON_WATER = 1 << 11,
+ BS_VEHICLE_COLLISIONS = 1 << 12,
BS_NONE = 0,
BS_ALL = 0xFFFFFFFF,
@@ -376,36 +377,36 @@ public enum CollisionFilterGroups : uint
// Don't use the bit definitions!! Define the use in a
// filter/mask definition below. This way collision interactions
// are more easily debugged.
- BNoneFilter = 0,
- BDefaultFilter = 1 << 0,
- BStaticFilter = 1 << 1,
- BKinematicFilter = 1 << 2,
- BDebrisFilter = 1 << 3,
- BSensorTrigger = 1 << 4,
- BCharacterFilter = 1 << 5,
- BAllFilter = 0xFFFFFFFF,
+ BNoneGroup = 0,
+ BDefaultGroup = 1 << 0,
+ BStaticGroup = 1 << 1,
+ BKinematicGroup = 1 << 2,
+ BDebrisGroup = 1 << 3,
+ BSensorTrigger = 1 << 4,
+ BCharacterGroup = 1 << 5,
+ BAllGroup = 0xFFFFFFFF,
// Filter groups defined by BulletSim
- BGroundPlaneFilter = 1 << 10,
- BTerrainFilter = 1 << 11,
- BRaycastFilter = 1 << 12,
- BSolidFilter = 1 << 13,
- BLinksetFilter = 1 << 14,
+ BGroundPlaneGroup = 1 << 10,
+ BTerrainGroup = 1 << 11,
+ BRaycastGroup = 1 << 12,
+ BSolidGroup = 1 << 13,
+ BLinksetGroup = 1 << 14,
// The collsion filters and masked are defined in one place -- don't want them scattered
- AvatarFilter = BCharacterFilter,
- AvatarMask = BAllFilter,
- ObjectFilter = BSolidFilter,
- ObjectMask = BAllFilter,
- StaticObjectFilter = BStaticFilter,
- StaticObjectMask = BAllFilter & ~BStaticFilter, // static objects don't collide with each other
- LinksetFilter = BLinksetFilter,
- LinksetMask = BAllFilter & ~BLinksetFilter, // linkset objects don't collide with each other
- VolumeDetectFilter = BSensorTrigger,
+ AvatarGroup = BCharacterGroup,
+ AvatarMask = BAllGroup,
+ ObjectGroup = BSolidGroup,
+ ObjectMask = BAllGroup,
+ StaticObjectGroup = BStaticGroup,
+ StaticObjectMask = AvatarGroup | ObjectGroup, // static things don't interact with much
+ LinksetGroup = BLinksetGroup,
+ LinksetMask = BAllGroup & ~BLinksetGroup, // linkset objects don't collide with each other
+ VolumeDetectGroup = BSensorTrigger,
VolumeDetectMask = ~BSensorTrigger,
- TerrainFilter = BTerrainFilter,
- TerrainMask = BAllFilter & ~BStaticFilter, // static objects on the ground don't collide
- GroundPlaneFilter = BGroundPlaneFilter,
- GroundPlaneMask = BAllFilter
+ TerrainGroup = BTerrainGroup,
+ TerrainMask = BAllGroup & ~BStaticGroup, // static objects on the ground don't collide
+ GroundPlaneGroup = BGroundPlaneGroup,
+ GroundPlaneMask = BAllGroup
};
@@ -945,7 +946,7 @@ public static extern IntPtr GetConstraintRef2(IntPtr obj, int index);
public static extern int GetNumConstraintRefs2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
-public static extern void SetCollisionFilterMask2(IntPtr body, uint filter, uint mask);
+public static extern bool SetCollisionGroupMask2(IntPtr body, uint filter, uint mask);
// =====================================================================================
// btCollisionShape entries
@@ -1006,14 +1007,17 @@ public static extern void DumpRigidBody2(IntPtr sim, IntPtr collisionObject);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpCollisionShape2(IntPtr sim, IntPtr collisionShape);
+[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
+public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo);
+
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpConstraint2(IntPtr sim, IntPtr constrain);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
-public static extern void DumpAllInfo2(IntPtr sim);
+public static extern void DumpActivationInfo2(IntPtr sim);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
-public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo);
+public static extern void DumpAllInfo2(IntPtr sim);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpPhysicsStatistics2(IntPtr sim);
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt
new file mode 100755
index 0000000000..d51003c40b
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt
@@ -0,0 +1,142 @@
+CRASHES
+=================================================
+20121129.1411: editting/moving phys object across region boundries causes crash
+ getPos-> btRigidBody::upcast -> getBodyType -> BOOM
+20121128.1600: mesh object not rezzing (no physics mesh).
+ Causes many errors. Doesn't stop after first error with box shape.
+ Eventually crashes when deleting the object.
+
+VEHICLES TODO LIST:
+=================================================
+Neb car jiggling left and right
+ Happens on terrain and any other mesh object. Flat cubes are much smoother.
+Vehicles (Move smoothly)
+Add vehicle collisions so IsColliding is properly reported.
+ Needed for banking, limitMotorUp, movementLimiting, ...
+Some vehicles should not be able to turn if no speed or off ground.
+For limitMotorUp, use raycast down to find if vehicle is in the air.
+Implement function efficiency for lineaar and angular motion.
+Should vehicle angular/linear movement friction happen after all the components
+ or does it only apply to the basic movement?
+After getting off a vehicle, the root prim is phantom (can be walked through)
+ Need to force a position update for the root prim after compound shape destruction
+Linkset explosion after three "rides" on Nebadon lite vehicle (LinksetConstraint)
+Implement referenceFrame for all the motion routines.
+Cannot edit/move a vehicle being ridden: it jumps back to the origional position.
+
+BULLETSIM TODO LIST:
+=================================================
+Disable activity of passive linkset children.
+ Since the linkset is a compound object, the old prims are left lying
+ around and need to be phantomized so they don't collide, ...
+Scenes with hundred of thousands of static objects take a lot of physics CPU time.
+BSPrim.Force should set a continious force on the prim. The force should be
+ applied each tick. Some limits?
+Single prim vehicles don't seem to properly vehiclize.
+Gun sending shooter flying.
+Collision margin (gap between physical objects lying on each other)
+Boundry checking (crashes related to crossing boundry)
+ Add check for border edge position for avatars and objects.
+ Verify the events are created for border crossings.
+Avatar rotation (check out changes to ScenePresence for physical rotation)
+Avatar running (what does phys engine need to do?)
+Small physical objects do not interact correctly
+ Create chain of .5x.5x.1 torui and make all but top physical so to hang.
+ The chain will fall apart and pairs will dance around on ground
+ Chains of 1x1x.2 will stay connected but will dance.
+ Chains above 2x2x.4 are move stable and get stablier as torui get larger.
+Add material type linkage and input all the material property definitions.
+ Skeleton classes and table are in the sources but are not filled or used.
+Add PID motor for avatar movement (slow to stop, ...)
+setForce should set a constant force. Different than AddImpulse.
+Implement raycast.
+Implement ShapeCollection.Dispose()
+Implement water as a plain so raycasting and collisions can happen with same.
+
+Find/remove avatar collision with ID=0.
+Test avatar walking up stairs. How does compare with SL.
+ Radius of the capsule affects ability to climb edges.
+Tune terrain/object friction to be closer to SL.
+Debounce avatar contact so legs don't keep folding up when standing.
+Implement LSL physics controls. Like STATUS_ROTATE_X.
+Add border extensions to terrain to help region crossings and objects leaving region.
+
+Speed up creation of large physical linksets
+ For instance, sitting in Neb's car (130 prims) takes several seconds to become physical
+Performance test with lots of avatars. Can BulletSim support a thousand?
+Optimize collisions in C++: only send up to the object subscribed to collisions.
+ Use collision subscription and remove the collsion(A,B) and collision(B,A)
+Check whether SimMotionState needs large if statement (see TODO).
+
+Implement 'top colliders' info.
+Avatar jump
+Performance measurement and changes to make quicker.
+Implement detailed physics stats (GetStats()).
+
+Eliminate collisions between objects in a linkset. (LinksetConstraint)
+ Have UserPointer point to struct with localID and linksetID?
+ Objects in original linkset still collide with each other?
+
+Measure performance improvement from hulls
+Test not using ghost objects for volume detect implementation.
+Performance of closures and delegates for taint processing
+ Are there faster ways?
+ Is any slowdown introduced by the existing implementation significant?
+Is there are more efficient method of implementing pre and post step actions?
+ See http://www.codeproject.com/Articles/29922/Weak-Events-in-C
+
+Physics Arena central pyramid: why is one side permiable?
+
+INTERNAL IMPROVEMENT/CLEANUP
+=================================================
+Remove unused fields from ShapeData (not used in API2)
+Breakout code for mesh/hull/compound/native into separate BSShape* classes
+ Standardize access to building and reference code.
+ The skeleton classes are in the sources but are not complete or linked in.
+Generalize Dynamics and PID with standardized motors.
+Generalize Linkset and vehicles into PropertyManagers
+ Methods for Refresh, RemoveBodyDependencies, RestoreBodyDependencies
+ Possibly generalized a 'pre step action' registration.
+Complete implemention of preStepActions
+ Replace vehicle step call with prestep event.
+ Is there a need for postStepActions? postStepTaints?
+Implement linkset by setting position of children when root updated. (LinksetManual)
+ Linkset implementation using manual prim movement.
+LinkablePrim class? Would that simplify/centralize the linkset logic?
+BSScene.UpdateParameterSet() is broken. How to set params on objects?
+Remove HeightmapInfo from terrain specification.
+ Since C++ code does not need terrain height, this structure et al are not needed.
+Add floating motor for BS_FLOATS_ON_WATER so prim and avatar will
+ bob at the water level. BSPrim.PositionSanityCheck().
+
+THREADING
+=================================================
+Do taint action immediately if not actually executing Bullet.
+ Add lock around Bullet execution and just do taint actions if simulation is not happening.
+
+DONE DONE DONE DONE
+=================================================
+Cleanup code in BSDynamics by using motors. (Resolution: started)
+Consider implementing terrain with a mesh rather than heightmap. (Resolution: done)
+ Would have better and adjustable resolution.
+Build terrain mesh so heighmap is height of the center of the square meter.
+ Resolution: NOT DONE: SL and ODE define meter square as being at one corner with one diagional.
+Terrain as mesh. (Resolution: done)
+How are static linksets seen by the physics engine?
+ Resolution: they are not linked in physics. When moved, all the children are repositioned.
+Convert BSCharacter to use all API2 (Resolution: done)
+Avatar pushing difficult (too heavy?)
+Use asset service passed to BulletSim to get sculptie bodies, etc. (Resolution: done)
+Remove old code in DLL (all non-API2 stuff). (Resolution: done)
+Measurements of mega-physical prim performance (with graph) (Resolution: done, email)
+Debug Bullet internal stats output (why is timing all wrong?)
+ Resolution: Bullet stats logging only works with a single instance of Bullet (one region).
+Implement meshes or just verify that they work. (Resolution: they do!)
+Do prim hash codes work for sculpties and meshes? (Resolution: yes)
+Linkset implementation using compound shapes. (Resolution: implemented LinksetCompound)
+ Compound shapes will need the LocalID in the shapes and collision
+ processing to get it from there.
+Light cycle falling over when driving (Resolution: implemented VerticalAttractor)
+Light cycle not banking (Resolution: It doesn't. Banking is roll adding yaw.)
+Package Bullet source mods for Bullet internal stats output
+ (Resolution: move code into WorldData.h rather than relying on patches)
\ No newline at end of file
diff --git a/OpenSim/Region/Physics/Manager/IMesher.cs b/OpenSim/Region/Physics/Manager/IMesher.cs
index 3a9ca1bd34..10c4bd3ae6 100644
--- a/OpenSim/Region/Physics/Manager/IMesher.cs
+++ b/OpenSim/Region/Physics/Manager/IMesher.cs
@@ -36,6 +36,7 @@ namespace OpenSim.Region.Physics.Manager
{
IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod);
IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical);
+ IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache);
}
// Values for level of detail to be passed to the mesher.
diff --git a/OpenSim/Region/Physics/Manager/ZeroMesher.cs b/OpenSim/Region/Physics/Manager/ZeroMesher.cs
index ba19db6c18..270d2eca0c 100644
--- a/OpenSim/Region/Physics/Manager/ZeroMesher.cs
+++ b/OpenSim/Region/Physics/Manager/ZeroMesher.cs
@@ -64,10 +64,15 @@ namespace OpenSim.Region.Physics.Manager
{
public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
{
- return CreateMesh(primName, primShape, size, lod, false);
+ return CreateMesh(primName, primShape, size, lod, false, false);
}
public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
+ {
+ return CreateMesh(primName, primShape, size, lod, false, false);
+ }
+
+ public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache)
{
// Remove the reference to the encoded JPEG2000 data so it can be GCed
primShape.SculptData = OpenMetaverse.Utils.EmptyBytes;
diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
index 3bd15ce5af..8145d61c6f 100644
--- a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
+++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
@@ -321,7 +321,10 @@ namespace OpenSim.Region.Physics.Meshing
if (primShape.SculptData.Length <= 0)
{
- m_log.ErrorFormat("[MESH]: asset data for {0} is zero length", primName);
+ // XXX: At the moment we can not log here since ODEPrim, for instance, ends up triggering this
+ // method twice - once before it has loaded sculpt data from the asset service and once afterwards.
+ // The first time will always call with unloaded SculptData if this needs to be uploaded.
+// m_log.ErrorFormat("[MESH]: asset data for {0} is zero length", primName);
return false;
}
@@ -699,11 +702,16 @@ namespace OpenSim.Region.Physics.Meshing
public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
{
- return CreateMesh(primName, primShape, size, lod, false);
+ return CreateMesh(primName, primShape, size, lod, false, true);
}
public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
{
+ return CreateMesh(primName, primShape, size, lod, isPhysical, true);
+ }
+
+ public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache)
+ {
#if SPAM
m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName);
#endif
@@ -713,9 +721,12 @@ namespace OpenSim.Region.Physics.Meshing
// If this mesh has been created already, return it instead of creating another copy
// For large regions with 100k+ prims and hundreds of copies of each, this can save a GB or more of memory
- key = primShape.GetMeshKey(size, lod);
- if (m_uniqueMeshes.TryGetValue(key, out mesh))
- return mesh;
+ if (shouldCache)
+ {
+ key = primShape.GetMeshKey(size, lod);
+ if (m_uniqueMeshes.TryGetValue(key, out mesh))
+ return mesh;
+ }
if (size.X < 0.01f) size.X = 0.01f;
if (size.Y < 0.01f) size.Y = 0.01f;
@@ -738,7 +749,10 @@ namespace OpenSim.Region.Physics.Meshing
// trim the vertex and triangle lists to free up memory
mesh.TrimExcess();
- m_uniqueMeshes.Add(key, mesh);
+ if (shouldCache)
+ {
+ m_uniqueMeshes.Add(key, mesh);
+ }
}
return mesh;
diff --git a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
index 5a0b8d1fe1..0d66496338 100644
--- a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
+++ b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
@@ -3361,6 +3361,11 @@ Console.WriteLine(" JointCreateFixed");
_pbs.SculptData = new byte[asset.Data.Length];
asset.Data.CopyTo(_pbs.SculptData, 0);
// m_assetFailed = false;
+
+// m_log.DebugFormat(
+// "[ODE PRIM]: Received mesh/sculpt data asset {0} with {1} bytes for {2} at {3} in {4}",
+// _pbs.SculptTexture, _pbs.SculptData.Length, Name, _position, _parent_scene.Name);
+
m_taintshape = true;
_parent_scene.AddPhysicsActorTaint(this);
}
diff --git a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs
index b04f6b6624..0cef550749 100644
--- a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs
+++ b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs
@@ -114,6 +114,16 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
UUID AssetID { get; }
Queue EventQueue { get; }
+ ///
+ /// Number of events queued for processing.
+ ///
+ long EventsQueued { get; }
+
+ ///
+ /// Number of events processed by this script instance.
+ ///
+ long EventsProcessed { get; }
+
void ClearQueue();
int StartParam { get; set; }
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
index acf4d8c1d2..4108588163 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
@@ -6856,6 +6856,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public void llCloseRemoteDataChannel(string channel)
{
m_host.AddScriptLPS(1);
+
+ IXmlRpcRouter xmlRpcRouter = m_ScriptEngine.World.RequestModuleInterface();
+ if (xmlRpcRouter != null)
+ {
+ xmlRpcRouter.UnRegisterReceiver(channel, m_item.ItemID);
+ }
+
IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface();
xmlrpcMod.CloseXMLRPCChannel((UUID)channel);
ScriptSleep(1000);
diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
index 5793cc9de6..5ad6eebbc9 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
@@ -173,6 +173,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
public Queue EventQueue { get; private set; }
+ public long EventsQueued
+ {
+ get
+ {
+ lock (EventQueue)
+ return EventQueue.Count;
+ }
+ }
+
+ public long EventsProcessed { get; private set; }
+
public int StartParam { get; set; }
public TaskInventoryItem ScriptTask { get; private set; }
@@ -774,6 +785,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
ChatTypeEnum.DebugChannel, 2147483647,
part.AbsolutePosition,
part.Name, part.UUID, false);
+
+
+ m_log.DebugFormat(
+ "[SCRIPT INSTANCE]: Runtime error in script {0}, part {1} {2} at {3} in {4}, displayed error {5}, actual exception {6}",
+ ScriptName,
+ PrimName,
+ part.UUID,
+ part.AbsolutePosition,
+ part.ParentGroup.Scene.Name,
+ text.Replace("\n", "\\n"),
+ e.InnerException);
}
catch (Exception)
{
@@ -808,6 +830,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
// script engine to run the next event.
lock (EventQueue)
{
+ EventsProcessed++;
+
if (EventQueue.Count > 0 && Running && !ShuttingDown)
{
m_CurrentWorkItem = Engine.QueueEventHandler(this);
@@ -1013,7 +1037,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
"({0}): {1}", scriptLine - 1,
e.InnerException.Message);
- System.Console.WriteLine(e.ToString()+"\n");
return message;
}
}
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiInventoryTests.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiInventoryTests.cs
index 2c9d9e8614..cb7291adb9 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiInventoryTests.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiInventoryTests.cs
@@ -57,8 +57,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
protected XEngine.XEngine m_engine;
[SetUp]
- public void SetUp()
+ public override void SetUp()
{
+ base.SetUp();
+
IConfigSource initConfigSource = new IniConfigSource();
IConfig config = initConfigSource.AddConfig("XEngine");
config.Set("Enabled", "true");
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiLinkingTests.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiLinkingTests.cs
index 57f19b950d..d9b17d774f 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiLinkingTests.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiLinkingTests.cs
@@ -62,8 +62,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
protected XEngine.XEngine m_engine;
[SetUp]
- public void SetUp()
+ public override void SetUp()
{
+ base.SetUp();
+
IConfigSource initConfigSource = new IniConfigSource();
IConfig config = initConfigSource.AddConfig("XEngine");
config.Set("Enabled", "true");
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiListTests.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiListTests.cs
index 182b07b484..98017d86e7 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiListTests.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiListTests.cs
@@ -51,8 +51,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
private LSL_Api m_lslApi;
[SetUp]
- public void SetUp()
+ public override void SetUp()
{
+ base.SetUp();
+
IConfigSource initConfigSource = new IniConfigSource();
IConfig config = initConfigSource.AddConfig("XEngine");
config.Set("Enabled", "true");
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiAppearanceTest.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiAppearanceTest.cs
index 213f33f060..1381d2b045 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiAppearanceTest.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiAppearanceTest.cs
@@ -57,8 +57,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
protected XEngine.XEngine m_engine;
[SetUp]
- public void SetUp()
+ public override void SetUp()
{
+ base.SetUp();
+
IConfigSource initConfigSource = new IniConfigSource();
IConfig config = initConfigSource.AddConfig("XEngine");
config.Set("Enabled", "true");
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiNpcTests.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiNpcTests.cs
index b49bcc2000..d6c82f1258 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiNpcTests.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiNpcTests.cs
@@ -127,12 +127,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
OSSL_Api osslApi = new OSSL_Api();
osslApi.Initialize(m_engine, so.RootPart, null);
- string npcRaw;
bool gotExpectedException = false;
try
{
- npcRaw
- = osslApi.osNpcCreate("Jane", "Doe", new LSL_Types.Vector3(128, 128, 128), "not existing notecard name");
+ osslApi.osNpcCreate("Jane", "Doe", new LSL_Types.Vector3(128, 128, 128), "not existing notecard name");
}
catch (ScriptException)
{
diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
index 0460f22b6a..965101aa46 100644
--- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
+++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
@@ -30,6 +30,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+using System.Linq;
using System.Reflection;
using System.Security;
using System.Security.Policy;
@@ -377,8 +378,20 @@ namespace OpenSim.Region.ScriptEngine.XEngine
///
///
///
- /// true if we're okay to proceed, false if not.
+ /// Basis on which to sort output. Can be null if no sort needs to take place
private void HandleScriptsAction(string[] cmdparams, Action action)
+ {
+ HandleScriptsAction