diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs index cec17e24fa..932652c545 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs @@ -107,11 +107,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain private bool[,] updated; // for each patch, whether it needs to be sent to this client private int updateCount; // number of patches that need to be sent public ScenePresence Presence; // a reference to the client to send to + public TerrainData Terrain; // reference to the underlying terrain public PatchUpdates(TerrainData terrData, ScenePresence pPresence) { updated = new bool[terrData.SizeX / Constants.TerrainPatchSize, terrData.SizeY / Constants.TerrainPatchSize]; updateCount = 0; Presence = pPresence; + Terrain = terrData; // Initially, send all patches to the client SetAll(true); } @@ -519,6 +521,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain // ITerrainModule.PushTerrain() public void PushTerrain(IClientAPI pClient) { + // If view distance based, set the modified patch bits and the frame event will send the updates if (m_sendTerrainUpdatesByViewDistance) { ScenePresence presence = m_scene.GetScenePresence(pClient.AgentId); @@ -533,6 +536,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain pups = new PatchUpdates(m_scene.Heightmap.GetTerrainData(), presence); m_perClientPatchUpdates.Add(presence.UUID, pups); } + // By setting all to modified, the next update tick will send the patches pups.SetAll(true); } } @@ -543,7 +547,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain pClient.SendLayerData(new float[10]); } } - #region Plugin Loading Methods private void LoadPlugins() @@ -991,16 +994,16 @@ namespace OpenSim.Region.CoreModules.World.Terrain else { // Legacy update sending where the update is sent out as soon as noticed - // We know the actual terrain data passed is ignored. This kludge saves changing IClientAPI. + // We know the actual terrain data that is passed is ignored so this passes a dummy heightmap. //float[] heightMap = terrData.GetFloatsSerialized(); float[] heightMap = new float[10]; m_scene.ForEachClient( delegate(IClientAPI controller) - { - controller.SendLayerData(x / Constants.TerrainPatchSize, + { + controller.SendLayerData(x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, heightMap); - } + } ); } } @@ -1026,6 +1029,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain // Called each frame time to see if there are any patches to send to any of the // ScenePresences. + // We know this is only called if we are doing view distance patch sending so some + // tests are not made. // Loop through all the per-client info and send any patches necessary. private void CheckSendingPatchesToClients() { @@ -1043,7 +1048,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain // LogHeader, toSend.Count, pups.Presence.Name, m_scene.RegionInfo.RegionName); // Sort the patches to send by the distance from the presence toSend.Sort(); - /* + /* old way that sent individual patches foreach (PatchesToSend pts in toSend) { pups.Presence.ControllingClient.SendLayerData(pts.PatchX, pts.PatchY, null); @@ -1051,6 +1056,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain } */ + // new way that sends all patches to the protocol so they can be sent in one block int[] xPieces = new int[toSend.Count]; int[] yPieces = new int[toSend.Count]; float[] patchPieces = new float[toSend.Count * 2]; @@ -1067,6 +1073,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain } } + // Compute a list of modified patches that are within our view distance. private List GetModifiedPatchesInViewDistance(PatchUpdates pups) { List ret = new List(); @@ -1075,43 +1082,60 @@ namespace OpenSim.Region.CoreModules.World.Terrain if (presence == null) return ret; - // Compute the area of patches within our draw distance - int startX = (((int)(presence.AbsolutePosition.X - presence.DrawDistance)) / Constants.TerrainPatchSize) - 2; - startX = Math.Max(startX, 0); - startX = Math.Min(startX, (int)m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize); - int startY = (((int)(presence.AbsolutePosition.Y - presence.DrawDistance)) / Constants.TerrainPatchSize) - 2; - startY = Math.Max(startY, 0); - startY = Math.Min(startY, (int)m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize); - int endX = (((int)(presence.AbsolutePosition.X + presence.DrawDistance)) / Constants.TerrainPatchSize) + 2; - endX = Math.Max(endX, 0); - endX = Math.Min(endX, (int)m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize); - int endY = (((int)(presence.AbsolutePosition.Y + presence.DrawDistance)) / Constants.TerrainPatchSize) + 2; - endY = Math.Max(endY, 0); - endY = Math.Min(endY, (int)m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize); - // m_log.DebugFormat("{0} GetModifiedPatchesInViewDistance. rName={1}, ddist={2}, apos={3}, start=<{4},{5}>, end=<{6},{7}>", + Vector3 presencePos = presence.AbsolutePosition; + + // Before this distance check, the whole region just showed up. Adding the distance + // check causes different things to happen for the current and adjacent regions. + // So, to keep legacy views, if the region is legacy sized, don't do distance check. + bool isLegacySizedRegion = pups.Terrain.SizeX == Constants.RegionSize && pups.Terrain.SizeY == Constants.RegionSize; + bool shouldCheckViewDistance = m_sendTerrainUpdatesByViewDistance && !isLegacySizedRegion; + + int startX = 0; + int endX = (int)m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize; + int startY = 0; + int endY = (int)m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize; + + // The following only reduces the size of area scanned for updates. Only significant for very large varregions. + if (shouldCheckViewDistance) + { + // Compute the area of patches within our draw distance + startX = (((int)(presencePos.X - presence.DrawDistance)) / Constants.TerrainPatchSize) - 2; + startX = Math.Max(startX, 0); + startX = Math.Min(startX, (int)m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize); + startY = (((int)(presencePos.Y - presence.DrawDistance)) / Constants.TerrainPatchSize) - 2; + startY = Math.Max(startY, 0); + startY = Math.Min(startY, (int)m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize); + endX = (((int)(presencePos.X + presence.DrawDistance)) / Constants.TerrainPatchSize) + 2; + endX = Math.Max(endX, 0); + endX = Math.Min(endX, (int)m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize); + endY = (((int)(presencePos.Y + presence.DrawDistance)) / Constants.TerrainPatchSize) + 2; + endY = Math.Max(endY, 0); + endY = Math.Min(endY, (int)m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize); + } + + // m_log.DebugFormat("{0} GetModifiedPatchesInViewDistance. rName={1}, ddist={2}, apos={3}, cpos={4}, isChild={5}, start=<{6},{7}>, end=<{8},{9}>", // LogHeader, m_scene.RegionInfo.RegionName, - // presence.DrawDistance, presence.AbsolutePosition, + // presence.DrawDistance, presencePos, presence.CameraPosition, + // isLegacySizeChildRegion, // startX, startY, endX, endY); for(int x = startX; x < endX; x++) { for(int y = startY; y < endY; y++) { //Need to make sure we don't send the same ones over and over - Vector3 presencePos = presence.AbsolutePosition; Vector3 patchPos = new Vector3(x * Constants.TerrainPatchSize, y * Constants.TerrainPatchSize, presencePos.Z); if (pups.GetByPatch(x, y)) { //Check which has less distance, camera or avatar position, both have to be done. //Its not a radius, its a diameter and we add 50 so that it doesn't look like it cuts off - if (Util.DistanceLessThan(presencePos, patchPos, presence.DrawDistance + 50) + if (!shouldCheckViewDistance + || Util.DistanceLessThan(presencePos, patchPos, presence.DrawDistance + 50) || Util.DistanceLessThan(presence.CameraPosition, patchPos, presence.DrawDistance + 50)) { //They can see it, send it to them pups.SetByPatch(x, y, false); float dist = Vector3.DistanceSquared(presencePos, patchPos); ret.Add(new PatchesToSend(x, y, dist)); - //Wait and send them all at once - // pups.client.SendLayerData(x, y, null); } } }