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
parent
e8dfa00d8c
commit
8e6c20b27f
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue