diff options
author | CasperW | 2009-11-21 15:36:38 +0100 |
---|---|---|
committer | Melanie | 2009-11-21 16:50:33 +0000 |
commit | 0149265ee83581cf2fb150dcd5d8734c02926261 (patch) | |
tree | 2671c9d6d6ba862aa542bf8f5254f59b5556b6b5 /OpenSim/Region/Framework/Scenes | |
parent | Merge branch 'master' into careminster (diff) | |
download | opensim-SC_OLD-0149265ee83581cf2fb150dcd5d8734c02926261.zip opensim-SC_OLD-0149265ee83581cf2fb150dcd5d8734c02926261.tar.gz opensim-SC_OLD-0149265ee83581cf2fb150dcd5d8734c02926261.tar.bz2 opensim-SC_OLD-0149265ee83581cf2fb150dcd5d8734c02926261.tar.xz |
Improved avatar responsiveness.
Diffstat (limited to 'OpenSim/Region/Framework/Scenes')
-rw-r--r-- | OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs | 4 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Scenes/Scene.cs | 10 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Scenes/ScenePresence.cs | 80 |
3 files changed, 72 insertions, 22 deletions
diff --git a/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs b/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs index cbe4118..2230fba 100644 --- a/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs +++ b/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs | |||
@@ -156,8 +156,8 @@ namespace OpenSim.Region.Framework.Scenes.Animation | |||
156 | Vector3 left = Vector3.Transform(Vector3.UnitY, rotMatrix); | 156 | Vector3 left = Vector3.Transform(Vector3.UnitY, rotMatrix); |
157 | 157 | ||
158 | // Check control flags | 158 | // Check control flags |
159 | bool heldForward = (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_AT_POS; | 159 | bool heldForward = ((controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_AT_POS || (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_POS); |
160 | bool heldBack = (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG; | 160 | bool heldBack = ((controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG || (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_NEG); |
161 | bool heldLeft = (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS; | 161 | bool heldLeft = (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS; |
162 | bool heldRight = (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG; | 162 | bool heldRight = (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG; |
163 | //bool heldTurnLeft = (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT; | 163 | //bool heldTurnLeft = (controlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT; |
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index a430b1e..5058457 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs | |||
@@ -1164,16 +1164,16 @@ namespace OpenSim.Region.Framework.Scenes | |||
1164 | // Check if any objects have reached their targets | 1164 | // Check if any objects have reached their targets |
1165 | CheckAtTargets(); | 1165 | CheckAtTargets(); |
1166 | 1166 | ||
1167 | // Update SceneObjectGroups that have scheduled themselves for updates | ||
1168 | // Objects queue their updates onto all scene presences | ||
1169 | if (m_frame % m_update_objects == 0) | ||
1170 | m_sceneGraph.UpdateObjectGroups(); | ||
1171 | |||
1172 | // Run through all ScenePresences looking for updates | 1167 | // Run through all ScenePresences looking for updates |
1173 | // Presence updates and queued object updates for each presence are sent to clients | 1168 | // Presence updates and queued object updates for each presence are sent to clients |
1174 | if (m_frame % m_update_presences == 0) | 1169 | if (m_frame % m_update_presences == 0) |
1175 | m_sceneGraph.UpdatePresences(); | 1170 | m_sceneGraph.UpdatePresences(); |
1176 | 1171 | ||
1172 | // Update SceneObjectGroups that have scheduled themselves for updates | ||
1173 | // Objects queue their updates onto all scene presences | ||
1174 | if (m_frame % m_update_objects == 0) | ||
1175 | m_sceneGraph.UpdateObjectGroups(); | ||
1176 | |||
1177 | int TempPhysicsMS2 = Environment.TickCount; | 1177 | int TempPhysicsMS2 = Environment.TickCount; |
1178 | if ((m_frame % m_update_physics == 0) && m_physics_enabled) | 1178 | if ((m_frame % m_update_physics == 0) && m_physics_enabled) |
1179 | m_sceneGraph.UpdatePreparePhysics(); | 1179 | m_sceneGraph.UpdatePreparePhysics(); |
diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 424c25b..99f3141 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs | |||
@@ -73,7 +73,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
73 | // { | 73 | // { |
74 | // m_log.Debug("[ScenePresence] Destructor called"); | 74 | // m_log.Debug("[ScenePresence] Destructor called"); |
75 | // } | 75 | // } |
76 | 76 | ||
77 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 77 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
78 | 78 | ||
79 | private static readonly byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20 }; | 79 | private static readonly byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20 }; |
@@ -144,7 +144,6 @@ namespace OpenSim.Region.Framework.Scenes | |||
144 | private int m_perfMonMS; | 144 | private int m_perfMonMS; |
145 | 145 | ||
146 | private bool m_setAlwaysRun; | 146 | private bool m_setAlwaysRun; |
147 | |||
148 | private bool m_forceFly; | 147 | private bool m_forceFly; |
149 | private bool m_flyDisabled; | 148 | private bool m_flyDisabled; |
150 | 149 | ||
@@ -166,7 +165,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
166 | protected RegionInfo m_regionInfo; | 165 | protected RegionInfo m_regionInfo; |
167 | protected ulong crossingFromRegion; | 166 | protected ulong crossingFromRegion; |
168 | 167 | ||
169 | private readonly Vector3[] Dir_Vectors = new Vector3[6]; | 168 | private readonly Vector3[] Dir_Vectors = new Vector3[9]; |
169 | private bool m_isNudging = false; | ||
170 | 170 | ||
171 | // Position of agent's camera in world (region cordinates) | 171 | // Position of agent's camera in world (region cordinates) |
172 | protected Vector3 m_CameraCenter; | 172 | protected Vector3 m_CameraCenter; |
@@ -230,6 +230,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
230 | DIR_CONTROL_FLAG_RIGHT = AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG, | 230 | DIR_CONTROL_FLAG_RIGHT = AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG, |
231 | DIR_CONTROL_FLAG_UP = AgentManager.ControlFlags.AGENT_CONTROL_UP_POS, | 231 | DIR_CONTROL_FLAG_UP = AgentManager.ControlFlags.AGENT_CONTROL_UP_POS, |
232 | DIR_CONTROL_FLAG_DOWN = AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG, | 232 | DIR_CONTROL_FLAG_DOWN = AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG, |
233 | DIR_CONTROL_FLAG_FORWARD_NUDGE = AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_POS, | ||
234 | DIR_CONTROL_FLAG_BACK_NUDGE = AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_NEG, | ||
233 | DIR_CONTROL_FLAG_DOWN_NUDGE = AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG | 235 | DIR_CONTROL_FLAG_DOWN_NUDGE = AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG |
234 | } | 236 | } |
235 | 237 | ||
@@ -714,21 +716,41 @@ namespace OpenSim.Region.Framework.Scenes | |||
714 | Dir_Vectors[3] = -Vector3.UnitY; //RIGHT | 716 | Dir_Vectors[3] = -Vector3.UnitY; //RIGHT |
715 | Dir_Vectors[4] = Vector3.UnitZ; //UP | 717 | Dir_Vectors[4] = Vector3.UnitZ; //UP |
716 | Dir_Vectors[5] = -Vector3.UnitZ; //DOWN | 718 | Dir_Vectors[5] = -Vector3.UnitZ; //DOWN |
717 | Dir_Vectors[5] = new Vector3(0f, 0f, -0.5f); //DOWN_Nudge | 719 | Dir_Vectors[6] = new Vector3(0.5f, 0f, 0f); //FORWARD_NUDGE |
720 | Dir_Vectors[7] = new Vector3(-0.5f, 0f, 0f); //BACK_NUDGE | ||
721 | Dir_Vectors[8] = new Vector3(0f, 0f, -0.5f); //DOWN_Nudge | ||
718 | } | 722 | } |
719 | 723 | ||
720 | private Vector3[] GetWalkDirectionVectors() | 724 | private Vector3[] GetWalkDirectionVectors() |
721 | { | 725 | { |
722 | Vector3[] vector = new Vector3[6]; | 726 | Vector3[] vector = new Vector3[9]; |
723 | vector[0] = new Vector3(m_CameraUpAxis.Z, 0f, -m_CameraAtAxis.Z); //FORWARD | 727 | vector[0] = new Vector3(m_CameraUpAxis.Z, 0f, -m_CameraAtAxis.Z); //FORWARD |
724 | vector[1] = new Vector3(-m_CameraUpAxis.Z, 0f, m_CameraAtAxis.Z); //BACK | 728 | vector[1] = new Vector3(-m_CameraUpAxis.Z, 0f, m_CameraAtAxis.Z); //BACK |
725 | vector[2] = Vector3.UnitY; //LEFT | 729 | vector[2] = Vector3.UnitY; //LEFT |
726 | vector[3] = -Vector3.UnitY; //RIGHT | 730 | vector[3] = -Vector3.UnitY; //RIGHT |
727 | vector[4] = new Vector3(m_CameraAtAxis.Z, 0f, m_CameraUpAxis.Z); //UP | 731 | vector[4] = new Vector3(m_CameraAtAxis.Z, 0f, m_CameraUpAxis.Z); //UP |
728 | vector[5] = new Vector3(-m_CameraAtAxis.Z, 0f, -m_CameraUpAxis.Z); //DOWN | 732 | vector[5] = new Vector3(-m_CameraAtAxis.Z, 0f, -m_CameraUpAxis.Z); //DOWN |
729 | vector[5] = new Vector3(-m_CameraAtAxis.Z, 0f, -m_CameraUpAxis.Z); //DOWN_Nudge | 733 | vector[6] = new Vector3(m_CameraUpAxis.Z, 0f, -m_CameraAtAxis.Z); //FORWARD_NUDGE |
734 | vector[7] = new Vector3(-m_CameraUpAxis.Z, 0f, m_CameraAtAxis.Z); //BACK_NUDGE | ||
735 | vector[8] = new Vector3(-m_CameraAtAxis.Z, 0f, -m_CameraUpAxis.Z); //DOWN_Nudge | ||
730 | return vector; | 736 | return vector; |
731 | } | 737 | } |
738 | |||
739 | private bool[] GetDirectionIsNudge() | ||
740 | { | ||
741 | bool[] isNudge = new bool[9]; | ||
742 | isNudge[0] = false; //FORWARD | ||
743 | isNudge[1] = false; //BACK | ||
744 | isNudge[2] = false; //LEFT | ||
745 | isNudge[3] = false; //RIGHT | ||
746 | isNudge[4] = false; //UP | ||
747 | isNudge[5] = false; //DOWN | ||
748 | isNudge[6] = true; //FORWARD_NUDGE | ||
749 | isNudge[7] = true; //BACK_NUDGE | ||
750 | isNudge[8] = true; //DOWN_Nudge | ||
751 | return isNudge; | ||
752 | } | ||
753 | |||
732 | 754 | ||
733 | #endregion | 755 | #endregion |
734 | 756 | ||
@@ -1147,7 +1169,6 @@ namespace OpenSim.Region.Framework.Scenes | |||
1147 | // // m_log.Debug("DEBUG: HandleAgentUpdate: child agent"); | 1169 | // // m_log.Debug("DEBUG: HandleAgentUpdate: child agent"); |
1148 | // return; | 1170 | // return; |
1149 | //} | 1171 | //} |
1150 | |||
1151 | m_perfMonMS = Environment.TickCount; | 1172 | m_perfMonMS = Environment.TickCount; |
1152 | 1173 | ||
1153 | ++m_movementUpdateCount; | 1174 | ++m_movementUpdateCount; |
@@ -1229,7 +1250,6 @@ namespace OpenSim.Region.Framework.Scenes | |||
1229 | m_scene.PhysicsScene.RaycastWorld(m_pos, Vector3.Normalize(m_CameraCenter - posAdjusted), Vector3.Distance(m_CameraCenter, posAdjusted) + 0.3f, RayCastCameraCallback); | 1250 | m_scene.PhysicsScene.RaycastWorld(m_pos, Vector3.Normalize(m_CameraCenter - posAdjusted), Vector3.Distance(m_CameraCenter, posAdjusted) + 0.3f, RayCastCameraCallback); |
1230 | } | 1251 | } |
1231 | } | 1252 | } |
1232 | |||
1233 | lock (scriptedcontrols) | 1253 | lock (scriptedcontrols) |
1234 | { | 1254 | { |
1235 | if (scriptedcontrols.Count > 0) | 1255 | if (scriptedcontrols.Count > 0) |
@@ -1261,7 +1281,6 @@ namespace OpenSim.Region.Framework.Scenes | |||
1261 | { | 1281 | { |
1262 | return; | 1282 | return; |
1263 | } | 1283 | } |
1264 | |||
1265 | if (m_allowMovement) | 1284 | if (m_allowMovement) |
1266 | { | 1285 | { |
1267 | int i = 0; | 1286 | int i = 0; |
@@ -1289,6 +1308,11 @@ namespace OpenSim.Region.Framework.Scenes | |||
1289 | update_rotation = true; | 1308 | update_rotation = true; |
1290 | } | 1309 | } |
1291 | 1310 | ||
1311 | //guilty until proven innocent.. | ||
1312 | bool Nudging = true; | ||
1313 | //Basically, if there is at least one non-nudge control then we don't need | ||
1314 | //to worry about stopping the avatar | ||
1315 | |||
1292 | if (m_parentID == 0) | 1316 | if (m_parentID == 0) |
1293 | { | 1317 | { |
1294 | bool bAllowUpdateMoveToPosition = false; | 1318 | bool bAllowUpdateMoveToPosition = false; |
@@ -1303,6 +1327,12 @@ namespace OpenSim.Region.Framework.Scenes | |||
1303 | else | 1327 | else |
1304 | dirVectors = Dir_Vectors; | 1328 | dirVectors = Dir_Vectors; |
1305 | 1329 | ||
1330 | bool[] isNudge = GetDirectionIsNudge(); | ||
1331 | |||
1332 | |||
1333 | |||
1334 | |||
1335 | |||
1306 | foreach (Dir_ControlFlags DCF in DIR_CONTROL_FLAGS) | 1336 | foreach (Dir_ControlFlags DCF in DIR_CONTROL_FLAGS) |
1307 | { | 1337 | { |
1308 | if (((uint)flags & (uint)DCF) != 0) | 1338 | if (((uint)flags & (uint)DCF) != 0) |
@@ -1312,6 +1342,10 @@ namespace OpenSim.Region.Framework.Scenes | |||
1312 | try | 1342 | try |
1313 | { | 1343 | { |
1314 | agent_control_v3 += dirVectors[i]; | 1344 | agent_control_v3 += dirVectors[i]; |
1345 | if (isNudge[i] == false) | ||
1346 | { | ||
1347 | Nudging = false; | ||
1348 | } | ||
1315 | } | 1349 | } |
1316 | catch (IndexOutOfRangeException) | 1350 | catch (IndexOutOfRangeException) |
1317 | { | 1351 | { |
@@ -1373,6 +1407,9 @@ namespace OpenSim.Region.Framework.Scenes | |||
1373 | // Ignore z component of vector | 1407 | // Ignore z component of vector |
1374 | Vector3 LocalVectorToTarget2D = new Vector3((float)(LocalVectorToTarget3D.X), (float)(LocalVectorToTarget3D.Y), 0f); | 1408 | Vector3 LocalVectorToTarget2D = new Vector3((float)(LocalVectorToTarget3D.X), (float)(LocalVectorToTarget3D.Y), 0f); |
1375 | LocalVectorToTarget2D.Normalize(); | 1409 | LocalVectorToTarget2D.Normalize(); |
1410 | |||
1411 | //We're not nudging | ||
1412 | Nudging = false; | ||
1376 | agent_control_v3 += LocalVectorToTarget2D; | 1413 | agent_control_v3 += LocalVectorToTarget2D; |
1377 | 1414 | ||
1378 | // update avatar movement flags. the avatar coordinate system is as follows: | 1415 | // update avatar movement flags. the avatar coordinate system is as follows: |
@@ -1450,7 +1487,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
1450 | // m_log.DebugFormat( | 1487 | // m_log.DebugFormat( |
1451 | // "In {0} adding velocity to {1} of {2}", m_scene.RegionInfo.RegionName, Name, agent_control_v3); | 1488 | // "In {0} adding velocity to {1} of {2}", m_scene.RegionInfo.RegionName, Name, agent_control_v3); |
1452 | 1489 | ||
1453 | AddNewMovement(agent_control_v3, q); | 1490 | AddNewMovement(agent_control_v3, q, Nudging); |
1454 | 1491 | ||
1455 | if (update_movementflag) | 1492 | if (update_movementflag) |
1456 | Animator.UpdateMovementAnimations(); | 1493 | Animator.UpdateMovementAnimations(); |
@@ -1886,7 +1923,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
1886 | /// </summary> | 1923 | /// </summary> |
1887 | /// <param name="vec">The vector in which to move. This is relative to the rotation argument</param> | 1924 | /// <param name="vec">The vector in which to move. This is relative to the rotation argument</param> |
1888 | /// <param name="rotation">The direction in which this avatar should now face. | 1925 | /// <param name="rotation">The direction in which this avatar should now face. |
1889 | public void AddNewMovement(Vector3 vec, Quaternion rotation) | 1926 | public void AddNewMovement(Vector3 vec, Quaternion rotation, bool Nudging) |
1890 | { | 1927 | { |
1891 | if (m_isChildAgent) | 1928 | if (m_isChildAgent) |
1892 | { | 1929 | { |
@@ -1960,7 +1997,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
1960 | 1997 | ||
1961 | // TODO: Add the force instead of only setting it to support multiple forces per frame? | 1998 | // TODO: Add the force instead of only setting it to support multiple forces per frame? |
1962 | m_forceToApply = direc; | 1999 | m_forceToApply = direc; |
1963 | 2000 | m_isNudging = Nudging; | |
1964 | m_scene.StatsReporter.AddAgentTime(Environment.TickCount - m_perfMonMS); | 2001 | m_scene.StatsReporter.AddAgentTime(Environment.TickCount - m_perfMonMS); |
1965 | } | 2002 | } |
1966 | 2003 | ||
@@ -1975,7 +2012,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
1975 | const float POSITION_TOLERANCE = 0.05f; | 2012 | const float POSITION_TOLERANCE = 0.05f; |
1976 | //const int TIME_MS_TOLERANCE = 3000; | 2013 | //const int TIME_MS_TOLERANCE = 3000; |
1977 | 2014 | ||
1978 | SendPrimUpdates(); | 2015 | |
1979 | 2016 | ||
1980 | if (m_newCoarseLocations) | 2017 | if (m_newCoarseLocations) |
1981 | { | 2018 | { |
@@ -2011,6 +2048,9 @@ namespace OpenSim.Region.Framework.Scenes | |||
2011 | CheckForBorderCrossing(); | 2048 | CheckForBorderCrossing(); |
2012 | CheckForSignificantMovement(); // sends update to the modules. | 2049 | CheckForSignificantMovement(); // sends update to the modules. |
2013 | } | 2050 | } |
2051 | |||
2052 | //Sending prim updates AFTER the avatar terse updates are sent | ||
2053 | SendPrimUpdates(); | ||
2014 | } | 2054 | } |
2015 | 2055 | ||
2016 | #endregion | 2056 | #endregion |
@@ -2864,14 +2904,24 @@ namespace OpenSim.Region.Framework.Scenes | |||
2864 | { | 2904 | { |
2865 | if (m_forceToApply.HasValue) | 2905 | if (m_forceToApply.HasValue) |
2866 | { | 2906 | { |
2867 | Vector3 force = m_forceToApply.Value; | ||
2868 | 2907 | ||
2908 | Vector3 force = m_forceToApply.Value; | ||
2869 | m_updateflag = true; | 2909 | m_updateflag = true; |
2870 | // movementvector = force; | ||
2871 | Velocity = force; | 2910 | Velocity = force; |
2872 | 2911 | ||
2873 | m_forceToApply = null; | 2912 | m_forceToApply = null; |
2874 | } | 2913 | } |
2914 | else | ||
2915 | { | ||
2916 | if (m_isNudging) | ||
2917 | { | ||
2918 | Vector3 force = Vector3.Zero; | ||
2919 | |||
2920 | m_updateflag = true; | ||
2921 | Velocity = force; | ||
2922 | m_isNudging = false; | ||
2923 | } | ||
2924 | } | ||
2875 | } | 2925 | } |
2876 | 2926 | ||
2877 | public override void SetText(string text, Vector3 color, double alpha) | 2927 | public override void SetText(string text, Vector3 color, double alpha) |