Handle ObjectSpin* packets to spin physical prims on Ctrl+Shift+Drag

Addresses Mantis #3381

The current implementation works as expected if the object has no rotation or 
only rotation around the Z axis; you can spin the object left or right (around
the world Z axis).

It works a little unexpectedly if the object has a non-Z-axis rotation; in this
case the body is spun about its local Z axis, not the world Z-axis. (But SL 
also behaves oddly with a spin on an arbitrarily rotated object.)
0.6.5-rc1
nlin 2009-04-10 06:39:52 +00:00
parent e8dfa00d8c
commit 8e6c20b27f
11 changed files with 198 additions and 3 deletions

View File

@ -607,6 +607,9 @@ namespace OpenSim.Client.MXP.ClientStack
public event GrabObject OnGrabObject;
public event ObjectSelect OnDeGrabObject;
public event MoveObject OnGrabUpdate;
public event SpinStart OnSpinStart;
public event SpinObject OnSpinUpdate;
public event SpinStop OnSpinStop;
public event UpdateShape OnUpdatePrimShape;
public event ObjectExtraParams OnUpdateExtraParams;
public event ObjectSelect OnObjectSelect;

View File

@ -160,7 +160,7 @@ namespace OpenSim.Framework
);
public delegate void SpinStart(UUID objectID, IClientAPI remoteClient);
public delegate void SpinUpdate(UUID objectID, Quaternion rotation, IClientAPI remoteClient);
public delegate void SpinObject(UUID objectID, Quaternion rotation, IClientAPI remoteClient);
public delegate void SpinStop(UUID objectID, IClientAPI remoteClient);
public delegate void ParcelAccessListRequest(
@ -607,6 +607,9 @@ namespace OpenSim.Framework
event GrabObject OnGrabObject;
event ObjectSelect OnDeGrabObject;
event MoveObject OnGrabUpdate;
event SpinStart OnSpinStart;
event SpinObject OnSpinUpdate;
event SpinStop OnSpinStop;
event UpdateShape OnUpdatePrimShape;
event ObjectExtraParams OnUpdateExtraParams;

View File

@ -172,7 +172,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private MoveObject handlerGrabUpdate; //OnGrabUpdate;
private ObjectSelect handlerDeGrabObject; //OnDeGrabObject;
private SpinStart handlerSpinStart; //OnSpinStart;
private SpinUpdate handlerSpinUpdate; //OnSpinUpdate;
private SpinObject handlerSpinUpdate; //OnSpinUpdate;
private SpinStop handlerSpinStop; //OnSpinStop;
private GenericCall7 handlerObjectDescription;
private GenericCall7 handlerObjectName;
@ -939,7 +939,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public event ObjectDuplicate OnObjectDuplicate;
public event ObjectDuplicateOnRay OnObjectDuplicateOnRay;
public event MoveObject OnGrabUpdate;
public event SpinUpdate OnSpinUpdate;
public event SpinObject OnSpinUpdate;
public event AddNewPrim OnAddPrim;
public event RequestGodlikePowers OnRequestGodlikePowers;
public event GodKickUser OnGodKickUser;

View File

@ -96,6 +96,9 @@ namespace OpenSim.Region.Examples.SimpleModule
public event GrabObject OnGrabObject;
public event ObjectSelect OnDeGrabObject;
public event MoveObject OnGrabUpdate;
public event SpinStart OnSpinStart;
public event SpinObject OnSpinUpdate;
public event SpinStop OnSpinStop;
public event ViewerEffectEventHandler OnViewerEffect;
public event FetchInventory OnAgentDataUpdateRequest;

View File

@ -132,6 +132,14 @@ namespace OpenSim.Region.Framework.Scenes
public event SceneGroupGrabed OnSceneGroupGrab;
public delegate bool SceneGroupSpinStarted(UUID groupID);
public event SceneGroupSpinStarted OnSceneGroupSpinStart;
public delegate bool SceneGroupSpun(UUID groupID, Quaternion rotation);
public event SceneGroupSpun OnSceneGroupSpin;
public delegate void LandObjectAdded(ILandObject newParcel);
public event LandObjectAdded OnLandObjectAdded;
@ -381,6 +389,8 @@ namespace OpenSim.Region.Framework.Scenes
private StopScript handlerStopScript = null; //OnStopScript;
private SceneGroupMoved handlerSceneGroupMove = null; //OnSceneGroupMove;
private SceneGroupGrabed handlerSceneGroupGrab = null; //OnSceneGroupGrab;
private SceneGroupSpinStarted handlerSceneGroupSpinStarted = null; //OnSceneGroupSpinStart;
private SceneGroupSpun handlerSceneGroupSpin = null; //OnSceneGroupSpin;
private LandObjectAdded handlerLandObjectAdded = null; //OnLandObjectAdded;
private LandObjectRemoved handlerLandObjectRemoved = null; //OnLandObjectRemoved;
private AvatarEnteringNewParcel handlerAvatarEnteringNewParcel = null; //OnAvatarEnteringNewParcel;
@ -636,6 +646,28 @@ namespace OpenSim.Region.Framework.Scenes
return true;
}
public bool TriggerGroupSpinStart(UUID groupID)
{
handlerSceneGroupSpinStarted = OnSceneGroupSpinStart;
if (handlerSceneGroupSpinStarted != null)
{
return handlerSceneGroupSpinStarted(groupID);
}
return true;
}
public bool TriggerGroupSpin(UUID groupID, Quaternion rotation)
{
handlerSceneGroupSpin = OnSceneGroupSpin;
if (handlerSceneGroupSpin != null)
{
return handlerSceneGroupSpin(groupID, rotation);
}
return true;
}
public void TriggerGroupGrab(UUID groupID, Vector3 offset, UUID userID)
{
handlerSceneGroupGrab = OnSceneGroupGrab;

View File

@ -1933,6 +1933,8 @@ namespace OpenSim.Region.Framework.Scenes
client.OnObjectSelect += SelectPrim;
client.OnObjectDeselect += DeselectPrim;
client.OnGrabUpdate += m_sceneGraph.MoveObject;
client.OnSpinStart += m_sceneGraph.SpinStart;
client.OnSpinUpdate += m_sceneGraph.SpinObject;
client.OnDeRezObject += DeRezObject;
client.OnRezObject += RezObject;
client.OnRezSingleAttachmentFromInv += RezSingleAttachment;

View File

@ -1341,6 +1341,47 @@ namespace OpenSim.Region.Framework.Scenes
}
}
/// <summary>
/// Start spinning the given object
/// </summary>
/// <param name="objectID"></param>
/// <param name="rotation"></param>
/// <param name="remoteClient"></param>
protected internal void SpinStart(UUID objectID, IClientAPI remoteClient)
{
SceneObjectGroup group = GetGroupByPrim(objectID);
if (group != null)
{
if (m_parentScene.Permissions.CanMoveObject(group.UUID, remoteClient.AgentId))// && PermissionsMngr.)
{
group.SpinStart(remoteClient);
}
}
}
/// <summary>
/// Spin the given object
/// </summary>
/// <param name="objectID"></param>
/// <param name="rotation"></param>
/// <param name="remoteClient"></param>
protected internal void SpinObject(UUID objectID, Quaternion rotation, IClientAPI remoteClient)
{
SceneObjectGroup group = GetGroupByPrim(objectID);
if (group != null)
{
if (m_parentScene.Permissions.CanMoveObject(group.UUID, remoteClient.AgentId))// && PermissionsMngr.)
{
group.SpinMovement(rotation, remoteClient);
}
// This is outside the above permissions condition
// so that if the object is locked the client moving the object
// get's it's position on the simulator even if it was the same as before
// This keeps the moving user's client in sync with the rest of the world.
group.SendGroupTerseUpdate();
}
}
/// <summary>
///
/// </summary>

