Merge branch 'master' of ssh://opensimulator.org/var/git/opensim

integration
BlueWall 2012-10-20 04:32:45 -04:00
commit af9cd7d30c
21 changed files with 814 additions and 460 deletions

View File

@ -28,6 +28,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using log4net;
@ -495,42 +496,36 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
protected virtual void StatusNotify(List<FriendInfo> friendList, UUID userID, bool online)
{
foreach (FriendInfo friend in friendList)
List<string> friendStringIds = friendList.ConvertAll<string>(friend => friend.Friend);
List<string> remoteFriendStringIds = new List<string>();
foreach (string friendStringId in friendStringIds)
{
UUID friendID;
if (UUID.TryParse(friend.Friend, out friendID))
UUID friendUuid;
if (UUID.TryParse(friendStringId, out friendUuid))
{
// Try local
if (LocalStatusNotification(userID, friendID, online))
if (LocalStatusNotification(userID, friendUuid, online))
continue;
// The friend is not here [as root]. Let's forward.
PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
if (friendSessions != null && friendSessions.Length > 0)
{
PresenceInfo friendSession = null;
foreach (PresenceInfo pinfo in friendSessions)
{
if (pinfo.RegionID != UUID.Zero) // let's guard against sessions-gone-bad
{
friendSession = pinfo;
break;
}
}
if (friendSession != null)
{
GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
//m_log.DebugFormat("[FRIENDS]: Remote Notify to region {0}", region.RegionName);
m_FriendsSimConnector.StatusNotify(region, userID, friendID, online);
}
}
// Friend is not online. Ignore.
remoteFriendStringIds.Add(friendStringId);
}
else
{
m_log.WarnFormat("[FRIENDS]: Error parsing friend ID {0}", friend.Friend);
m_log.WarnFormat("[FRIENDS]: Error parsing friend ID {0}", friendStringId);
}
}
// We do this regrouping so that we can efficiently send a single request rather than one for each
// friend in what may be a very large friends list.
PresenceInfo[] friendSessions = PresenceService.GetAgents(remoteFriendStringIds.ToArray());
foreach (PresenceInfo friendSession in friendSessions)
{
// let's guard against sessions-gone-bad
if (friendSession.RegionID != UUID.Zero)
{
GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
//m_log.DebugFormat("[FRIENDS]: Remote Notify to region {0}", region.RegionName);
m_FriendsSimConnector.StatusNotify(region, userID, friendSession.UserID, online);
}
}
}

View File

@ -66,9 +66,9 @@ namespace OpenSim.Region.Framework.Scenes
// /// </summary>
// private bool m_waitingForObjectAsset;
public UuidGatherer(IAssetService assetCache)
public UuidGatherer(IAssetService assetService)
{
m_assetService = assetCache;
m_assetService = assetService;
}
/// <summary>

View File

@ -27,6 +27,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using log4net;
using Mono.Addins;
@ -36,6 +37,8 @@ using OpenMetaverse.StructuredData;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
{
@ -45,6 +48,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private List<Scene> m_sceneList = new List<Scene>();
private IPresenceService m_presenceService;
private IMessageTransferModule m_msgTransferModule = null;
@ -54,6 +58,27 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
private bool m_groupMessagingEnabled = false;
private bool m_debugEnabled = true;
/// <summary>
/// If enabled, module only tries to send group IMs to online users by querying cached presence information.
/// </summary>
private bool m_messageOnlineAgentsOnly;
/// <summary>
/// Cache for online users.
/// </summary>
/// <remarks>
/// Group ID is key, presence information for online members is value.
/// Will only be non-null if m_messageOnlineAgentsOnly = true
/// We cache here so that group messages don't constantly have to re-request the online user list to avoid
/// attempted expensive sending of messages to offline users.
/// The tradeoff is that a user that comes online will not receive messages consistently from all other users
/// until caches have updated.
/// Therefore, we set the cache expiry to just 20 seconds.
/// </remarks>
private ExpiringCache<UUID, PresenceInfo[]> m_usersOnlineCache;
private int m_usersOnlineCacheExpirySeconds = 20;
#region IRegionModuleBase Members
public void Initialise(IConfigSource config)
@ -83,10 +108,17 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
return;
}
m_messageOnlineAgentsOnly = groupsConfig.GetBoolean("MessageOnlineUsersOnly", false);
if (m_messageOnlineAgentsOnly)
m_usersOnlineCache = new ExpiringCache<UUID, PresenceInfo[]>();
m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", true);
}
m_log.Info("[GROUPS-MESSAGING]: GroupsMessagingModule starting up");
m_log.InfoFormat(
"[GROUPS-MESSAGING]: GroupsMessagingModule enabled with MessageOnlineOnly = {0}, DebugEnabled = {1}",
m_messageOnlineAgentsOnly, m_debugEnabled);
}
public void AddRegion(Scene scene)
@ -126,6 +158,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
return;
}
if (m_presenceService == null)
m_presenceService = scene.PresenceService;
m_sceneList.Add(scene);
@ -207,11 +241,41 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
public void SendMessageToGroup(GridInstantMessage im, UUID groupID)
{
List<GroupMembersData> groupMembers = m_groupData.GetGroupMembers(new UUID(im.fromAgentID), groupID);
int groupMembersCount = groupMembers.Count;
if (m_messageOnlineAgentsOnly)
{
string[] t1 = groupMembers.ConvertAll<string>(gmd => gmd.AgentID.ToString()).ToArray();
// We cache in order not to overwhlem the presence service on large grids with many groups. This does
// mean that members coming online will not see all group members until after m_usersOnlineCacheExpirySeconds has elapsed.
// (assuming this is the same across all grid simulators).
PresenceInfo[] onlineAgents;
if (!m_usersOnlineCache.TryGetValue(groupID, out onlineAgents))
{
onlineAgents = m_presenceService.GetAgents(t1);
m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds);
}
HashSet<string> onlineAgentsUuidSet = new HashSet<string>();
Array.ForEach<PresenceInfo>(onlineAgents, pi => onlineAgentsUuidSet.Add(pi.UserID));
groupMembers = groupMembers.Where(gmd => onlineAgentsUuidSet.Contains(gmd.AgentID.ToString())).ToList();
// if (m_debugEnabled)
// m_log.DebugFormat(
// "[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members, {2} online",
// groupID, groupMembersCount, groupMembers.Count());
}
else
{
if (m_debugEnabled)
m_log.DebugFormat(
"[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members",
groupID, groupMembers.Count);
}
int requestStartTick = Environment.TickCount;
foreach (GroupMembersData member in groupMembers)
{
@ -254,6 +318,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
ProcessMessageFromGroupSession(msg);
}
}
// Temporary for assessing how long it still takes to send messages to large online groups.
if (m_messageOnlineAgentsOnly)
m_log.DebugFormat(
"[GROUPS-MESSAGING]: SendMessageToGroup for group {0} with {1} visible members, {2} online took {3}ms",
groupID, groupMembersCount, groupMembers.Count(), Environment.TickCount - requestStartTick);
}
#region SimGridEventHandlers

View File

@ -123,7 +123,36 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
public void AddRegion(Scene scene)
{
if (m_groupsEnabled)
{
scene.RegisterModuleInterface<IGroupsModule>(this);
scene.AddCommand(
"debug",
this,
"debug groups verbose",
"debug groups verbose <true|false>",
"This setting turns on very verbose groups debugging",
HandleDebugGroupsVerbose);
}
}
private void HandleDebugGroupsVerbose(object modules, string[] args)
{
if (args.Length < 4)
{
MainConsole.Instance.Output("Usage: debug groups verbose <true|false>");
return;
}
bool verbose = false;
if (!bool.TryParse(args[3], out verbose))
{
MainConsole.Instance.Output("Usage: debug groups verbose <true|false>");
return;
}
m_debugEnabled = verbose;
MainConsole.Instance.OutputFormat("{0} verbose logging set to {1}", Name, m_debugEnabled);
}
public void RegionLoaded(Scene scene)

View File

