diff options
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/Scene.cs')
-rw-r--r-- | OpenSim/Region/Framework/Scenes/Scene.cs | 146 |
1 files changed, 92 insertions, 54 deletions
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 5005ac9..42051d0 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs | |||
@@ -61,6 +61,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
61 | Time = 0, | 61 | Time = 0, |
62 | Distance = 1, | 62 | Distance = 1, |
63 | SimpleAngularDistance = 2, | 63 | SimpleAngularDistance = 2, |
64 | FrontBack = 3, | ||
64 | } | 65 | } |
65 | 66 | ||
66 | public delegate void SynchronizeSceneHandler(Scene scene); | 67 | public delegate void SynchronizeSceneHandler(Scene scene); |
@@ -81,8 +82,6 @@ namespace OpenSim.Region.Framework.Scenes | |||
81 | 82 | ||
82 | protected Timer m_restartWaitTimer = new Timer(); | 83 | protected Timer m_restartWaitTimer = new Timer(); |
83 | 84 | ||
84 | protected Thread m_updateEntitiesThread; | ||
85 | |||
86 | public SimStatsReporter StatsReporter; | 85 | public SimStatsReporter StatsReporter; |
87 | 86 | ||
88 | protected List<RegionInfo> m_regionRestartNotifyList = new List<RegionInfo>(); | 87 | protected List<RegionInfo> m_regionRestartNotifyList = new List<RegionInfo>(); |
@@ -107,11 +106,11 @@ namespace OpenSim.Region.Framework.Scenes | |||
107 | public bool m_physicalPrim; | 106 | public bool m_physicalPrim; |
108 | public float m_maxNonphys = 256; | 107 | public float m_maxNonphys = 256; |
109 | public float m_maxPhys = 10; | 108 | public float m_maxPhys = 10; |
110 | public bool m_clampPrimSize = false; | 109 | public bool m_clampPrimSize; |
111 | public bool m_trustBinaries = false; | 110 | public bool m_trustBinaries; |
112 | public bool m_allowScriptCrossings = false; | 111 | public bool m_allowScriptCrossings; |
113 | public bool m_useFlySlow = false; | 112 | public bool m_useFlySlow; |
114 | public bool m_usePreJump = false; | 113 | public bool m_usePreJump; |
115 | public bool m_seeIntoRegionFromNeighbor; | 114 | public bool m_seeIntoRegionFromNeighbor; |
116 | // TODO: need to figure out how allow client agents but deny | 115 | // TODO: need to figure out how allow client agents but deny |
117 | // root agents when ACL denies access to root agent | 116 | // root agents when ACL denies access to root agent |
@@ -119,11 +118,11 @@ namespace OpenSim.Region.Framework.Scenes | |||
119 | public int MaxUndoCount = 5; | 118 | public int MaxUndoCount = 5; |
120 | private int m_RestartTimerCounter; | 119 | private int m_RestartTimerCounter; |
121 | private readonly Timer m_restartTimer = new Timer(15000); // Wait before firing | 120 | private readonly Timer m_restartTimer = new Timer(15000); // Wait before firing |
122 | private int m_incrementsof15seconds = 0; | 121 | private int m_incrementsof15seconds; |
123 | private volatile bool m_backingup = false; | 122 | private volatile bool m_backingup; |
123 | private bool m_useAsyncWhenPossible; | ||
124 | 124 | ||
125 | private Dictionary<UUID, ReturnInfo> m_returns = new Dictionary<UUID, ReturnInfo>(); | 125 | private Dictionary<UUID, ReturnInfo> m_returns = new Dictionary<UUID, ReturnInfo>(); |
126 | |||
127 | private Dictionary<UUID, SceneObjectGroup> m_groupsWithTargets = new Dictionary<UUID, SceneObjectGroup>(); | 126 | private Dictionary<UUID, SceneObjectGroup> m_groupsWithTargets = new Dictionary<UUID, SceneObjectGroup>(); |
128 | 127 | ||
129 | protected string m_simulatorVersion = "OpenSimulator Server"; | 128 | protected string m_simulatorVersion = "OpenSimulator Server"; |
@@ -143,8 +142,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
143 | 142 | ||
144 | public IXfer XferManager; | 143 | public IXfer XferManager; |
145 | 144 | ||
146 | protected IAssetService m_AssetService = null; | 145 | protected IAssetService m_AssetService; |
147 | protected IAuthorizationService m_AuthorizationService = null; | 146 | protected IAuthorizationService m_AuthorizationService; |
148 | 147 | ||
149 | private Object m_heartbeatLock = new Object(); | 148 | private Object m_heartbeatLock = new Object(); |
150 | 149 | ||
@@ -185,7 +184,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
185 | } | 184 | } |
186 | } | 185 | } |
187 | 186 | ||
188 | protected IInventoryService m_InventoryService = null; | 187 | protected IInventoryService m_InventoryService; |
189 | 188 | ||
190 | public IInventoryService InventoryService | 189 | public IInventoryService InventoryService |
191 | { | 190 | { |
@@ -205,7 +204,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
205 | } | 204 | } |
206 | } | 205 | } |
207 | 206 | ||
208 | protected IGridService m_GridService = null; | 207 | protected IGridService m_GridService; |
209 | 208 | ||
210 | public IGridService GridService | 209 | public IGridService GridService |
211 | { | 210 | { |
@@ -253,9 +252,9 @@ namespace OpenSim.Region.Framework.Scenes | |||
253 | // Central Update Loop | 252 | // Central Update Loop |
254 | 253 | ||
255 | protected int m_fps = 10; | 254 | protected int m_fps = 10; |
256 | protected int m_frame = 0; | 255 | protected int m_frame; |
257 | protected float m_timespan = 0.089f; | 256 | protected float m_timespan = 0.089f; |
258 | protected DateTime m_lastupdate = DateTime.Now; | 257 | protected DateTime m_lastupdate = DateTime.UtcNow; |
259 | 258 | ||
260 | private int m_update_physics = 1; | 259 | private int m_update_physics = 1; |
261 | private int m_update_entitymovement = 1; | 260 | private int m_update_entitymovement = 1; |
@@ -266,17 +265,17 @@ namespace OpenSim.Region.Framework.Scenes | |||
266 | private int m_update_terrain = 50; | 265 | private int m_update_terrain = 50; |
267 | private int m_update_land = 1; | 266 | private int m_update_land = 1; |
268 | 267 | ||
269 | private int frameMS = 0; | 268 | private int frameMS; |
270 | private int physicsMS2 = 0; | 269 | private int physicsMS2; |
271 | private int physicsMS = 0; | 270 | private int physicsMS; |
272 | private int otherMS = 0; | 271 | private int otherMS; |
273 | 272 | ||
274 | private bool m_physics_enabled = true; | 273 | private bool m_physics_enabled = true; |
275 | private bool m_scripts_enabled = true; | 274 | private bool m_scripts_enabled = true; |
276 | private string m_defaultScriptEngine; | 275 | private string m_defaultScriptEngine; |
277 | private int m_LastLogin = 0; | 276 | private int m_LastLogin; |
278 | private Thread HeartbeatThread = null; | 277 | private Thread HeartbeatThread; |
279 | private volatile bool shuttingdown = false; | 278 | private volatile bool shuttingdown; |
280 | 279 | ||
281 | private int m_lastUpdate = Environment.TickCount; | 280 | private int m_lastUpdate = Environment.TickCount; |
282 | private bool m_firstHeartbeat = true; | 281 | private bool m_firstHeartbeat = true; |
@@ -480,6 +479,9 @@ namespace OpenSim.Region.Framework.Scenes | |||
480 | // | 479 | // |
481 | IConfig startupConfig = m_config.Configs["Startup"]; | 480 | IConfig startupConfig = m_config.Configs["Startup"]; |
482 | 481 | ||
482 | // Should we try to run loops synchronously or asynchronously? | ||
483 | m_useAsyncWhenPossible = startupConfig.GetBoolean("use_async_when_possible", false); | ||
484 | |||
483 | //Animation states | 485 | //Animation states |
484 | m_useFlySlow = startupConfig.GetBoolean("enableflyslow", false); | 486 | m_useFlySlow = startupConfig.GetBoolean("enableflyslow", false); |
485 | // TODO: Change default to true once the feature is supported | 487 | // TODO: Change default to true once the feature is supported |
@@ -542,6 +544,9 @@ namespace OpenSim.Region.Framework.Scenes | |||
542 | case "simpleangulardistance": | 544 | case "simpleangulardistance": |
543 | m_update_prioritization_scheme = UpdatePrioritizationSchemes.SimpleAngularDistance; | 545 | m_update_prioritization_scheme = UpdatePrioritizationSchemes.SimpleAngularDistance; |
544 | break; | 546 | break; |
547 | case "frontback": | ||
548 | m_update_prioritization_scheme = UpdatePrioritizationSchemes.FrontBack; | ||
549 | break; | ||
545 | default: | 550 | default: |
546 | m_log.Warn("[SCENE]: UpdatePrioritizationScheme was not recognized, setting to default settomg of Time"); | 551 | m_log.Warn("[SCENE]: UpdatePrioritizationScheme was not recognized, setting to default settomg of Time"); |
547 | m_update_prioritization_scheme = UpdatePrioritizationSchemes.Time; | 552 | m_update_prioritization_scheme = UpdatePrioritizationSchemes.Time; |
@@ -889,6 +894,9 @@ namespace OpenSim.Region.Framework.Scenes | |||
889 | { | 894 | { |
890 | m_log.InfoFormat("[SCENE]: Closing down the single simulator: {0}", RegionInfo.RegionName); | 895 | m_log.InfoFormat("[SCENE]: Closing down the single simulator: {0}", RegionInfo.RegionName); |
891 | 896 | ||
897 | m_restartTimer.Stop(); | ||
898 | m_restartTimer.Close(); | ||
899 | |||
892 | // Kick all ROOT agents with the message, 'The simulator is going down' | 900 | // Kick all ROOT agents with the message, 'The simulator is going down' |
893 | ForEachScenePresence(delegate(ScenePresence avatar) | 901 | ForEachScenePresence(delegate(ScenePresence avatar) |
894 | { | 902 | { |
@@ -945,11 +953,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
945 | HeartbeatThread = null; | 953 | HeartbeatThread = null; |
946 | } | 954 | } |
947 | m_lastUpdate = Environment.TickCount; | 955 | m_lastUpdate = Environment.TickCount; |
948 | HeartbeatThread = new Thread(new ParameterizedThreadStart(Heartbeat)); | 956 | |
949 | HeartbeatThread.SetApartmentState(ApartmentState.MTA); | 957 | HeartbeatThread = Watchdog.StartThread(Heartbeat, "Heartbeat for region " + RegionInfo.RegionName, ThreadPriority.Normal, false); |
950 | HeartbeatThread.Name = string.Format("Heartbeat for region {0}", RegionInfo.RegionName); | ||
951 | HeartbeatThread.Priority = ThreadPriority.AboveNormal; | ||
952 | HeartbeatThread.Start(); | ||
953 | } | 958 | } |
954 | 959 | ||
955 | /// <summary> | 960 | /// <summary> |
@@ -976,12 +981,13 @@ namespace OpenSim.Region.Framework.Scenes | |||
976 | /// <summary> | 981 | /// <summary> |
977 | /// Performs per-frame updates regularly | 982 | /// Performs per-frame updates regularly |
978 | /// </summary> | 983 | /// </summary> |
979 | /// <param name="sender"></param> | 984 | private void Heartbeat() |
980 | /// <param name="e"></param> | ||
981 | private void Heartbeat(object sender) | ||
982 | { | 985 | { |
983 | if (!Monitor.TryEnter(m_heartbeatLock)) | 986 | if (!Monitor.TryEnter(m_heartbeatLock)) |
987 | { | ||
988 | Watchdog.RemoveThread(); | ||
984 | return; | 989 | return; |
990 | } | ||
985 | 991 | ||
986 | try | 992 | try |
987 | { | 993 | { |
@@ -998,6 +1004,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
998 | Monitor.Pulse(m_heartbeatLock); | 1004 | Monitor.Pulse(m_heartbeatLock); |
999 | Monitor.Exit(m_heartbeatLock); | 1005 | Monitor.Exit(m_heartbeatLock); |
1000 | } | 1006 | } |
1007 | |||
1008 | Watchdog.RemoveThread(); | ||
1001 | } | 1009 | } |
1002 | 1010 | ||
1003 | /// <summary> | 1011 | /// <summary> |
@@ -1016,10 +1024,11 @@ namespace OpenSim.Region.Framework.Scenes | |||
1016 | //#endif | 1024 | //#endif |
1017 | maintc = Environment.TickCount; | 1025 | maintc = Environment.TickCount; |
1018 | 1026 | ||
1019 | TimeSpan SinceLastFrame = DateTime.Now - m_lastupdate; | 1027 | TimeSpan SinceLastFrame = DateTime.UtcNow - m_lastupdate; |
1020 | float physicsFPS = 0; | 1028 | float physicsFPS = 0; |
1021 | 1029 | ||
1022 | frameMS = Environment.TickCount; | 1030 | frameMS = Environment.TickCount; |
1031 | |||
1023 | try | 1032 | try |
1024 | { | 1033 | { |
1025 | // Increment the frame counter | 1034 | // Increment the frame counter |
@@ -1101,6 +1110,11 @@ namespace OpenSim.Region.Framework.Scenes | |||
1101 | } | 1110 | } |
1102 | if (loginsdisabled && (m_frame > 20)) | 1111 | if (loginsdisabled && (m_frame > 20)) |
1103 | { | 1112 | { |
1113 | // In 99.9% of cases it is a bad idea to manually force garbage collection. However, | ||
1114 | // this is a rare case where we know we have just went through a long cycle of heap | ||
1115 | // allocations, and there is no more work to be done until someone logs in | ||
1116 | GC.Collect(); | ||
1117 | |||
1104 | m_log.Debug("[REGION]: Enabling Logins"); | 1118 | m_log.Debug("[REGION]: Enabling Logins"); |
1105 | loginsdisabled = false; | 1119 | loginsdisabled = false; |
1106 | } | 1120 | } |
@@ -1139,13 +1153,16 @@ namespace OpenSim.Region.Framework.Scenes | |||
1139 | } | 1153 | } |
1140 | m_timedilation = tmpval; | 1154 | m_timedilation = tmpval; |
1141 | 1155 | ||
1142 | m_lastupdate = DateTime.Now; | 1156 | m_lastupdate = DateTime.UtcNow; |
1143 | } | 1157 | } |
1144 | maintc = Environment.TickCount - maintc; | 1158 | maintc = Environment.TickCount - maintc; |
1145 | maintc = (int)(m_timespan * 1000) - maintc; | 1159 | maintc = (int)(m_timespan * 1000) - maintc; |
1146 | 1160 | ||
1147 | if ((maintc < (m_timespan * 1000)) && maintc > 0) | 1161 | if ((maintc < (m_timespan * 1000)) && maintc > 0) |
1148 | Thread.Sleep(maintc); | 1162 | Thread.Sleep(maintc); |
1163 | |||
1164 | // Tell the watchdog that this thread is still alive | ||
1165 | Watchdog.UpdateThread(); | ||
1149 | } | 1166 | } |
1150 | } | 1167 | } |
1151 | 1168 | ||
@@ -1219,10 +1236,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
1219 | if (!m_backingup) | 1236 | if (!m_backingup) |
1220 | { | 1237 | { |
1221 | m_backingup = true; | 1238 | m_backingup = true; |
1222 | 1239 | Util.FireAndForget(BackupWaitCallback); | |
1223 | System.ComponentModel.BackgroundWorker backupWorker = new System.ComponentModel.BackgroundWorker(); | ||
1224 | backupWorker.DoWork += delegate(object sender, System.ComponentModel.DoWorkEventArgs e) { Backup(); }; | ||
1225 | backupWorker.RunWorkerAsync(); | ||
1226 | } | 1240 | } |
1227 | } | 1241 | } |
1228 | 1242 | ||
@@ -1235,6 +1249,14 @@ namespace OpenSim.Region.Framework.Scenes | |||
1235 | } | 1249 | } |
1236 | 1250 | ||
1237 | /// <summary> | 1251 | /// <summary> |
1252 | /// Wrapper for Backup() that can be called with Util.FireAndForget() | ||
1253 | /// </summary> | ||
1254 | private void BackupWaitCallback(object o) | ||
1255 | { | ||
1256 | Backup(); | ||
1257 | } | ||
1258 | |||
1259 | /// <summary> | ||
1238 | /// Backup the scene. This acts as the main method of the backup thread. | 1260 | /// Backup the scene. This acts as the main method of the backup thread. |
1239 | /// </summary> | 1261 | /// </summary> |
1240 | /// <returns></returns> | 1262 | /// <returns></returns> |
@@ -2460,7 +2482,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
2460 | /// <param name="client"></param> | 2482 | /// <param name="client"></param> |
2461 | public override void AddNewClient(IClientAPI client) | 2483 | public override void AddNewClient(IClientAPI client) |
2462 | { | 2484 | { |
2463 | ClientManager.Add(client); | 2485 | m_clientManager.Add(client); |
2464 | 2486 | ||
2465 | CheckHeartbeat(); | 2487 | CheckHeartbeat(); |
2466 | SubscribeToClientEvents(client); | 2488 | SubscribeToClientEvents(client); |
@@ -3099,7 +3121,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
3099 | 3121 | ||
3100 | // Remove the avatar from the scene | 3122 | // Remove the avatar from the scene |
3101 | m_sceneGraph.RemoveScenePresence(agentID); | 3123 | m_sceneGraph.RemoveScenePresence(agentID); |
3102 | ClientManager.Remove(agentID); | 3124 | m_clientManager.Remove(agentID); |
3103 | 3125 | ||
3104 | try | 3126 | try |
3105 | { | 3127 | { |
@@ -3496,9 +3518,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
3496 | public virtual void AgentCrossing(UUID agentID, Vector3 position, bool isFlying) | 3518 | public virtual void AgentCrossing(UUID agentID, Vector3 position, bool isFlying) |
3497 | { | 3519 | { |
3498 | ScenePresence presence; | 3520 | ScenePresence presence; |
3499 | 3521 | m_sceneGraph.TryGetAvatar(agentID, out presence); | |
3500 | lock (m_sceneGraph.ScenePresences) | ||
3501 | m_sceneGraph.ScenePresences.TryGetValue(agentID, out presence); | ||
3502 | 3522 | ||
3503 | if (presence != null) | 3523 | if (presence != null) |
3504 | { | 3524 | { |
@@ -3709,8 +3729,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
3709 | Vector3 lookAt, uint teleportFlags) | 3729 | Vector3 lookAt, uint teleportFlags) |
3710 | { | 3730 | { |
3711 | ScenePresence sp; | 3731 | ScenePresence sp; |
3712 | lock (m_sceneGraph.ScenePresences) | 3732 | m_sceneGraph.TryGetAvatar(remoteClient.AgentId, out sp); |
3713 | m_sceneGraph.ScenePresences.TryGetValue(remoteClient.AgentId, out sp); | ||
3714 | 3733 | ||
3715 | if (sp != null) | 3734 | if (sp != null) |
3716 | { | 3735 | { |
@@ -4112,7 +4131,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
4112 | /// This list is a new object, so it can be iterated over without locking. | 4131 | /// This list is a new object, so it can be iterated over without locking. |
4113 | /// </summary> | 4132 | /// </summary> |
4114 | /// <returns></returns> | 4133 | /// <returns></returns> |
4115 | public List<ScenePresence> GetScenePresences() | 4134 | public ScenePresence[] GetScenePresences() |
4116 | { | 4135 | { |
4117 | return m_sceneGraph.GetScenePresences(); | 4136 | return m_sceneGraph.GetScenePresences(); |
4118 | } | 4137 | } |
@@ -4159,15 +4178,13 @@ namespace OpenSim.Region.Framework.Scenes | |||
4159 | public void ForEachScenePresence(Action<ScenePresence> action) | 4178 | public void ForEachScenePresence(Action<ScenePresence> action) |
4160 | { | 4179 | { |
4161 | // We don't want to try to send messages if there are no avatars. | 4180 | // We don't want to try to send messages if there are no avatars. |
4162 | if (m_sceneGraph != null && m_sceneGraph.ScenePresences != null) | 4181 | if (m_sceneGraph != null) |
4163 | { | 4182 | { |
4164 | try | 4183 | try |
4165 | { | 4184 | { |
4166 | List<ScenePresence> presenceList = GetScenePresences(); | 4185 | ScenePresence[] presences = GetScenePresences(); |
4167 | foreach (ScenePresence presence in presenceList) | 4186 | for (int i = 0; i < presences.Length; i++) |
4168 | { | 4187 | action(presences[i]); |
4169 | action(presence); | ||
4170 | } | ||
4171 | } | 4188 | } |
4172 | catch (Exception e) | 4189 | catch (Exception e) |
4173 | { | 4190 | { |
@@ -4239,7 +4256,28 @@ namespace OpenSim.Region.Framework.Scenes | |||
4239 | 4256 | ||
4240 | public void ForEachClient(Action<IClientAPI> action) | 4257 | public void ForEachClient(Action<IClientAPI> action) |
4241 | { | 4258 | { |
4242 | ClientManager.ForEach(action); | 4259 | ForEachClient(action, m_useAsyncWhenPossible); |
4260 | } | ||
4261 | |||
4262 | public void ForEachClient(Action<IClientAPI> action, bool doAsynchronous) | ||
4263 | { | ||
4264 | // FIXME: Asynchronous iteration is disabled until we have a threading model that | ||
4265 | // can support calling this function from an async packet handler without | ||
4266 | // potentially deadlocking | ||
4267 | //if (doAsynchronous) | ||
4268 | // m_clientManager.ForEach(action); | ||
4269 | //else | ||
4270 | // m_clientManager.ForEachSync(action); | ||
4271 | } | ||
4272 | |||
4273 | public bool TryGetClient(UUID avatarID, out IClientAPI client) | ||
4274 | { | ||
4275 | return m_clientManager.TryGetValue(avatarID, out client); | ||
4276 | } | ||
4277 | |||
4278 | public bool TryGetClient(System.Net.IPEndPoint remoteEndPoint, out IClientAPI client) | ||
4279 | { | ||
4280 | return m_clientManager.TryGetValue(remoteEndPoint, out client); | ||
4243 | } | 4281 | } |
4244 | 4282 | ||
4245 | public void ForEachSOG(Action<SceneObjectGroup> action) | 4283 | public void ForEachSOG(Action<SceneObjectGroup> action) |
@@ -4571,7 +4609,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
4571 | { | 4609 | { |
4572 | case PhysicsJointType.Ball: | 4610 | case PhysicsJointType.Ball: |
4573 | { | 4611 | { |
4574 | PhysicsVector jointAnchor = PhysicsScene.GetJointAnchor(joint); | 4612 | Vector3 jointAnchor = PhysicsScene.GetJointAnchor(joint); |
4575 | Vector3 proxyPos = new Vector3(jointAnchor.X, jointAnchor.Y, jointAnchor.Z); | 4613 | Vector3 proxyPos = new Vector3(jointAnchor.X, jointAnchor.Y, jointAnchor.Z); |
4576 | jointProxyObject.ParentGroup.UpdateGroupPosition(proxyPos); // schedules the entire group for a terse update | 4614 | jointProxyObject.ParentGroup.UpdateGroupPosition(proxyPos); // schedules the entire group for a terse update |
4577 | } | 4615 | } |
@@ -4579,7 +4617,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
4579 | 4617 | ||
4580 | case PhysicsJointType.Hinge: | 4618 | case PhysicsJointType.Hinge: |
4581 | { | 4619 | { |
4582 | PhysicsVector jointAnchor = PhysicsScene.GetJointAnchor(joint); | 4620 | Vector3 jointAnchor = PhysicsScene.GetJointAnchor(joint); |
4583 | 4621 | ||
4584 | // Normally, we would just ask the physics scene to return the axis for the joint. | 4622 | // Normally, we would just ask the physics scene to return the axis for the joint. |
4585 | // Unfortunately, ODE sometimes returns <0,0,0> for the joint axis, which should | 4623 | // Unfortunately, ODE sometimes returns <0,0,0> for the joint axis, which should |