View File

@ -2298,6 +2298,106 @@ namespace OpenSim.Region.Framework.Scenes
m_rootPart.SendTerseUpdateToAllClients();
}
/// <summary>
/// If object is physical, prepare for spinning torques (set flag to save old orientation)
/// </summary>
/// <param name="rotation">Rotation. We do the math here to turn it into a torque</param>
/// <param name="remoteClient"></param>
public void SpinStart(IClientAPI remoteClient)
{
if (m_scene.EventManager.TriggerGroupSpinStart(UUID))
{
if (m_rootPart.PhysActor != null)
{
if (m_rootPart.PhysActor.IsPhysical)
{
m_rootPart.IsWaitingForFirstSpinUpdatePacket = true;
}
}
}
}
/// <summary>
/// If object is physical, apply torque to spin it around
/// </summary>
/// <param name="rotation">Rotation. We do the math here to turn it into a torque</param>
/// <param name="remoteClient"></param>
public void SpinMovement(Quaternion newOrientation, IClientAPI remoteClient)
{
// The incoming newOrientation, sent by the client, "seems" to be the
// desired target orientation. This needs further verification; in particular,
// one would expect that the initial incoming newOrientation should be
// fairly close to the original prim's physical orientation,
// m_rootPart.PhysActor.Orientation. This however does not seem to be the
// case (might just be an issue with different quaternions representing the
// same rotation, or it might be a coordinate system issue).
//
// Since it's not clear what the relationship is between the PhysActor.Orientation
// and the incoming orientations sent by the client, we take an alternative approach
// of calculating the delta rotation between the orientations being sent by the
// client. (Since a spin is invoked by ctrl+shift+drag in the client, we expect
// a steady stream of several new orientations coming in from the client.)
// This ensures that the delta rotations are being calculated from self-consistent
// pairs of old/new rotations. Given the delta rotation, we apply a torque around
// the delta rotation axis, scaled by the object mass times an arbitrary scaling
// factor (to ensure the resulting torque is not "too strong" or "too weak").
//
// Ideally we need to calculate (probably iteratively) the exact torque or series
// of torques needed to arrive exactly at the destination orientation. However, since
// it is not yet clear how to map the destination orientation (provided by the viewer)
// into PhysActor orientations (needed by the physics engine), we omit this step.
// This means that the resulting torque will at least be in the correct direction,
// but it will result in over-shoot or under-shoot of the target orientation.
// For the end user, this means that ctrl+shift+drag can be used for relative,
// but not absolute, adjustments of orientation for physical prims.
if (m_scene.EventManager.TriggerGroupSpin(UUID, newOrientation))
{
if (m_rootPart.PhysActor != null)
{
if (m_rootPart.PhysActor.IsPhysical)
{
if(m_rootPart.IsWaitingForFirstSpinUpdatePacket)
{
// first time initialization of "old" orientation for calculation of delta rotations
m_rootPart.SpinOldOrientation = newOrientation;
m_rootPart.IsWaitingForFirstSpinUpdatePacket = false;
}
else
{
// save and update old orientation
Quaternion old = m_rootPart.SpinOldOrientation;
m_rootPart.SpinOldOrientation = newOrientation;
//m_log.Error("[SCENE OBJECT GROUP]: Old orientation is " + old);
//m_log.Error("[SCENE OBJECT GROUP]: Incoming new orientation is " + newOrientation);
// compute difference between previous old rotation and new incoming rotation
Quaternion minimalRotationFromQ1ToQ2 = Quaternion.Inverse(old) * newOrientation;
float rotationAngle;
Vector3 rotationAxis;
minimalRotationFromQ1ToQ2.GetAxisAngle(out rotationAxis, out rotationAngle);
rotationAxis.Normalize();
//m_log.Error("SCENE OBJECT GROUP]: rotation axis is " + rotationAxis);
PhysicsVector spinforce = new PhysicsVector(rotationAxis.X, rotationAxis.Y, rotationAxis.Z);
spinforce = (spinforce/8) * m_rootPart.PhysActor.Mass; // 8 is an arbitrary torque scaling factor
m_rootPart.PhysActor.AddAngularForce(spinforce,true);
m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
}
}
else
{
//NonPhysicalSpinMovement(pos);
}
}
else
{
//NonPhysicalSpinMovement(pos);
}
}
}
/// <summary>
/// Return metadata about a prim (name, description, sale price, etc.)
/// </summary>