@ -41,8 +41,6 @@ public class BSCharacter : BSPhysObject
// private bool _stopped;
private OMV.Vector3 _size;
private OMV.Vector3 _scale;
private PrimitiveBaseShape _pbs;
private bool _grabbed;
private bool _selected;
private OMV.Vector3 _position;
@ -67,6 +65,10 @@ public class BSCharacter : BSPhysObject
private bool _kinematic;
private float _buoyancy;
// The friction and velocity of the avatar is modified depending on whether walking or not.
private OMV.Vector3 _appliedVelocity; // the last velocity applied to the avatar
private float _currentFriction; // the friction currently being used (changed by setVelocity).
private OMV.Vector3 _PIDTarget;
private bool _usePID;
private float _PIDTau;
@ -84,14 +86,18 @@ public class BSCharacter : BSPhysObject
_flying = isFlying;
_orientation = OMV.Quaternion.Identity;
_velocity = OMV.Vector3.Zero;
_appliedVelocity = OMV.Vector3.Zero;
_buoyancy = ComputeBuoyancyFromFlying(isFlying);
_currentFriction = PhysicsScene.Params.avatarStandingFriction;
_avatarDensity = PhysicsScene.Params.avatarDensity;
// The dimensions of the avatar capsule are kept in the scale.
// Physics creates a unit capsule which is scaled by the physics engine.
ComputeAvatarScale(_size);
_avatarDensity = PhysicsScene.Params.avatarDensity;
// set _avatarVolume and _mass based on capsule size, _density and _scale
// set _avatarVolume and _mass based on capsule size, _density and Scale
ComputeAvatarVolumeAndMass();
DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}",
LocalID, _size, Scale, _avatarDensity, _avatarVolume, MassRaw);
ShapeData shapeData = new ShapeData();
shapeData.ID = LocalID;
@ -99,28 +105,22 @@ public class BSCharacter : BSPhysObject
shapeData.Position = _position;
shapeData.Rotation = _orientation;
shapeData.Velocity = _velocity;
shapeData.Scale = _scale;
shapeData.Size = Scale;
shapeData.Scale = Scale;
shapeData.Mass = _mass;
shapeData.Buoyancy = _buoyancy;
shapeData.Static = ShapeData.numericFalse;
shapeData.Friction = PhysicsScene.Params.avatarFriction;
shapeData.Friction = PhysicsScene.Params.avatarStandingFriction;
shapeData.Restitution = PhysicsScene.Params.avatarRestitution;
// do actual create at taint time
PhysicsScene.TaintedObject("BSCharacter.create", delegate()
{
DetailLog("{0},BSCharacter.create,taint", LocalID);
BulletSimAPI.CreateObject(PhysicsScene.WorldID, shapeData);
// New body and shape into BSBody and BSShape
PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this, shapeData, null, null, null);
// Set the buoyancy for flying. This will be refactored when all the settings happen in C#.
// If not set at creation, the avatar will stop flying when created after crossing a region boundry.
BulletSimAPI.SetObjectBuoyancy(PhysicsScene.WorldID, LocalID, _buoyancy);
BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(PhysicsScene.World.ptr, LocalID));
// This works here because CreateObject has already put the character into the physical world.
BulletSimAPI.SetCollisionFilterMask2(BSBody.ptr,
(uint)CollisionFilterGroups.AvatarFilter, (uint)CollisionFilterGroups.AvatarMask);
SetPhysicalProperties();
});
return;
}
@ -131,53 +131,85 @@ public class BSCharacter : BSPhysObject
DetailLog("{0},BSCharacter.Destroy", LocalID);
PhysicsScene.TaintedObject("BSCharacter.destroy", delegate()
{
BulletSimAPI.DestroyObject(PhysicsScene.WorldID, LocalID);
PhysicsScene.Shapes.DereferenceBody(BSBody, true, null);
PhysicsScene.Shapes.DereferenceShape(BSShape, true, null);
});
}
private void SetPhysicalProperties()
{
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, BSBody.ptr);
ZeroMotion();
ForcePosition = _position;
// Set the velocity and compute the proper friction
ForceVelocity = _velocity;
BulletSimAPI.SetRestitution2(BSBody.ptr, PhysicsScene.Params.avatarRestitution);
BulletSimAPI.SetLocalScaling2(BSShape.ptr, Scale);
BulletSimAPI.SetContactProcessingThreshold2(BSBody.ptr, PhysicsScene.Params.contactProcessingThreshold);
if (PhysicsScene.Params.ccdMotionThreshold > 0f)
{
BulletSimAPI.SetCcdMotionThreshold2(BSBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
BulletSimAPI.SetCcdSweepSphereRadius2(BSBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
}
OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(BSShape.ptr, MassRaw);
BulletSimAPI.SetMassProps2(BSBody.ptr, MassRaw, localInertia);
BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.CF_CHARACTER_OBJECT);
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, BSBody.ptr);
BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG);
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, BSBody.ptr);
// Do this after the object has been added to the world
BulletSimAPI.SetCollisionFilterMask2(BSBody.ptr,
(uint)CollisionFilterGroups.AvatarFilter,
(uint)CollisionFilterGroups.AvatarMask);
}
public override void RequestPhysicsterseUpdate()
{
base.RequestPhysicsterseUpdate();
}
// No one calls this method so I don't know what it could possibly mean
public override bool Stopped {
get { return false; }
}
public override bool Stopped { get { return false; } }
public override OMV.Vector3 Size {
get
{
// Avatar capsule size is kept in the scale parameter.
return new OMV.Vector3(_scale.X * 2, _scale.Y * 2, _scale.Z);
return _size;
}
set {
// When an avatar's size is set, only the height is changed
// and that really only depends on the radius.
// When an avatar's size is set, only the height is changed.
_size = value;
ComputeAvatarScale(_size);
// TODO: something has to be done with the avatar's vertical position
ComputeAvatarVolumeAndMass();
DetailLog("{0},BSCharacter.setSize,call,scale={1},density={2},volume={3},mass={4}",
LocalID, Scale, _avatarDensity, _avatarVolume, MassRaw);
PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
{
BulletSimAPI.SetObjectScaleMass(PhysicsScene.WorldID, LocalID, _scale, _mass, true);
BulletSimAPI.SetLocalScaling2(BSShape.ptr, Scale);
OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(BSShape.ptr, MassRaw);
BulletSimAPI.SetMassProps2(BSBody.ptr, MassRaw, localInertia);
});
}
}
public override PrimitiveBaseShape Shape {
set { _pbs = value;
}
public override OMV.Vector3 Scale { get; set; }
public override PrimitiveBaseShape Shape
{
set { BaseShape = value; }
}
public override bool Grabbed {
set { _grabbed = value;
}
set { _grabbed = value; }
}
public override bool Selected {
set { _selected = value;
}
set { _selected = value; }
}
public override void CrossingFailure() { return; }
public override void link(PhysicsActor obj) { return; }
@ -204,7 +236,7 @@ public class BSCharacter : BSPhysObject
public override OMV.Vector3 Position {
get {
// _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID);
// _position = BulletSimAPI.GetObjectPosition2(Scene.World.ptr, LocalID);
return _position;
}
set {
@ -214,7 +246,7 @@ public class BSCharacter : BSPhysObject
PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
{
DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation);
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
});
}
}
@ -263,7 +295,7 @@ public class BSCharacter : BSPhysObject
// A version of the sanity check that also makes sure a new position value is
// pushed back to the physics engine. This routine would be used by anyone
// who is not already pushing the value.
private bool PositionSanityCheck2(bool inTaintTime)
private bool PositionSanityCheck(bool inTaintTime)
{
bool ret = false;
if (PositionSanityCheck())
@ -273,7 +305,7 @@ public class BSCharacter : BSPhysObject
BSScene.TaintCallback sanityOperation = delegate()
{
DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation);
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
};
if (inTaintTime)
sanityOperation();
@ -284,11 +316,7 @@ public class BSCharacter : BSPhysObject
return ret;
}
public override float Mass {
get {
return _mass;
}
}
public override float Mass { get { return _mass; } }
// used when we only want this prim's mass and not the linkset thing
public override float MassRaw { get {return _mass; } }
@ -301,15 +329,13 @@ public class BSCharacter : BSPhysObject
PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
{
DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
BulletSimAPI.SetObjectForce(PhysicsScene.WorldID, LocalID, _force);
BulletSimAPI.SetObjectForce2(BSBody.ptr, _force);
});
}
}
public override int VehicleType {
get { return 0; }
set { return; }
}
// Avatars don't do vehicles
public override int VehicleType { get { return 0; } set { return; } }
public override void VehicleFloatParam(int param, float value) { }
public override void VehicleVectorParam(int param, OMV.Vector3 value) {}
public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { }
@ -328,15 +354,37 @@ public class BSCharacter : BSPhysObject
PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate()
{
DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity);
BulletSimAPI.SetObjectVelocity(PhysicsScene.WorldID, LocalID, _velocity);
ForceVelocity = _velocity;
});
}
}
public override OMV.Vector3 ForceVelocity {
get { return _velocity; }
set {
// Depending on whether the avatar is moving or not, change the friction
// to keep the avatar from slipping around
if (_velocity.Length() == 0)
{
if (_currentFriction != PhysicsScene.Params.avatarStandingFriction)
{
_currentFriction = PhysicsScene.Params.avatarStandingFriction;
BulletSimAPI.SetFriction2(BSBody.ptr, _currentFriction);
}
}
else
{
if (_currentFriction != PhysicsScene.Params.avatarFriction)
{
_currentFriction = PhysicsScene.Params.avatarFriction;
BulletSimAPI.SetFriction2(BSBody.ptr, _currentFriction);
}
}
_velocity = value;
BulletSimAPI.SetObjectVelocity(PhysicsScene.WorldID, LocalID, _velocity);
// Remember the set velocity so we can suppress the reduction by friction, ...
_appliedVelocity = value;
BulletSimAPI.SetLinearVelocity2(BSBody.ptr, _velocity);
BulletSimAPI.Activate2(BSBody.ptr, true);
}
}
public override OMV.Vector3 Torque {
@ -360,8 +408,8 @@ public class BSCharacter : BSPhysObject
// m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation);
PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
{
// _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID);
BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation);
// _position = BulletSimAPI.GetPosition2(BSBody.ptr);
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation);
});
}
}
@ -389,12 +437,18 @@ public class BSCharacter : BSPhysObject
set { _isPhysical = value;
}
}
public override bool IsSolid {
get { return true; }
}
public override bool IsStatic {
get { return false; }
}
public override bool Flying {
get { return _flying; }
set {
_flying = value;
// simulate flying by changing the effect of gravity
this.Buoyancy = ComputeBuoyancyFromFlying(_flying);
Buoyancy = ComputeBuoyancyFromFlying(_flying);
}
}
// Flying is implimented by changing the avatar's buoyancy.
@ -454,10 +508,19 @@ public class BSCharacter : BSPhysObject
PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate()
{
DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
BulletSimAPI.SetObjectBuoyancy(PhysicsScene.WorldID, LocalID, _buoyancy);
ForceBuoyancy = _buoyancy;
});
}
}
public override float ForceBuoyancy {
get { return _buoyancy; }
set { _buoyancy = value;
DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
// Buoyancy is faked by changing the gravity applied to the object
float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
BulletSimAPI.SetGravity2(BSBody.ptr, new OMV.Vector3(0f, 0f, grav));
}
}
// Used for MoveTo
public override OMV.Vector3 PIDTarget {
@ -518,27 +581,32 @@ public class BSCharacter : BSPhysObject
private void ComputeAvatarScale(OMV.Vector3 size)
{
_scale.X = PhysicsScene.Params.avatarCapsuleRadius;
_scale.Y = PhysicsScene.Params.avatarCapsuleRadius;
// The 'size' given by the simulator is the mid-point of the avatar
// and X and Y are unspecified.
// The 1.15 came from ODE but it seems to cause the avatar to float off the ground
// _scale.Z = (_size.Z * 1.15f) - (_scale.X + _scale.Y);
_scale.Z = (_size.Z) - (_scale.X + _scale.Y);
OMV.Vector3 newScale = OMV.Vector3.Zero;
newScale.X = PhysicsScene.Params.avatarCapsuleRadius;
newScale.Y = PhysicsScene.Params.avatarCapsuleRadius;
// From the total height, remote the capsule half spheres that are at each end
newScale.Z = (size.Z * 2f) - Math.Min(newScale.X, newScale.Y);
// newScale.Z = (size.Z * 2f);
Scale = newScale;
}
// set _avatarVolume and _mass based on capsule size, _density and _scale
// set _avatarVolume and _mass based on capsule size, _density and Scale
private void ComputeAvatarVolumeAndMass()
{
_avatarVolume = (float)(
Math.PI
* _scale.X
* _scale.Y // the area of capsule cylinder
* _scale.Z // times height of capsule cylinder
* Scale.X
* Scale.Y // the area of capsule cylinder
* Scale.Z // times height of capsule cylinder
+ 1.33333333f
* Math.PI
* _scale.X
* Math.Min(_scale.X, _scale.Y)
* _scale.Y // plus the volume of the capsule end caps
* Scale.X
* Math.Min(Scale.X, Scale.Y)
* Scale.Y // plus the volume of the capsule end caps
);
_mass = _avatarDensity * _avatarVolume;
}
@ -553,7 +621,23 @@ public class BSCharacter : BSPhysObject
_acceleration = entprop.Acceleration;
_rotationalVelocity = entprop.RotationalVelocity;
// Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
PositionSanityCheck2(true);
PositionSanityCheck(true);
// remember the current and last set values
LastEntityProperties = CurrentEntityProperties;
CurrentEntityProperties = entprop;
if (entprop.Velocity != LastEntityProperties.Velocity)
{
// Changes in the velocity are suppressed in avatars.
// That's just the way they are defined.
OMV.Vector3 avVel = new OMV.Vector3(_appliedVelocity.X, _appliedVelocity.Y, entprop.Velocity.Z);
_velocity = avVel;
BulletSimAPI.SetLinearVelocity2(BSBody.ptr, avVel);
}
// Tell the linkset about this
Linkset.UpdateProperties(this);
// Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
// base.RequestPhysicsterseUpdate();

View File

@ -47,6 +47,7 @@ public abstract class BSPhysObject : PhysicsActor
TypeName = typeName;
Linkset = new BSLinkset(PhysicsScene, this);
LastAssetBuildFailed = false;
CollisionCollection = new CollisionEventUpdate();
SubscribedEventsMs = 0;
@ -69,6 +70,23 @@ public abstract class BSPhysObject : PhysicsActor
// Reference to the physical shape (btCollisionShape) of this object
public BulletShape BSShape;
// 'true' if the mesh's underlying asset failed to build.
// This will keep us from looping after the first time the build failed.
public bool LastAssetBuildFailed { get; set; }
// The objects base shape information. Null if not a prim type shape.
public PrimitiveBaseShape BaseShape { get; protected set; }
// When the physical properties are updated, an EntityProperty holds the update values.
// Keep the current and last EntityProperties to enable computation of differences
// between the current update and the previous values.
public EntityProperties CurrentEntityProperties { get; set; }
public EntityProperties LastEntityProperties { get; set; }
public abstract OMV.Vector3 Scale { get; set; }
public abstract bool IsSolid { get; }
public abstract bool IsStatic { get; }
// Stop all physical motion.
public abstract void ZeroMotion();
@ -89,6 +107,10 @@ public abstract class BSPhysObject : PhysicsActor
public abstract OMV.Vector3 ForceRotationalVelocity { get; set; }
public abstract float ForceBuoyancy { get; set; }
public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
#region Collisions
// Requested number of milliseconds between collision events. Zero means disabled.
@ -129,30 +151,28 @@ public abstract class BSPhysObject : PhysicsActor
// if someone has subscribed for collision events....
if (SubscribedEvents()) {
CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
// DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}",
// LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth);
DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}",
LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth);
ret = true;
}
return ret;
}
// Routine to send the collected collisions into the simulator.
// Also handles removal of this from the collection of objects with collisions if
// there are no collisions from this object. Mechanism is create one last
// collision event to make collision_end work.
// Send the collected collisions into the simulator.
// Called at taint time from within the Step() function thus no locking problems
// with CollisionCollection and ObjectsWithNoMoreCollisions.
// Return 'true' if there were some actual collisions passed up
public virtual bool SendCollisions()
{
bool ret = true;
// If the 'no collision' call, force it to happen right now so quick collision_end
bool force = CollisionCollection.Count == 0;
// throttle the collisions to the number of milliseconds specified in the subscription
int nowTime = PhysicsScene.SimulationNowTime;
if (nowTime >= NextCollisionOkTime)
if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime))
{
NextCollisionOkTime = nowTime + SubscribedEventsMs;
NextCollisionOkTime = PhysicsScene.SimulationNowTime + SubscribedEventsMs;
// We are called if we previously had collisions. If there are no collisions
// this time, send up one last empty event so OpenSim can sense collision end.

View File

@ -46,12 +46,10 @@ public sealed class BSPrim : BSPhysObject
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[BULLETS PRIM]";
private PrimitiveBaseShape _pbs;
// _size is what the user passed. _scale is what we pass to the physics engine with the mesh.
// Often _scale is unity because the meshmerizer will apply _size when creating the mesh.
// _size is what the user passed. Scale is what we pass to the physics engine with the mesh.
// Often Scale is unity because the meshmerizer will apply _size when creating the mesh.
private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user
private OMV.Vector3 _scale; // the multiplier for each mesh dimension for the mesh as created by the meshmerizer
// private OMV.Vector3 _scale; // the multiplier for each mesh dimension for the mesh as created by the meshmerizer
private bool _grabbed;
private bool _isSelected;
@ -98,12 +96,12 @@ public sealed class BSPrim : BSPhysObject
_physicsActorType = (int)ActorTypes.Prim;
_position = pos;
_size = size;
_scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type
Scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type
_orientation = rotation;
_buoyancy = 1f;
_velocity = OMV.Vector3.Zero;
_rotationalVelocity = OMV.Vector3.Zero;
_pbs = pbs;
BaseShape = pbs;
_isPhysical = pisPhysical;
_isVolumeDetect = false;
_friction = PhysicsScene.Params.defaultFriction; // TODO: compute based on object material
@ -160,32 +158,31 @@ public sealed class BSPrim : BSPhysObject
get { return _size; }
set {
_size = value;
PhysicsScene.TaintedObject("BSPrim.setSize", delegate()
{
_mass = CalculateMass(); // changing size changes the mass
// Since _size changed, the mesh needs to be rebuilt. If rebuilt, all the correct
// scale and margins are set.
CreateGeomAndObject(true);
// DetailLog("{0},BSPrim.setSize,size={1},scale={2},mass={3},physical={4}", LocalID, _size, _scale, _mass, IsPhysical);
});
ForceBodyShapeRebuild(false);
}
}
// Scale is what we set in the physics engine. It is different than 'size' in that
// 'size' can be encorporated into the mesh. In that case, the scale is <1,1,1>.
public OMV.Vector3 Scale
{
get { return _scale; }
set { _scale = value; }
}
public override OMV.Vector3 Scale { get; set; }
public override PrimitiveBaseShape Shape {
set {
_pbs = value;
PhysicsScene.TaintedObject("BSPrim.setShape", delegate()
BaseShape = value;
ForceBodyShapeRebuild(false);
}
}
public override bool ForceBodyShapeRebuild(bool inTaintTime)
{
BSScene.TaintCallback rebuildOperation = delegate()
{
_mass = CalculateMass(); // changing the shape changes the mass
CreateGeomAndObject(true);
});
}
};
if (inTaintTime)
rebuildOperation();
else
PhysicsScene.TaintedObject("BSPrim.ForceBodyShapeRebuild", rebuildOperation);
return true;
}
public override bool Grabbed {
set { _grabbed = value;
@ -325,9 +322,9 @@ public sealed class BSPrim : BSPhysObject
}
// A version of the sanity check that also makes sure a new position value is
// pushed back to the physics engine. This routine would be used by anyone
// pushed to the physics engine. This routine would be used by anyone
// who is not already pushing the value.
private bool PositionSanityCheck2(bool inTaintTime)
private bool PositionSanityCheck(bool inTaintTime)
{
bool ret = false;
if (PositionSanityCheck())
@ -337,7 +334,7 @@ public sealed class BSPrim : BSPhysObject
BSScene.TaintCallback sanityOperation = delegate()
{
DetailLog("{0},BSPrim.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation);
ForcePosition = _position;
};
if (inTaintTime)
sanityOperation();
@ -547,13 +544,13 @@ public sealed class BSPrim : BSPhysObject
}
// An object is static (does not move) if selected or not physical
private bool IsStatic
public override bool IsStatic
{
get { return _isSelected || !IsPhysical; }
}
// An object is solid if it's not phantom and if it's not doing VolumeDetect
public bool IsSolid
public override bool IsSolid
{
get { return !IsPhantom && !_isVolumeDetect; }
}
@ -631,6 +628,12 @@ public sealed class BSPrim : BSPhysObject
BulletSimAPI.SetMassProps2(BSBody.ptr, 0f, OMV.Vector3.Zero);
// There is no inertia in a static object
BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr);
// Set collision detection parameters
if (PhysicsScene.Params.ccdMotionThreshold > 0f)
{
BulletSimAPI.SetCcdMotionThreshold2(BSBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
BulletSimAPI.SetCcdSweepSphereRadius2(BSBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
}
// 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.
@ -662,6 +665,13 @@ public sealed class BSPrim : BSPhysObject
BulletSimAPI.SetMassProps2(BSBody.ptr, _mass, inertia);
BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr);
// Set collision detection parameters
if (PhysicsScene.Params.ccdMotionThreshold > 0f)
{
BulletSimAPI.SetCcdMotionThreshold2(BSBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
BulletSimAPI.SetCcdSweepSphereRadius2(BSBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
}
// Various values for simulation limits
BulletSimAPI.SetDamping2(BSBody.ptr, PhysicsScene.Params.linearDamping, PhysicsScene.Params.angularDamping);
BulletSimAPI.SetDeactivationTime2(BSBody.ptr, PhysicsScene.Params.deactivationTime);
@ -813,11 +823,18 @@ public sealed class BSPrim : BSPhysObject
_buoyancy = value;
PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate()
{
// DetailLog("{0},BSPrim.SetBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
ForceBuoyancy = _buoyancy;
});
}
}
public override float ForceBuoyancy {
get { return _buoyancy; }
set {
_buoyancy = value;
// DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
// Buoyancy is faked by changing the gravity applied to the object
float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
BulletSimAPI.SetGravity2(BSBody.ptr, new OMV.Vector3(0f, 0f, grav));
});
}
}
@ -907,19 +924,19 @@ public sealed class BSPrim : BSPhysObject
float tmp;
float returnMass = 0;
float hollowAmount = (float)_pbs.ProfileHollow * 2.0e-5f;
float hollowAmount = (float)BaseShape.ProfileHollow * 2.0e-5f;
float hollowVolume = hollowAmount * hollowAmount;
switch (_pbs.ProfileShape)
switch (BaseShape.ProfileShape)
{
case ProfileShape.Square:
// default box
if (_pbs.PathCurve == (byte)Extrusion.Straight)
if (BaseShape.PathCurve == (byte)Extrusion.Straight)
{
if (hollowAmount > 0.0)
{
switch (_pbs.HollowShape)
switch (BaseShape.HollowShape)
{
case HollowShape.Square:
case HollowShape.Same:
@ -943,19 +960,19 @@ public sealed class BSPrim : BSPhysObject
}
}
else if (_pbs.PathCurve == (byte)Extrusion.Curve1)
else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
{
//a tube
volume *= 0.78539816339e-2f * (float)(200 - _pbs.PathScaleX);
tmp= 1.0f -2.0e-2f * (float)(200 - _pbs.PathScaleY);
volume *= 0.78539816339e-2f * (float)(200 - BaseShape.PathScaleX);
tmp= 1.0f -2.0e-2f * (float)(200 - BaseShape.PathScaleY);
volume -= volume*tmp*tmp;
if (hollowAmount > 0.0)
{
hollowVolume *= hollowAmount;
switch (_pbs.HollowShape)
switch (BaseShape.HollowShape)
{
case HollowShape.Square:
case HollowShape.Same:
@ -980,13 +997,13 @@ public sealed class BSPrim : BSPhysObject
case ProfileShape.Circle:
if (_pbs.PathCurve == (byte)Extrusion.Straight)
if (BaseShape.PathCurve == (byte)Extrusion.Straight)
{
volume *= 0.78539816339f; // elipse base
if (hollowAmount > 0.0)
{
switch (_pbs.HollowShape)
switch (BaseShape.HollowShape)
{
case HollowShape.Same:
case HollowShape.Circle:
@ -1008,10 +1025,10 @@ public sealed class BSPrim : BSPhysObject
}
}
else if (_pbs.PathCurve == (byte)Extrusion.Curve1)
else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
{
volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - _pbs.PathScaleX);
tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY);
volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - BaseShape.PathScaleX);
tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
volume *= (1.0f - tmp * tmp);
if (hollowAmount > 0.0)
@ -1020,7 +1037,7 @@ public sealed class BSPrim : BSPhysObject
// calculate the hollow volume by it's shape compared to the prim shape
hollowVolume *= hollowAmount;
switch (_pbs.HollowShape)
switch (BaseShape.HollowShape)
{
case HollowShape.Same:
case HollowShape.Circle:
@ -1044,7 +1061,7 @@ public sealed class BSPrim : BSPhysObject
break;
case ProfileShape.HalfCircle:
if (_pbs.PathCurve == (byte)Extrusion.Curve1)
if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
{
volume *= 0.52359877559829887307710723054658f;
}
@ -1052,7 +1069,7 @@ public sealed class BSPrim : BSPhysObject
case ProfileShape.EquilateralTriangle:
if (_pbs.PathCurve == (byte)Extrusion.Straight)
if (BaseShape.PathCurve == (byte)Extrusion.Straight)
{
volume *= 0.32475953f;
@ -1060,7 +1077,7 @@ public sealed class BSPrim : BSPhysObject
{
// calculate the hollow volume by it's shape compared to the prim shape
switch (_pbs.HollowShape)
switch (BaseShape.HollowShape)
{
case HollowShape.Same:
case HollowShape.Triangle:
@ -1085,11 +1102,11 @@ public sealed class BSPrim : BSPhysObject
volume *= (1.0f - hollowVolume);
}
}
else if (_pbs.PathCurve == (byte)Extrusion.Curve1)
else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
{
volume *= 0.32475953f;
volume *= 0.01f * (float)(200 - _pbs.PathScaleX);
tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY);
volume *= 0.01f * (float)(200 - BaseShape.PathScaleX);
tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
volume *= (1.0f - tmp * tmp);
if (hollowAmount > 0.0)
@ -1097,7 +1114,7 @@ public sealed class BSPrim : BSPhysObject
hollowVolume *= hollowAmount;
switch (_pbs.HollowShape)
switch (BaseShape.HollowShape)
{
case HollowShape.Same:
case HollowShape.Triangle:
@ -1137,26 +1154,26 @@ public sealed class BSPrim : BSPhysObject
float profileBegin;
float profileEnd;
if (_pbs.PathCurve == (byte)Extrusion.Straight || _pbs.PathCurve == (byte)Extrusion.Flexible)
if (BaseShape.PathCurve == (byte)Extrusion.Straight || BaseShape.PathCurve == (byte)Extrusion.Flexible)
{
taperX1 = _pbs.PathScaleX * 0.01f;
taperX1 = BaseShape.PathScaleX * 0.01f;
if (taperX1 > 1.0f)
taperX1 = 2.0f - taperX1;
taperX = 1.0f - taperX1;
taperY1 = _pbs.PathScaleY * 0.01f;
taperY1 = BaseShape.PathScaleY * 0.01f;
if (taperY1 > 1.0f)
taperY1 = 2.0f - taperY1;
taperY = 1.0f - taperY1;
}
else
{
taperX = _pbs.PathTaperX * 0.01f;
taperX = BaseShape.PathTaperX * 0.01f;
if (taperX < 0.0f)
taperX = -taperX;
taperX1 = 1.0f - taperX;
taperY = _pbs.PathTaperY * 0.01f;
taperY = BaseShape.PathTaperY * 0.01f;
if (taperY < 0.0f)
taperY = -taperY;
taperY1 = 1.0f - taperY;
@ -1166,13 +1183,13 @@ public sealed class BSPrim : BSPhysObject
volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY);
pathBegin = (float)_pbs.PathBegin * 2.0e-5f;
pathEnd = 1.0f - (float)_pbs.PathEnd * 2.0e-5f;
pathBegin = (float)BaseShape.PathBegin * 2.0e-5f;
pathEnd = 1.0f - (float)BaseShape.PathEnd * 2.0e-5f;
volume *= (pathEnd - pathBegin);
// this is crude aproximation
profileBegin = (float)_pbs.ProfileBegin * 2.0e-5f;
profileEnd = 1.0f - (float)_pbs.ProfileEnd * 2.0e-5f;
profileBegin = (float)BaseShape.ProfileBegin * 2.0e-5f;
profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f;
volume *= (profileEnd - profileBegin);
returnMass = _density * volume;
@ -1207,7 +1224,8 @@ public sealed class BSPrim : BSPhysObject
shape.Position = _position;
shape.Rotation = _orientation;
shape.Velocity = _velocity;
shape.Scale = _scale;
shape.Size = _size;
shape.Scale = Scale;
shape.Mass = _isPhysical ? _mass : 0f;
shape.Buoyancy = _buoyancy;
shape.HullKey = 0;
@ -1217,7 +1235,6 @@ public sealed class BSPrim : BSPhysObject
shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse;
shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue;
shape.Solid = IsSolid ? ShapeData.numericFalse : ShapeData.numericTrue;
shape.Size = _size;
}
// Rebuild the geometry and object.
// This is called when the shape changes so we need to recreate the mesh/hull.
@ -1234,7 +1251,7 @@ public sealed class BSPrim : BSPhysObject
// Create the correct physical representation for this type of object.
// Updates BSBody and BSShape with the new information.
// Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary.
PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, _pbs,
PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, BaseShape,
null, delegate(BulletBody dBody)
{
// Called if the current prim body is about to be destroyed.
@ -1328,9 +1345,11 @@ public sealed class BSPrim : BSPhysObject
_acceleration = entprop.Acceleration;
_rotationalVelocity = entprop.RotationalVelocity;
PositionSanityCheck2(true);
// remember the current and last set values
LastEntityProperties = CurrentEntityProperties;
CurrentEntityProperties = entprop;
Linkset.UpdateProperties(this);
PositionSanityCheck(true);
DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity);
@ -1348,6 +1367,9 @@ public sealed class BSPrim : BSPhysObject
entprop.Acceleration, entprop.RotationalVelocity);
}
*/
// The linkset implimentation might want to know about this.
Linkset.UpdateProperties(this);
}
}
}

View File

@ -256,14 +256,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
// m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
WorldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(),
World = new BulletSim(0, this, BulletSimAPI.Initialize2(worldExtent, m_paramsHandle.AddrOfPinnedObject(),
m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(),
m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject(),
m_DebugLogCallbackHandle);
// Initialization to support the transition to a new API which puts most of the logic
// into the C# code so it is easier to modify and add to.
World = new BulletSim(WorldID, this, BulletSimAPI.GetSimHandle2(WorldID));
m_DebugLogCallbackHandle));
Constraints = new BSConstraintCollection(World);
@ -360,7 +356,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
}
// Anything left in the unmanaged code should be cleaned out
BulletSimAPI.Shutdown(WorldID);
BulletSimAPI.Shutdown2(World.ptr);
// Not logging any more
PhysicsLogging.Close();
@ -498,7 +494,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
{
if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
numSubSteps = BulletSimAPI.PhysicsStep(WorldID, timeStep, m_maxSubSteps, m_fixedTimeStep,
numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
@ -536,19 +532,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
}
}
// This is a kludge to get avatar movement updates.
// the simulator expects collisions for avatars even if there are have been no collisions. This updates
// avatar animations and stuff.
// If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
foreach (BSPhysObject bsp in m_avatars)
bsp.SendCollisions();
// The above SendCollision's batch up the collisions on the objects.
// Now push the collisions into the simulator.
if (ObjectsWithCollisions.Count > 0)
{
foreach (BSPhysObject bsp in ObjectsWithCollisions)
if (!m_avatars.Contains(bsp)) // don't call avatars twice
if (!bsp.SendCollisions())
{
// If the object is done colliding, see that it's removed from the colliding list
@ -556,6 +544,14 @@ public class BSScene : PhysicsScene, IPhysicsParameters
}
}
// This is a kludge to get avatar movement updates.
// The simulator expects collisions for avatars even if there are have been no collisions.
// The event updates avatar animations and stuff.
// If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
foreach (BSPhysObject bsp in m_avatars)
if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
bsp.SendCollisions();
// Objects that are done colliding are removed from the ObjectsWithCollisions list.
// Not done above because it is inside an iteration of ObjectWithCollisions.
if (ObjectsWithNoMoreCollisions.Count > 0)
@ -579,11 +575,15 @@ public class BSScene : PhysicsScene, IPhysicsParameters
}
}
// This causes the unmanaged code to output ALL the values found in ALL the objects in the world.
// Only enable this in a limited test world with few objects.
// BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
// The physics engine returns the number of milliseconds it simulated this call.
// These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
// We multiply by 45 to give a recognizable running rate (45 or less).
return numSubSteps * m_fixedTimeStep * 1000 * 45;
// return timeStep * 1000 * 45;
// We multiply by 55 to give a recognizable running rate (55 or less).
return numSubSteps * m_fixedTimeStep * 1000 * 55;
// return timeStep * 1000 * 55;
}
// Something has collided
@ -800,6 +800,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
delegate float ParamGet(BSScene scene);
delegate void ParamSet(BSScene scene, string paramName, uint localID, float val);
delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val);
private struct ParameterDefn
{
@ -809,6 +810,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
public ParamUser userParam; // get the value from the configuration file
public ParamGet getter; // return the current value stored for this parameter
public ParamSet setter; // set the current value for this parameter
public SetOnObject onObject; // set the value on an object in the physical domain
public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s)
{
name = n;
@ -817,6 +819,17 @@ public class BSScene : PhysicsScene, IPhysicsParameters
userParam = u;
getter = g;
setter = s;
onObject = null;
}
public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o)
{
name = n;
desc = d;
defaultValue = v;
userParam = u;
getter = g;
setter = s;
onObject = o;
}
}
@ -838,6 +851,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
//
// The single letter parameters for the delegates are:
// s = BSScene
// o = BSPhysObject
// p = string parameter name
// l = localID of referenced object
// v = float value
@ -947,70 +961,84 @@ public class BSScene : PhysicsScene, IPhysicsParameters
-9.80665f,
(s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].gravity; },
(s,p,l,v) => { s.m_params[0].gravity = v; s.TaintedUpdateParameter(p,l,v); } ),
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].gravity, p, PhysParameterEntry.APPLY_TO_NONE, v); },
(s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ),
new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)",
0f,
(s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].linearDamping; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); } ),
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); },
(s,o,v) => { BulletSimAPI.SetDamping2(o.BSBody.ptr, v, v); } ),
new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)",
0f,
(s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].angularDamping; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); } ),
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); },
(s,o,v) => { BulletSimAPI.SetDamping2(o.BSBody.ptr, v, v); } ),
new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static",
0.2f,
(s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].deactivationTime; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); } ),
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); },
(s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.BSBody.ptr, v); } ),
new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static",
0.8f,
(s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].linearSleepingThreshold; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); } ),
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); },
(s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.BSBody.ptr, v, v); } ),
new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static",
1.0f,
(s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].angularSleepingThreshold; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); } ),
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); },
(s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.BSBody.ptr, v, v); } ),
new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" ,
0f, // set to zero to disable
(s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].ccdMotionThreshold; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); } ),
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); },
(s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.BSBody.ptr, v); } ),
new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" ,
0f,
(s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].ccdSweptSphereRadius; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); } ),
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); },
(s,o,v) => { BulletSimAPI.SetCcdSweepSphereRadius2(o.BSBody.ptr, v); } ),
new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" ,
0.1f,
(s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].contactProcessingThreshold; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); } ),
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); },
(s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.BSBody.ptr, v); } ),
new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
0.5f,
(s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].terrainFriction; },
(s,p,l,v) => { s.m_params[0].terrainFriction = v; s.TaintedUpdateParameter(p,l,v); } ),
(s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ),
new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" ,
0.8f,
(s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].terrainHitFraction; },
(s,p,l,v) => { s.m_params[0].terrainHitFraction = v; s.TaintedUpdateParameter(p,l,v); } ),
(s,p,l,v) => { s.m_params[0].terrainHitFraction = v; /* TODO: set on real terrain */ } ),
new ParameterDefn("TerrainRestitution", "Bouncyness" ,
0f,
(s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].terrainRestitution; },
(s,p,l,v) => { s.m_params[0].terrainRestitution = v; s.TaintedUpdateParameter(p,l,v); } ),
(s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ),
new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
0.2f,
(s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); },
(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.",
10f,
(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; } ),
new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.",
60f,
(s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); },
@ -1227,52 +1255,54 @@ public class BSScene : PhysicsScene, IPhysicsParameters
return ret;
}
// check to see if we are updating a parameter for a particular or all of the prims
protected void UpdateParameterObject(ref float loc, string parm, uint localID, float val)
{
List<uint> operateOn;
lock (PhysObjects) operateOn = new List<uint>(PhysObjects.Keys);
UpdateParameterSet(operateOn, ref loc, parm, localID, val);
}
// update all the localIDs specified
// If the local ID is APPLY_TO_NONE, just change the default value
// If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
// If the localID is a specific object, apply the parameter change to only that object
protected void UpdateParameterSet(List<uint> lIDs, ref float defaultLoc, string parm, uint localID, float val)
protected void UpdateParameterObject(ref float defaultLoc, string parm, uint localID, float val)
{
List<uint> objectIDs = new List<uint>();
switch (localID)
{
case PhysParameterEntry.APPLY_TO_NONE:
defaultLoc = val; // setting only the default value
// This will cause a call into the physical world if some operation is specified (SetOnObject).
objectIDs.Add(TERRAIN_ID);
TaintedUpdateParameter(parm, objectIDs, val);
break;
case PhysParameterEntry.APPLY_TO_ALL:
defaultLoc = val; // setting ALL also sets the default value
List<uint> objectIDs = lIDs;
string xparm = parm.ToLower();
float xval = val;
TaintedObject("BSScene.UpdateParameterSet", delegate() {
foreach (uint lID in objectIDs)
{
BulletSimAPI.UpdateParameter(WorldID, lID, xparm, xval);
}
});
lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
TaintedUpdateParameter(parm, objectIDs, val);
break;
default:
// setting only one localID
TaintedUpdateParameter(parm, localID, val);
objectIDs.Add(localID);
TaintedUpdateParameter(parm, objectIDs, val);
break;
}
}
// schedule the actual updating of the paramter to when the phys engine is not busy
protected void TaintedUpdateParameter(string parm, uint localID, float val)
protected void TaintedUpdateParameter(string parm, List<uint> lIDs, float val)
{
uint xlocalID = localID;
string xparm = parm.ToLower();
float xval = val;
TaintedObject("BSScene.TaintedUpdateParameter", delegate() {
BulletSimAPI.UpdateParameter(WorldID, xlocalID, xparm, xval);
List<uint> xlIDs = lIDs;
string xparm = parm;
TaintedObject("BSScene.UpdateParameterSet", delegate() {
ParameterDefn thisParam;
if (TryGetParameter(xparm, out thisParam))
{
if (thisParam.onObject != null)
{
foreach (uint lID in xlIDs)
{
BSPhysObject theObject = null;
PhysObjects.TryGetValue(lID, out theObject);
thisParam.onObject(this, theObject, xval);
}
}
}
});
}

View File

@ -51,7 +51,7 @@ public class BSShapeCollection : IDisposable
}
// Description of a hull.
// Meshes and hulls have the same shape hash key but we only need hulls for efficient physical objects
// Meshes and hulls have the same shape hash key but we only need hulls for efficient collision calculations.
private struct HullDesc
{
public IntPtr ptr;
@ -59,17 +59,9 @@ public class BSShapeCollection : IDisposable
public DateTime lastReferenced;
}
private struct BodyDesc
{
public IntPtr ptr;
// Bodies are only used once so reference count is always either one or zero
public int referenceCount;
public DateTime lastReferenced;
}
// The sharable set of meshes and hulls. Indexed by their shape hash.
private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>();
private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>();
private Dictionary<uint, BodyDesc> Bodies = new Dictionary<uint, BodyDesc>();
public BSShapeCollection(BSScene physScene)
{
@ -92,8 +84,12 @@ public class BSShapeCollection : IDisposable
// First checks the shape and updates that if necessary then makes
// sure the body is of the right type.
// Return 'true' if either the body or the shape changed.
// 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before
// the current shape or body is destroyed. This allows the caller to remove any
// higher level dependencies on the shape or body. Mostly used for LinkSets to
// remove the physical constraints before the body is destroyed.
// Called at taint-time!!
public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPrim prim,
public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim,
ShapeData shapeData, PrimitiveBaseShape pbs,
ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
{
@ -103,7 +99,8 @@ public class BSShapeCollection : IDisposable
lock (m_collectionActivityLock)
{
// Do we have the correct geometry for this type of object?
// Updates prim.BSShape with information/pointers to requested shape
// Updates prim.BSShape with information/pointers to shape.
// CreateGeom returns 'true' of BSShape as changed to a new shape.
bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs, shapeCallback);
// If we had to select a new shape geometry for the object,
// rebuild the body around it.
@ -120,31 +117,18 @@ public class BSShapeCollection : IDisposable
// Track another user of a body
// We presume the caller has allocated the body.
// Bodies only have one user so the reference count is either 1 or 0.
// Bodies only have one user so the body is just put into the world if not already there.
public void ReferenceBody(BulletBody body, bool inTaintTime)
{
lock (m_collectionActivityLock)
{
BodyDesc bodyDesc;
if (Bodies.TryGetValue(body.ID, out bodyDesc))
{
bodyDesc.referenceCount++;
DetailLog("{0},BSShapeCollection.ReferenceBody,existingBody,body={1},ref={2}", body.ID, body, bodyDesc.referenceCount);
}
else
{
// New entry
bodyDesc.ptr = body.ptr;
bodyDesc.referenceCount = 1;
DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,ref={2}",
body.ID, body, bodyDesc.referenceCount);
DetailLog("{0},BSShapeCollection.ReferenceBody,newBody", body.ID, body);
BSScene.TaintCallback createOperation = delegate()
{
if (!BulletSimAPI.IsInWorld2(body.ptr))
{
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr);
DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}",
body.ID, body);
DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
}
};
if (inTaintTime)
@ -152,9 +136,6 @@ public class BSShapeCollection : IDisposable
else
PhysicsScene.TaintedObject("BSShapeCollection.ReferenceBody", createOperation);
}
bodyDesc.lastReferenced = System.DateTime.Now;
Bodies[body.ID] = bodyDesc;
}
}
// Release the usage of a body.
@ -166,18 +147,6 @@ public class BSShapeCollection : IDisposable
lock (m_collectionActivityLock)
{
BodyDesc bodyDesc;
if (Bodies.TryGetValue(body.ID, out bodyDesc))
{
bodyDesc.referenceCount--;
bodyDesc.lastReferenced = System.DateTime.Now;
Bodies[body.ID] = bodyDesc;
DetailLog("{0},BSShapeCollection.DereferenceBody,ref={1}", body.ID, bodyDesc.referenceCount);
// If body is no longer being used, free it -- bodies can never be shared.
if (bodyDesc.referenceCount == 0)
{
Bodies.Remove(body.ID);
BSScene.TaintCallback removeOperation = delegate()
{
DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}, inTaintTime={2}",
@ -199,12 +168,6 @@ public class BSShapeCollection : IDisposable
PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation);
}
}
else
{
DetailLog("{0},BSShapeCollection.DereferenceBody,DID NOT FIND BODY", body.ID, bodyDesc.referenceCount);
}
}
}
// Track the datastructures and use count for a shape.
// When creating a hull, this is called first to reference the mesh
@ -271,13 +234,24 @@ public class BSShapeCollection : IDisposable
}
// Release the usage of a shape.
// The collisionObject is released since it is a copy of the real collision shape.
public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback)
{
if (shape.ptr == IntPtr.Zero)
return;
BSScene.TaintCallback dereferenceOperation = delegate()
{
if (shape.ptr != IntPtr.Zero)
{
if (shape.isNativeShape)
{
// Native shapes are not tracked and are released immediately
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);
}
else
{
switch (shape.type)
{
@ -290,16 +264,10 @@ public class BSShapeCollection : IDisposable
case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
break;
default:
// Native shapes are not tracked and are released immediately
if (shape.ptr != IntPtr.Zero & shape.isNativeShape)
{
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);
}
break;
}
}
}
};
if (inTaintTime)
{
@ -351,19 +319,31 @@ public class BSShapeCollection : IDisposable
// Create the geometry information in Bullet for later use.
// The objects needs a hull if it's physical otherwise a mesh is enough.
// No locking here because this is done when we know physics is not simulating.
// if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used.
// if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls,
// shared geometries will be used. If the parameters of the existing shape are the same
// as this request, the shape is not rebuilt.
// Info in prim.BSShape is updated to the new shape.
// Returns 'true' if the geometry was rebuilt.
// Called at taint-time!
private bool CreateGeom(bool forceRebuild, BSPrim prim, ShapeData shapeData,
private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeData shapeData,
PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback)
{
bool ret = false;
bool haveShape = false;
bool nativeShapePossible = true;
if (shapeData.Type == ShapeData.PhysicsShapeType.SHAPE_AVATAR)
{
// an avatar capsule is close to a native shape (it is not shared)
ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_AVATAR,
ShapeData.FixedShapeKey.KEY_CAPSULE, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.BSShape);
haveShape = true;
}
// If the prim attributes are simple, this could be a simple Bullet native shape
if (nativeShapePossible
if (!haveShape
&& pbs != null
&& nativeShapePossible
&& ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim)
|| (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
&& pbs.ProfileHollow == 0
@ -406,7 +386,7 @@ public class BSShapeCollection : IDisposable
// If a simple shape is not happening, create a mesh and possibly a hull.
// Note that if it's a native shape, the check for physical/non-physical is not
// made. Native shapes are best used in either case.
if (!haveShape)
if (!haveShape && pbs != null)
{
if (prim.IsPhysical && PhysicsScene.ShouldUseHullsForPhysicalObjects)
{
@ -425,12 +405,12 @@ public class BSShapeCollection : IDisposable
return ret;
}
// Creates a native shape and assignes it to prim.BSShape
private bool GetReferenceToNativeShape( BSPrim prim, ShapeData shapeData,
// Creates a native shape and assignes it to prim.BSShape.
// "Native" shapes are never shared. they are created here and destroyed in DereferenceShape().
private bool GetReferenceToNativeShape(BSPhysObject prim, ShapeData shapeData,
ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey,
ShapeDestructionCallback shapeCallback)
{
BulletShape newShape;
shapeData.Type = shapeType;
// Bullet native objects are scaled by the Bullet engine so pass the size in
@ -440,23 +420,42 @@ public class BSShapeCollection : IDisposable
// release any previous shape
DereferenceShape(prim.BSShape, true, shapeCallback);
// Native shapes are always built independently.
newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType);
newShape.shapeKey = (System.UInt64)shapeKey;
newShape.isNativeShape = true;
BulletShape newShape = BuildPhysicalNativeShape(shapeType, shapeData, shapeKey);
// Don't need to do a 'ReferenceShape()' here because native shapes are not tracked.
// DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1}", shapeData.ID, newShape);
// Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
shapeData.ID, newShape, shapeData.Scale);
prim.BSShape = newShape;
return true;
}
private BulletShape BuildPhysicalNativeShape(ShapeData.PhysicsShapeType shapeType,
ShapeData shapeData, ShapeData.FixedShapeKey shapeKey)
{
BulletShape newShape;
if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR)
{
newShape = new BulletShape(
BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1.0f, 1.0f, shapeData.Scale),
shapeType);
}
else
{
newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType);
}
newShape.shapeKey = (System.UInt64)shapeKey;
newShape.isNativeShape = true;
return newShape;
}
// Builds a mesh shape in the physical world and updates prim.BSShape.
// Dereferences previous shape in BSShape and adds a reference for this new shape.
// Returns 'true' of a mesh was actually built. Otherwise .
// Called at taint-time!
private bool GetReferenceToMesh(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs,
private bool GetReferenceToMesh(BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs,
ShapeDestructionCallback shapeCallback)
{
BulletShape newShape = new BulletShape(IntPtr.Zero);
@ -475,6 +474,8 @@ public class BSShapeCollection : IDisposable
DereferenceShape(prim.BSShape, true, shapeCallback);
newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod);
// Take evasive action if the mesh was not constructed.
newShape = VerifyMeshCreated(newShape, prim, shapeData, pbs);
ReferenceShape(newShape);
@ -488,7 +489,7 @@ public class BSShapeCollection : IDisposable
private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
{
IMesh meshData = null;
IntPtr meshPtr;
IntPtr meshPtr = IntPtr.Zero;
MeshDesc meshDesc;
if (Meshes.TryGetValue(newMeshKey, out meshDesc))
{
@ -500,6 +501,8 @@ public class BSShapeCollection : IDisposable
// 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);
if (meshData != null)
{
int[] indices = meshData.getIndexListAsInt();
List<OMV.Vector3> vertices = meshData.getVertexList();
@ -518,6 +521,7 @@ public class BSShapeCollection : IDisposable
meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
indices.GetLength(0), indices, vertices.Count, verticesAsFloats);
}
}
BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH);
newShape.shapeKey = newMeshKey;
@ -526,7 +530,7 @@ public class BSShapeCollection : IDisposable
// See that hull shape exists in the physical world and update prim.BSShape.
// We could be creating the hull because scale changed or whatever.
private bool GetReferenceToHull(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs,
private bool GetReferenceToHull(BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs,
ShapeDestructionCallback shapeCallback)
{
BulletShape newShape;
@ -545,6 +549,7 @@ public class BSShapeCollection : IDisposable
DereferenceShape(prim.BSShape, true, shapeCallback);
newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod);
newShape = VerifyMeshCreated(newShape, prim, shapeData, pbs);
ReferenceShape(newShape);
@ -558,7 +563,7 @@ public class BSShapeCollection : IDisposable
private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
{
IntPtr hullPtr;
IntPtr hullPtr = IntPtr.Zero;
HullDesc hullDesc;
if (Hulls.TryGetValue(newHullKey, out hullDesc))
{
@ -570,6 +575,8 @@ public class BSShapeCollection : IDisposable
// 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);
if (meshData != null)
{
int[] indices = meshData.getIndexListAsInt();
List<OMV.Vector3> vertices = meshData.getVertexList();
@ -651,6 +658,7 @@ public class BSShapeCollection : IDisposable
// create the hull data structure in Bullet
hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
}
}
BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL);
newShape.shapeKey = newHullKey;
@ -690,11 +698,55 @@ public class BSShapeCollection : IDisposable
return ComputeShapeKey(shapeData, pbs, out lod);
}
private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs)
{
// If the shape was successfully created, nothing more to do
if (newShape.ptr != IntPtr.Zero)
return newShape;
// The most common reason for failure is that an underlying asset is not available
// If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
if (pbs.SculptEntry && !prim.LastAssetBuildFailed && pbs.SculptTexture != OMV.UUID.Zero)
{
prim.LastAssetBuildFailed = true;
BSPhysObject xprim = prim;
Util.FireAndForget(delegate
{
RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod;
if (assetProvider != null)
{
BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
{
if (!yprim.BaseShape.SculptEntry)
return;
if (yprim.BaseShape.SculptTexture.ToString() != asset.ID)
return;
yprim.BaseShape.SculptData = new byte[asset.Data.Length];
asset.Data.CopyTo(yprim.BaseShape.SculptData, 0);
// This will cause the prim to see that the filler shape is not the right
// one and try again to build the object.
yprim.ForceBodyShapeRebuild(false);
});
}
});
}
// While we figure out the real problem, stick a simple native shape on the object.
BulletShape fillinShape =
BuildPhysicalNativeShape(ShapeData.PhysicsShapeType.SHAPE_SPHERE, shapeData, ShapeData.FixedShapeKey.KEY_SPHERE);
return fillinShape;
}
// Create a body object in Bullet.
// Updates prim.BSBody with the information about the new body if one is created.
// Returns 'true' if an object was actually created.
// Called at taint-time.
private bool CreateBody(bool forceRebuild, BSPrim prim, BulletSim sim, BulletShape shape,
private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape,
ShapeData shapeData, BodyDestructionCallback bodyCallback)
{
bool ret = false;

View File

@ -114,6 +114,7 @@ public class BSTerrainManager
BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
Vector3.Zero, Quaternion.Identity));
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr);
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_groundPlane.ptr);
// Ground plane does not move
BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION);
// Everything collides with the ground plane.
@ -334,7 +335,8 @@ public class BSTerrainManager
// Make sure the new shape is processed.
// BulletSimAPI.Activate2(mapInfo.terrainBody.ptr, true);
BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.ISLAND_SLEEPING);
// BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
m_terrainModified = true;
};

