diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index c29e8953f7..687c02ae1c 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -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; diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 1fa5ae2eb3..e0983e86d5 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -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; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 24d04de034..239c4b37b9 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -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; diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index 90ed9aebc0..26839f376f 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -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; diff --git a/OpenSim/Region/Framework/Scenes/EventManager.cs b/OpenSim/Region/Framework/Scenes/EventManager.cs index 8621f701d9..7496af0662 100644 --- a/OpenSim/Region/Framework/Scenes/EventManager.cs +++ b/OpenSim/Region/Framework/Scenes/EventManager.cs @@ -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; diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 0fe27a54f3..54c420d484 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -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; diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index c2f9fd2445..e6328dc3b9 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -1341,6 +1341,47 @@ namespace OpenSim.Region.Framework.Scenes } } + /// + /// Start spinning the given object + /// + /// + /// + /// + 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); + } + } + } + + /// + /// Spin the given object + /// + /// + /// + /// + 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(); + } + } + /// /// /// diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 1834215262..4fdc71ee7f 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -2298,6 +2298,106 @@ namespace OpenSim.Region.Framework.Scenes m_rootPart.SendTerseUpdateToAllClients(); } + /// + /// If object is physical, prepare for spinning torques (set flag to save old orientation) + /// + /// Rotation. We do the math here to turn it into a torque + /// + 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; + } + } + } + } + + /// + /// If object is physical, apply torque to spin it around + /// + /// Rotation. We do the math here to turn it into a torque + /// + 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); + } + } + } + /// /// Return metadata about a prim (name, description, sale price, etc.) /// diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 2a5a563715..1a5dd6d991 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -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(); + /// /// This part's inventory /// diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index 644f429842..edd6b673fe 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -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; diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index e82c26df1f..4647349040 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -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;