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 GrabObject OnGrabObject;
|
||||||
public event ObjectSelect OnDeGrabObject;
|
public event ObjectSelect OnDeGrabObject;
|
||||||
public event MoveObject OnGrabUpdate;
|
public event MoveObject OnGrabUpdate;
|
||||||
|
public event SpinStart OnSpinStart;
|
||||||
|
public event SpinObject OnSpinUpdate;
|
||||||
|
public event SpinStop OnSpinStop;
|
||||||
public event UpdateShape OnUpdatePrimShape;
|
public event UpdateShape OnUpdatePrimShape;
|
||||||
public event ObjectExtraParams OnUpdateExtraParams;
|
public event ObjectExtraParams OnUpdateExtraParams;
|
||||||
public event ObjectSelect OnObjectSelect;
|
public event ObjectSelect OnObjectSelect;
|
||||||
|
|
|
@ -160,7 +160,7 @@ namespace OpenSim.Framework
|
||||||
);
|
);
|
||||||
|
|
||||||
public delegate void SpinStart(UUID objectID, IClientAPI remoteClient);
|
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 SpinStop(UUID objectID, IClientAPI remoteClient);
|
||||||
|
|
||||||
public delegate void ParcelAccessListRequest(
|
public delegate void ParcelAccessListRequest(
|
||||||
|
@ -607,6 +607,9 @@ namespace OpenSim.Framework
|
||||||
event GrabObject OnGrabObject;
|
event GrabObject OnGrabObject;
|
||||||
event ObjectSelect OnDeGrabObject;
|
event ObjectSelect OnDeGrabObject;
|
||||||
event MoveObject OnGrabUpdate;
|
event MoveObject OnGrabUpdate;
|
||||||
|
event SpinStart OnSpinStart;
|
||||||
|
event SpinObject OnSpinUpdate;
|
||||||
|
event SpinStop OnSpinStop;
|
||||||
|
|
||||||
event UpdateShape OnUpdatePrimShape;
|
event UpdateShape OnUpdatePrimShape;
|
||||||
event ObjectExtraParams OnUpdateExtraParams;
|
event ObjectExtraParams OnUpdateExtraParams;
|
||||||
|
|
|
@ -172,7 +172,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
private MoveObject handlerGrabUpdate; //OnGrabUpdate;
|
private MoveObject handlerGrabUpdate; //OnGrabUpdate;
|
||||||
private ObjectSelect handlerDeGrabObject; //OnDeGrabObject;
|
private ObjectSelect handlerDeGrabObject; //OnDeGrabObject;
|
||||||
private SpinStart handlerSpinStart; //OnSpinStart;
|
private SpinStart handlerSpinStart; //OnSpinStart;
|
||||||
private SpinUpdate handlerSpinUpdate; //OnSpinUpdate;
|
private SpinObject handlerSpinUpdate; //OnSpinUpdate;
|
||||||
private SpinStop handlerSpinStop; //OnSpinStop;
|
private SpinStop handlerSpinStop; //OnSpinStop;
|
||||||
private GenericCall7 handlerObjectDescription;
|
private GenericCall7 handlerObjectDescription;
|
||||||
private GenericCall7 handlerObjectName;
|
private GenericCall7 handlerObjectName;
|
||||||
|
@ -939,7 +939,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
public event ObjectDuplicate OnObjectDuplicate;
|
public event ObjectDuplicate OnObjectDuplicate;
|
||||||
public event ObjectDuplicateOnRay OnObjectDuplicateOnRay;
|
public event ObjectDuplicateOnRay OnObjectDuplicateOnRay;
|
||||||
public event MoveObject OnGrabUpdate;
|
public event MoveObject OnGrabUpdate;
|
||||||
public event SpinUpdate OnSpinUpdate;
|
public event SpinObject OnSpinUpdate;
|
||||||
public event AddNewPrim OnAddPrim;
|
public event AddNewPrim OnAddPrim;
|
||||||
public event RequestGodlikePowers OnRequestGodlikePowers;
|
public event RequestGodlikePowers OnRequestGodlikePowers;
|
||||||
public event GodKickUser OnGodKickUser;
|
public event GodKickUser OnGodKickUser;
|
||||||
|
|
|
@ -96,6 +96,9 @@ namespace OpenSim.Region.Examples.SimpleModule
|
||||||
public event GrabObject OnGrabObject;
|
public event GrabObject OnGrabObject;
|
||||||
public event ObjectSelect OnDeGrabObject;
|
public event ObjectSelect OnDeGrabObject;
|
||||||
public event MoveObject OnGrabUpdate;
|
public event MoveObject OnGrabUpdate;
|
||||||
|
public event SpinStart OnSpinStart;
|
||||||
|
public event SpinObject OnSpinUpdate;
|
||||||
|
public event SpinStop OnSpinStop;
|
||||||
public event ViewerEffectEventHandler OnViewerEffect;
|
public event ViewerEffectEventHandler OnViewerEffect;
|
||||||
|
|
||||||
public event FetchInventory OnAgentDataUpdateRequest;
|
public event FetchInventory OnAgentDataUpdateRequest;
|
||||||
|
|
|
@ -132,6 +132,14 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
public event SceneGroupGrabed OnSceneGroupGrab;
|
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 delegate void LandObjectAdded(ILandObject newParcel);
|
||||||
|
|
||||||
public event LandObjectAdded OnLandObjectAdded;
|
public event LandObjectAdded OnLandObjectAdded;
|
||||||
|
@ -381,6 +389,8 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
private StopScript handlerStopScript = null; //OnStopScript;
|
private StopScript handlerStopScript = null; //OnStopScript;
|
||||||
private SceneGroupMoved handlerSceneGroupMove = null; //OnSceneGroupMove;
|
private SceneGroupMoved handlerSceneGroupMove = null; //OnSceneGroupMove;
|
||||||
private SceneGroupGrabed handlerSceneGroupGrab = null; //OnSceneGroupGrab;
|
private SceneGroupGrabed handlerSceneGroupGrab = null; //OnSceneGroupGrab;
|
||||||
|
private SceneGroupSpinStarted handlerSceneGroupSpinStarted = null; //OnSceneGroupSpinStart;
|
||||||
|
private SceneGroupSpun handlerSceneGroupSpin = null; //OnSceneGroupSpin;
|
||||||
private LandObjectAdded handlerLandObjectAdded = null; //OnLandObjectAdded;
|
private LandObjectAdded handlerLandObjectAdded = null; //OnLandObjectAdded;
|
||||||
private LandObjectRemoved handlerLandObjectRemoved = null; //OnLandObjectRemoved;
|
private LandObjectRemoved handlerLandObjectRemoved = null; //OnLandObjectRemoved;
|
||||||
private AvatarEnteringNewParcel handlerAvatarEnteringNewParcel = null; //OnAvatarEnteringNewParcel;
|
private AvatarEnteringNewParcel handlerAvatarEnteringNewParcel = null; //OnAvatarEnteringNewParcel;
|
||||||
|
@ -636,6 +646,28 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
return true;
|
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)
|
public void TriggerGroupGrab(UUID groupID, Vector3 offset, UUID userID)
|
||||||
{
|
{
|
||||||
handlerSceneGroupGrab = OnSceneGroupGrab;
|
handlerSceneGroupGrab = OnSceneGroupGrab;
|
||||||
|
|
|
@ -1933,6 +1933,8 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
client.OnObjectSelect += SelectPrim;
|
client.OnObjectSelect += SelectPrim;
|
||||||
client.OnObjectDeselect += DeselectPrim;
|
client.OnObjectDeselect += DeselectPrim;
|
||||||
client.OnGrabUpdate += m_sceneGraph.MoveObject;
|
client.OnGrabUpdate += m_sceneGraph.MoveObject;
|
||||||
|
client.OnSpinStart += m_sceneGraph.SpinStart;
|
||||||
|
client.OnSpinUpdate += m_sceneGraph.SpinObject;
|
||||||
client.OnDeRezObject += DeRezObject;
|
client.OnDeRezObject += DeRezObject;
|
||||||
client.OnRezObject += RezObject;
|
client.OnRezObject += RezObject;
|
||||||
client.OnRezSingleAttachmentFromInv += RezSingleAttachment;
|
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>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -2298,6 +2298,106 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
m_rootPart.SendTerseUpdateToAllClients();
|
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>
|
/// <summary>
|
||||||
/// Return metadata about a prim (name, description, sale price, etc.)
|
/// Return metadata about a prim (name, description, sale price, etc.)
|
||||||
/// </summary>
|
/// </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
|
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
|
// Certainly this must be a persistant setting finally
|
||||||
|
|
||||||
|
[XmlIgnore]
|
||||||
|
public bool IsWaitingForFirstSpinUpdatePacket = false;
|
||||||
|
[XmlIgnore]
|
||||||
|
public Quaternion SpinOldOrientation = new Quaternion();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This part's inventory
|
/// This part's inventory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -202,6 +202,9 @@ namespace OpenSim.Region.OptionalModules.World.NPC
|
||||||
public event GrabObject OnGrabObject;
|
public event GrabObject OnGrabObject;
|
||||||
public event ObjectSelect OnDeGrabObject;
|
public event ObjectSelect OnDeGrabObject;
|
||||||
public event MoveObject OnGrabUpdate;
|
public event MoveObject OnGrabUpdate;
|
||||||
|
public event SpinStart OnSpinStart;
|
||||||
|
public event SpinObject OnSpinUpdate;
|
||||||
|
public event SpinStop OnSpinStop;
|
||||||
public event ViewerEffectEventHandler OnViewerEffect;
|
public event ViewerEffectEventHandler OnViewerEffect;
|
||||||
|
|
||||||
public event FetchInventory OnAgentDataUpdateRequest;
|
public event FetchInventory OnAgentDataUpdateRequest;
|
||||||
|
|
|
@ -107,6 +107,9 @@ namespace OpenSim.Tests.Common.Mock
|
||||||
public event GrabObject OnGrabObject;
|
public event GrabObject OnGrabObject;
|
||||||
public event ObjectSelect OnDeGrabObject;
|
public event ObjectSelect OnDeGrabObject;
|
||||||
public event MoveObject OnGrabUpdate;
|
public event MoveObject OnGrabUpdate;
|
||||||
|
public event SpinStart OnSpinStart;
|
||||||
|
public event SpinObject OnSpinUpdate;
|
||||||
|
public event SpinStop OnSpinStop;
|
||||||
public event ViewerEffectEventHandler OnViewerEffect;
|
public event ViewerEffectEventHandler OnViewerEffect;
|
||||||
|
|
||||||
public event FetchInventory OnAgentDataUpdateRequest;
|
public event FetchInventory OnAgentDataUpdateRequest;
|
||||||
|
|
Loading…
Reference in New Issue