View File

@ -176,6 +176,11 @@ namespace OpenSim.Region.Framework.Scenes
public bool VolumeDetectActive = false; // XmlIgnore set to avoid problems with persistance until I come to care for this
// Certainly this must be a persistant setting finally
[XmlIgnore]
public bool IsWaitingForFirstSpinUpdatePacket = false;
[XmlIgnore]
public Quaternion SpinOldOrientation = new Quaternion();
/// <summary>
/// This part's inventory
/// </summary>

View File

@ -202,6 +202,9 @@ namespace OpenSim.Region.OptionalModules.World.NPC
public event GrabObject OnGrabObject;
public event ObjectSelect OnDeGrabObject;
public event MoveObject OnGrabUpdate;
public event SpinStart OnSpinStart;
public event SpinObject OnSpinUpdate;
public event SpinStop OnSpinStop;
public event ViewerEffectEventHandler OnViewerEffect;
public event FetchInventory OnAgentDataUpdateRequest;

View File

@ -107,6 +107,9 @@ namespace OpenSim.Tests.Common.Mock
public event GrabObject OnGrabObject;
public event ObjectSelect OnDeGrabObject;
public event MoveObject OnGrabUpdate;
public event SpinStart OnSpinStart;
public event SpinObject OnSpinUpdate;
public event SpinStop OnSpinStop;
public event ViewerEffectEventHandler OnViewerEffect;
public event FetchInventory OnAgentDataUpdateRequest;