View File

@ -223,6 +223,7 @@ public struct ShapeData
KEY_SPHERE = 2,
KEY_CONE = 3,
KEY_CYLINDER = 4,
KEY_CAPSULE = 5,
}
}
[StructLayout(LayoutKind.Sequential)]
@ -282,6 +283,7 @@ public struct ConfigurationParameters
public float terrainHitFraction;
public float terrainRestitution;
public float avatarFriction;
public float avatarStandingFriction;
public float avatarDensity;
public float avatarRestitution;
public float avatarCapsuleRadius;
@ -388,7 +390,7 @@ public enum CollisionFilterGroups : uint
VolumeDetectMask = ~BSensorTrigger,
TerrainFilter = BTerrainFilter,
TerrainMask = BAllFilter & ~BStaticFilter,
GroundPlaneFilter = BAllFilter,
GroundPlaneFilter = BGroundPlaneFilter,
GroundPlaneMask = BAllFilter
};
@ -428,6 +430,7 @@ public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string GetVersion();
/* Remove the linkage to the old api methods
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern uint Initialize(Vector3 maxPosition, IntPtr parms,
int maxCollisions, IntPtr collisionArray,
@ -531,7 +534,7 @@ public static extern Vector3 RecoverFromPenetration(uint worldID, uint id);
// ===============================================================================
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpBulletStatistics();
*/
// Log a debug message
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetDebugLogCallback(DebugLogCallback callback);
@ -562,7 +565,8 @@ public static extern IntPtr GetBodyHandle2(IntPtr world, uint id);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr Initialize2(Vector3 maxPosition, IntPtr parms,
int maxCollisions, IntPtr collisionArray,
int maxUpdates, IntPtr updateArray);
int maxUpdates, IntPtr updateArray,
DebugLogCallback logRoutine);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool UpdateParameter2(IntPtr world, uint localID, String parm, float value);
@ -603,6 +607,9 @@ public static extern IntPtr BuildNativeShape2(IntPtr world, ShapeData shapeData)
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsNativeShape2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateCompoundShape2(IntPtr sim);

