From 8e6c20b27fdb95b9008614eb36678508407a4d19 Mon Sep 17 00:00:00 2001
From: nlin
Date: Fri, 10 Apr 2009 06:39:52 +0000
Subject: 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.)
---
.../Region/ClientStack/LindenUDP/LLClientView.cs | 4 +-
.../Region/Examples/SimpleModule/MyNpcCharacter.cs | 3 +
OpenSim/Region/Framework/Scenes/EventManager.cs | 32 +++++++
OpenSim/Region/Framework/Scenes/Scene.cs | 2 +
OpenSim/Region/Framework/Scenes/SceneGraph.cs | 41 +++++++++
.../Region/Framework/Scenes/SceneObjectGroup.cs | 100 +++++++++++++++++++++
OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | 5 ++
.../Region/OptionalModules/World/NPC/NPCAvatar.cs | 3 +
8 files changed, 188 insertions(+), 2 deletions(-)
(limited to 'OpenSim/Region')
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 24d04de..239c4b3 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 90ed9ae..26839f3 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 8621f70..7496af0 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 0fe27a5..54c420d 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 c2f9fd2..e6328dc 100644
--- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs
@@ -1342,6 +1342,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 1834215..4fdc71e 100644
--- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
@@ -2299,6 +2299,106 @@ namespace OpenSim.Region.Framework.Scenes
}
///
+ /// 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 2a5a563..1a5dd6d 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 644f429..edd6b67 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;
--
cgit v1.1