View File

@ -66,6 +66,14 @@ namespace OpenSim.Region.Physics.OdePlugin
public int ExpectedCollisionContacts { get { return m_expectedCollisionContacts; } }
private int m_expectedCollisionContacts = 0;
/// <summary>
/// Gets collide bits so that we can still perform land collisions if a mesh fails to load.
/// </summary>
private int BadMeshAssetCollideBits
{
get { return m_isphysical ? (int)CollisionCategories.Land : 0; }
}
/// <summary>
/// Is this prim subject to physics? Even if not, it's still solid for collision purposes.
/// </summary>
@ -344,11 +352,10 @@ namespace OpenSim.Region.Physics.OdePlugin
if (m_assetFailed)
{
d.GeomSetCategoryBits(prim_geom, 0);
d.GeomSetCollideBits(prim_geom, BadAssetColideBits());
d.GeomSetCollideBits(prim_geom, BadMeshAssetCollideBits);
}
else
{
d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
}
@ -418,7 +425,7 @@ namespace OpenSim.Region.Physics.OdePlugin
if (m_assetFailed)
{
d.GeomSetCategoryBits(prim_geom, 0);
d.GeomSetCollideBits(prim_geom, BadAssetColideBits());
d.GeomSetCollideBits(prim_geom, BadMeshAssetCollideBits);
}
else
{
@ -851,11 +858,6 @@ namespace OpenSim.Region.Physics.OdePlugin
private static Dictionary<IMesh, IntPtr> m_MeshToTriMeshMap = new Dictionary<IMesh, IntPtr>();
public int BadAssetColideBits()
{
return (m_isphysical ? (int)CollisionCategories.Land : 0);
}
private void setMesh(OdeScene parent_scene, IMesh mesh)
{
// m_log.DebugFormat("[ODE PRIM]: Setting mesh on {0} to {1}", Name, mesh);
@ -1137,7 +1139,7 @@ Console.WriteLine("ZProcessTaints for " + Name);
if (prm.m_assetFailed)
{
d.GeomSetCategoryBits(prm.prim_geom, 0);
d.GeomSetCollideBits(prm.prim_geom, prm.BadAssetColideBits());
d.GeomSetCollideBits(prm.prim_geom, prm.BadMeshAssetCollideBits);
}
else
{
@ -1191,7 +1193,7 @@ Console.WriteLine("ZProcessTaints for " + Name);
if (m_assetFailed)
{
d.GeomSetCategoryBits(prim_geom, 0);
d.GeomSetCollideBits(prim_geom, BadAssetColideBits());
d.GeomSetCollideBits(prim_geom, BadMeshAssetCollideBits);
}
else
{
@ -1393,7 +1395,7 @@ Console.WriteLine("ZProcessTaints for " + Name);
if (m_assetFailed)
{
d.GeomSetCategoryBits(prim_geom, 0);
d.GeomSetCollideBits(prim_geom, BadAssetColideBits());
d.GeomSetCollideBits(prim_geom, BadMeshAssetCollideBits);
}
else
{
@ -2137,7 +2139,7 @@ Console.WriteLine(" JointCreateFixed");
}
if (m_assetFailed)
d.GeomSetCollideBits(prim_geom, BadAssetColideBits());
d.GeomSetCollideBits(prim_geom, BadMeshAssetCollideBits);
else
d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);

View File

@ -128,7 +128,7 @@ namespace OpenSim.Services.Connectors.Friends
return Call(region, sendData);
}
public bool StatusNotify(GridRegion region, UUID userID, UUID friendID, bool online)
public bool StatusNotify(GridRegion region, UUID userID, string friendID, bool online)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
//sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString();
@ -136,7 +136,7 @@ namespace OpenSim.Services.Connectors.Friends
sendData["METHOD"] = "status";
sendData["FromID"] = userID.ToString();
sendData["ToID"] = friendID.ToString();
sendData["ToID"] = friendID;
sendData["Online"] = online.ToString();
return Call(region, sendData);

View File

@ -397,7 +397,7 @@ namespace OpenSim.Services.HypergridService
if (region != null)
{
m_log.DebugFormat("[HGFRIENDS SERVICE]: Remote Notify to region {0}, user {1} is {2}", region.RegionName, foreignUserID, (online ? "online" : "offline"));
m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID, online);
m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID.ToString(), online);
}
}
}

View File

@ -504,7 +504,7 @@ namespace OpenSim.Services.HypergridService
if (region != null)
{
m_log.DebugFormat("[USER AGENT SERVICE]: Remote Notify to region {0}, user {1} is {2}", region.RegionName, foreignUserID, (online ? "online" : "offline"));
m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID, online);
m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID.ToString(), online);
}
}
}

View File

@ -61,13 +61,49 @@ namespace OpenSim.Services.Interfaces
public interface IPresenceService
{
/// <summary>
/// Store session information.
/// </summary>
/// <returns>/returns>
/// <param name='userID'></param>
/// <param name='sessionID'></param>
/// <param name='secureSessionID'></param>
bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID);
/// <summary>
/// Remove session information.
/// </summary>
/// <returns></returns>
/// <param name='sessionID'></param>
bool LogoutAgent(UUID sessionID);
/// <summary>
/// Remove session information for all agents in the given region.
/// </summary>
/// <returns></returns>
/// <param name='regionID'></param>
bool LogoutRegionAgents(UUID regionID);
/// <summary>
/// Update data for an existing session.
/// </summary>
/// <returns></returns>
/// <param name='sessionID'></param>
/// <param name='regionID'></param>
bool ReportAgent(UUID sessionID, UUID regionID);
/// <summary>
/// Get session information for a given session ID.
/// </summary>
/// <returns></returns>
/// <param name='sessionID'></param>
PresenceInfo GetAgent(UUID sessionID);
/// <summary>
/// Get session information for a collection of users.
/// </summary>
/// <returns>Session information for the users.</returns>
/// <param name='userIDs'></param>
PresenceInfo[] GetAgents(string[] userIDs);
}
}

View File

@ -1577,6 +1577,11 @@
MessagingModule = GroupsMessagingModule
;MessagingEnabled = true
; Experimental option to only message cached online users rather than all users
; Should make large group with few online members messaging faster, as the expense of more calls to ROBUST presence service
; This currently only applies to the Flotsam XmlRpc backend
MessageOnlineUsersOnly = false
; Service connectors to the Groups Service. Select one depending on whether you're using a Flotsam XmlRpc backend or a SimianGrid backend
; SimianGrid Service for Groups

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.