// Create terrain of specified size and initialize with specified terrain. // TODO: join this with the terrain initializers. public TerrainChannel(String type, int pX, int pY, int pZ) { m_terrainData = new HeightmapTerrainData(pX, pY, pZ); if (type.Equals("flat")) FlatLand(); else PinHeadIsland(); }
/// <summary> /// Add a patch of terrain to a BitPacker /// </summary> /// <param name="output">BitPacker to write the patch to</param> /// <param name="heightmap"> /// Heightmap of the simulator. Presumed to be an sizeX*sizeY array. /// </param> /// <param name="patchX"> /// X offset of the patch to create. /// </param> /// <param name="patchY"> /// Y offset of the patch to create. /// </param> /// <param name="pRegionSizeX"></param> /// <param name="pRegionSizeY"></param> public static void CreatePatchFromHeightmap(BitPack output, TerrainData terrData, int patchX, int patchY) { TerrainPatch.Header header = PrescanPatch(terrData, patchX, patchY); header.QuantWBits = 136; // If larger than legacy region size, pack patch X and Y info differently. if (terrData.SizeX > Constants.RegionSize || terrData.SizeY > Constants.RegionSize) { header.PatchIDs = (patchY & 0xFFFF); header.PatchIDs += (patchX << 16); } else { header.PatchIDs = (patchY & 0x1F); header.PatchIDs += (patchX << 5); } // m_log.DebugFormat("{0} CreatePatchFromHeightmap. patchX={1}, patchY={2}, DCOffset={3}, range={4}", // LogHeader, patchX, patchY, header.DCOffset, header.Range); // NOTE: No idea what prequant and postquant should be or what they do int wbits; int[] patch = CompressPatch(terrData, patchX, patchY, header, 10, out wbits); wbits = EncodePatchHeader(output, header, patch, (uint)terrData.SizeX, (uint)terrData.SizeY, wbits); EncodePatch(output, patch, 0, wbits); }
public static LayerDataPacket CreateLandPacket(TerrainData terrData, int[] xPieces, int[] yPieces) { byte landPacketType = (byte)TerrainPatch.LayerType.Land; if (terrData.SizeX > Constants.RegionSize || terrData.SizeY > Constants.RegionSize) { landPacketType = (byte)TerrainPatch.LayerType.LandExtended; } return CreateLandPacket(terrData, xPieces, yPieces, landPacketType); }
public void StoreTerrain(TerrainData terrData, UUID regionID) { lock (m_dbLock) { using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) { dbcon.Open(); using (MySqlCommand cmd = dbcon.CreateCommand()) { cmd.CommandText = "delete from terrain where RegionUUID = ?RegionUUID"; cmd.Parameters.AddWithValue("RegionUUID", regionID.ToString()); ExecuteNonQuery(cmd); int terrainDBRevision; Array terrainDBblob; terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob); m_log.InfoFormat("{0} Storing terrain. X={1}, Y={2}, rev={3}", LogHeader, terrData.SizeX, terrData.SizeY, terrainDBRevision); cmd.CommandText = "insert into terrain (RegionUUID, Revision, Heightfield)" + "values (?RegionUUID, ?Revision, ?Heightfield)"; cmd.Parameters.AddWithValue("Revision", terrainDBRevision); cmd.Parameters.AddWithValue("Heightfield", terrainDBblob); ExecuteNonQuery(cmd); } } } }
// Create channel passed a heightmap and expected dimensions of the region. // The heightmap might not fit the passed size so accomodations must be made. public TerrainChannel(double[,] pM, int pSizeX, int pSizeY, int pAltitude) { int hmSizeX = pM.GetLength(0); int hmSizeY = pM.GetLength(1); m_terrainData = new HeightmapTerrainData(pSizeX, pSizeY, pAltitude); for (int xx = 0; xx < pSizeX; xx++) for (int yy = 0; yy < pSizeY; yy++) if (xx > hmSizeX || yy > hmSizeY) m_terrainData[xx, yy] = TerrainData.DefaultTerrainHeight; else m_terrainData[xx, yy] = (float)pM[xx, yy]; }
// Create terrain of given size public TerrainChannel(int pX, int pY) { m_terrainData = new HeightmapTerrainData(pX, pY, (int)Constants.RegionHeight); }
// New terrain serialization format that includes the width and length. private void FromXml2(XmlReader xmlReader) { XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage)); TerrainChannelXMLPackage package = (TerrainChannelXMLPackage)serializer.Deserialize(xmlReader); m_terrainData = new HeightmapTerrainData(package.Map, package.CompressionFactor, package.SizeX, package.SizeY, package.SizeZ); }
public TerrainChannel(TerrainData pTerrData) { m_terrainData = pTerrData; }
/// <summary> /// Checks to see height deltas in the tainted terrain patch at xStart ,yStart /// are all within the current estate limits /// <returns>true if changes were limited, false otherwise</returns> /// </summary> private bool LimitChannelChanges(TerrainData terrData, int xStart, int yStart) { bool changesLimited = false; float minDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainLowerLimit; float maxDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit; // loop through the height map for this patch and compare it against // the revert map for (int x = xStart; x < xStart + Constants.TerrainPatchSize; x++) { for (int y = yStart; y < yStart + Constants.TerrainPatchSize; y++) { float requestedHeight = terrData[x, y]; float bakedHeight = (float)m_revert[x, y]; float requestedDelta = requestedHeight - bakedHeight; if (requestedDelta > maxDelta) { terrData[x, y] = bakedHeight + maxDelta; changesLimited = true; } else if (requestedDelta < minDelta) { terrData[x, y] = bakedHeight + minDelta; //as lower is a -ve delta changesLimited = true; } } } return changesLimited; }
/// <summary> /// Store a terrain revision in region storage /// </summary> /// <param name="ter">terrain heightfield</param> /// <param name="regionID">region UUID</param> public void StoreTerrain(TerrainData terrData, UUID regionID) { lock (ds) { using ( SqliteCommand cmd = new SqliteCommand("delete from terrain where RegionUUID=:RegionUUID", m_conn)) { cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString())); cmd.ExecuteNonQuery(); } // the following is an work around for .NET. The perf // issues associated with it aren't as bad as you think. String sql = "insert into terrain(RegionUUID, Revision, Heightfield)" + " values(:RegionUUID, :Revision, :Heightfield)"; int terrainDBRevision; Array terrainDBblob; terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob); m_log.DebugFormat("{0} Storing terrain revision r {1}", LogHeader, terrainDBRevision); using (SqliteCommand cmd = new SqliteCommand(sql, m_conn)) { cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString())); cmd.Parameters.Add(new SqliteParameter(":Revision", terrainDBRevision)); cmd.Parameters.Add(new SqliteParameter(":Heightfield", terrainDBblob)); cmd.ExecuteNonQuery(); } } }
public void StoreTerrain(TerrainData ter, UUID regionID) { if (m_terrains.ContainsKey(regionID)) m_terrains.Remove(regionID); m_terrains.Add(regionID, ter); }
private static int[] CompressPatch(TerrainData terrData, int patchX, int patchY, TerrainPatch.Header header, int prequant, out int wbits) { float[] block = new float[Constants.TerrainPatchSize * Constants.TerrainPatchSize]; int[] iout = new int[Constants.TerrainPatchSize * Constants.TerrainPatchSize]; float oozrange = 1.0f / header.Range; float invprequat = (1 << prequant); float premult = oozrange * invprequat; float sub = 0.5f * header.Range + header.DCOffset; int wordsize = (prequant - 2) & 0x0f; header.QuantWBits = wordsize; header.QuantWBits |= wordsize << 4; int k = 0; int startX = patchX * Constants.TerrainPatchSize; int startY = patchY * Constants.TerrainPatchSize; for (int y = startY; y < startY + Constants.TerrainPatchSize; y++) { for (int x = startX; x < startX + Constants.TerrainPatchSize; x++) { block[k++] = (terrData[x, y] - sub) * premult; } } wbits = (prequant >> 1); dct16x16(block, iout, ref wbits); return iout; }
// Scan the height info we're returning and return a patch packet header for this patch. private static TerrainPatch.Header PrescanPatch(TerrainData terrData, int patchX, int patchY, out float frange) { TerrainPatch.Header header = new TerrainPatch.Header(); float zmax = float.MinValue; float zmin = float.MaxValue; int startx = patchX * Constants.TerrainPatchSize; int starty = patchY * Constants.TerrainPatchSize; for (int j = starty; j < starty + Constants.TerrainPatchSize; j++) { for (int i = startx; i < startx + Constants.TerrainPatchSize; i++) { float val = terrData[i, j]; if (val > zmax) zmax = val; if (val < zmin) zmin = val; } } header.DCOffset = zmin; frange = ((zmax - zmin) + 1.0f); header.Range = (int)frange; return header; }
public static void CreatePatchFromTerrainData(BitPack output, TerrainData terrData, int patchX, int patchY) { float frange; TerrainPatch.Header header = PrescanPatch(terrData, patchX, patchY, out frange); header.QuantWBits = 130; bool largeRegion = false; // If larger than legacy region size, pack patch X and Y info differently. if (terrData.SizeX > Constants.RegionSize || terrData.SizeY > Constants.RegionSize) { header.PatchIDs = (patchY & 0xFFFF); header.PatchIDs += (patchX << 16); largeRegion = true; } else { header.PatchIDs = (patchY & 0x1F); header.PatchIDs += (patchX << 5); } if (Math.Round((double)frange, 2) == 1.0) { // flat terrain speed up things header.DCOffset -= 0.5f; header.QuantWBits = 0x00; output.PackBits(header.QuantWBits, 8); output.PackFloat(header.DCOffset); output.PackBits(1, 16); if (largeRegion) output.PackBits(header.PatchIDs, 32); else output.PackBits(header.PatchIDs, 10); // and thats all output.PackBits(ZERO_EOB, 2); return; } int wbits; int[] patch = CompressPatch(terrData, patchX, patchY, header, 10, out wbits); EncodePatchHeader(output, header, patch, largeRegion, ref wbits); EncodePatch(output, patch, 0, wbits); }
// new using terrain data and patchs indexes public static List<LayerDataPacket> CreateLayerDataPackets(TerrainData terrData, int[] x, int[] y, byte landPacketType) { List<LayerDataPacket> ret = new List<LayerDataPacket>(); //create packet and global header LayerDataPacket layer = new LayerDataPacket(); layer.LayerID.Type = landPacketType; byte[] data = new byte[x.Length * Constants.TerrainPatchSize * Constants.TerrainPatchSize * 2]; BitPack bitpack = new BitPack(data, 0); bitpack.PackBits(STRIDE, 16); bitpack.PackBits(Constants.TerrainPatchSize, 8); bitpack.PackBits(landPacketType, 8); for (int i = 0; i < x.Length; i++) { CreatePatchFromTerrainData(bitpack, terrData, x[i], y[i]); if (bitpack.BytePos > 980 && i != x.Length - 1) { //finish this packet bitpack.PackBits(END_OF_PATCHES, 8); layer.LayerData.Data = new byte[bitpack.BytePos + 1]; Buffer.BlockCopy(bitpack.Data, 0, layer.LayerData.Data, 0, bitpack.BytePos + 1); ret.Add(layer); // start another layer = new LayerDataPacket(); layer.LayerID.Type = landPacketType; bitpack = new BitPack(data, 0); bitpack.PackBits(STRIDE, 16); bitpack.PackBits(Constants.TerrainPatchSize, 8); bitpack.PackBits(landPacketType, 8); } } bitpack.PackBits(END_OF_PATCHES, 8); layer.LayerData.Data = new byte[bitpack.BytePos + 1]; Buffer.BlockCopy(bitpack.Data, 0, layer.LayerData.Data, 0, bitpack.BytePos + 1); ret.Add(layer); return ret; }
// Scan the height info we're returning and return a patch packet header for this patch. private static TerrainPatch.Header PrescanPatch(TerrainData terrData, int patchX, int patchY) { TerrainPatch.Header header = new TerrainPatch.Header(); float zmax = -99999999.0f; float zmin = 99999999.0f; for (int j = patchY*Constants.TerrainPatchSize; j < (patchY + 1)*Constants.TerrainPatchSize; j++) { for (int i = patchX*Constants.TerrainPatchSize; i < (patchX + 1)*Constants.TerrainPatchSize; i++) { float val = terrData[i, j]; if (val > zmax) zmax = val; if (val < zmin) zmin = val; } } header.DCOffset = zmin; header.Range = (int)((zmax - zmin) + 1.0f); return header; }
private static int[] CompressPatch(TerrainData terrData, int patchX, int patchY, TerrainPatch.Header header, int prequant, out int wbits) { float[] block = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; int wordsize = prequant; float oozrange = 1.0f/header.Range; float range = (1 << prequant); float premult = oozrange*range; float sub = (1 << (prequant - 1)) + header.DCOffset*premult; header.QuantWBits = wordsize - 2; header.QuantWBits |= (prequant - 2) << 4; int k = 0; int yPatchLimit = patchY >= (terrData.SizeY / Constants.TerrainPatchSize) ? (terrData.SizeY - Constants.TerrainPatchSize) / Constants.TerrainPatchSize : patchY; yPatchLimit = (yPatchLimit + 1) * Constants.TerrainPatchSize; int xPatchLimit = patchX >= (terrData.SizeX / Constants.TerrainPatchSize) ? (terrData.SizeX - Constants.TerrainPatchSize) / Constants.TerrainPatchSize : patchX; xPatchLimit = (xPatchLimit + 1) * Constants.TerrainPatchSize; for (int yy = patchY * Constants.TerrainPatchSize; yy < yPatchLimit; yy++) { for (int xx = patchX * Constants.TerrainPatchSize; xx < xPatchLimit; xx++) { block[k++] = terrData[xx, yy] * premult - sub; } } float[] ftemp = new float[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; int[] itemp = new int[Constants.TerrainPatchSize*Constants.TerrainPatchSize]; int maxWbits = prequant + 5; wbits = (prequant >> 1); for (int o = 0; o < Constants.TerrainPatchSize; o++) DCTLine16(block, ftemp, o); for (int o = 0; o < Constants.TerrainPatchSize; o++) wbits = DCTColumn16Wbits(ftemp, itemp, o, wbits, maxWbits); return itemp; }
/// <summary> /// Sends a copy of the current terrain to the scenes clients /// </summary> /// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param> /// <param name="x">The patch corner to send</param> /// <param name="y">The patch corner to send</param> private void SendToClients(TerrainData terrData, int x, int y) { // We know the actual terrain data passed is ignored. This kludge saves changing IClientAPI. //float[] heightMap = terrData.GetFloatsSerialized(); float[] heightMap = new float[10]; m_scene.ForEachClient( delegate(IClientAPI controller) { controller.SendLayerData( x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, heightMap); } ); }
// Read legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array. private void FromXml(XmlReader xmlReader) { XmlSerializer serializer = new XmlSerializer(typeof(byte[])); byte[] dataArray = (byte[])serializer.Deserialize(xmlReader); int index = 0; m_terrainData = new HeightmapTerrainData(Height, Width, (int)Constants.RegionHeight); for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { float value; value = BitConverter.ToSingle(dataArray, index); index += 4; this[x, y] = (double)value; } } }
public void StoreTerrain(TerrainData ter, UUID regionID) { m_terrains[regionID] = ter; }
public int Altitude { get { return m_terrainData.SizeZ; } } // Y dimension // Default, not-often-used builder public TerrainChannel() { m_terrainData = new HeightmapTerrainData((int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight); FlatLand(); // PinHeadIsland(); }
/// <summary> /// Stores the terrain map to DB. /// </summary> /// <param name="terrain">terrain map data.</param> /// <param name="regionID">regionID.</param> public void StoreTerrain(TerrainData terrData, UUID regionID) { //Delete old terrain map string sql = "delete from terrain where RegionUUID=@RegionUUID"; using (SqlConnection conn = new SqlConnection(m_connectionString)) using (SqlCommand cmd = new SqlCommand(sql, conn)) { cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID)); conn.Open(); cmd.ExecuteNonQuery(); } sql = "insert into terrain(RegionUUID, Revision, Heightfield) values(@RegionUUID, @Revision, @Heightfield)"; int terrainDBRevision; Array terrainDBblob; terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob); using (SqlConnection conn = new SqlConnection(m_connectionString)) { using (SqlCommand cmd = new SqlCommand(sql, conn)) { cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID)); cmd.Parameters.Add(_Database.CreateParameter("@Revision", terrainDBRevision)); cmd.Parameters.Add(_Database.CreateParameter("@Heightfield", terrainDBblob)); conn.Open(); cmd.ExecuteNonQuery(); } } _Log.InfoFormat("{0} Stored terrain revision r={1}", LogHeader, terrainDBRevision); }
/// <summary> /// Stores the terrain map to DB. /// </summary> /// <param name="terrain">terrain map data.</param> /// <param name="regionID">regionID.</param> public void StoreTerrain(TerrainData terrData, UUID regionID) { //Delete old terrain map string sql = @"delete from terrain where ""RegionUUID""=:RegionUUID"; using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) { using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) { cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); conn.Open(); cmd.ExecuteNonQuery(); _Log.InfoFormat("{0} Deleted terrain revision id = {1}", LogHeader, regionID); } } int terrainDBRevision; Array terrainDBblob; terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob); sql = @"insert into terrain(""RegionUUID"", ""Revision"", ""Heightfield"") values(:RegionUUID, :Revision, :Heightfield)"; using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) { using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) { cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); cmd.Parameters.Add(_Database.CreateParameter("Revision", terrainDBRevision)); cmd.Parameters.Add(_Database.CreateParameter("Heightfield", terrainDBblob)); conn.Open(); cmd.ExecuteNonQuery(); _Log.InfoFormat("{0} Stored terrain id = {1}, terrainSize = <{2},{3}>", LogHeader, regionID, terrData.SizeX, terrData.SizeY); } } }
// Create a land packet for a single patch. public static LayerDataPacket CreateLandPacket(TerrainData terrData, int patchX, int patchY) { int[] xPieces = new int[1]; int[] yPieces = new int[1]; xPieces[0] = patchX; // patch X dimension yPieces[0] = patchY; byte landPacketType = (byte)TerrainPatch.LayerType.Land; if (terrData.SizeX > Constants.RegionSize || terrData.SizeY > Constants.RegionSize) { landPacketType = (byte)TerrainPatch.LayerType.LandExtended; } return CreateLandPacket(terrData, xPieces, yPieces, landPacketType); }
public void StoreTerrain(TerrainData terrain, UUID regionID) { m_database.StoreTerrain(terrain, regionID); }
/// <summary> /// Creates a LayerData packet for compressed land data given a full /// simulator heightmap and an array of indices of patches to compress /// </summary> /// <param name="terrData"> /// Terrain data that can result in a meter square heightmap. /// </param> /// <param name="x"> /// Array of indexes in the grid of patches /// for this simulator. /// If creating a packet for multiple patches, there will be entries in /// both the X and Y arrays for each of the patches. /// For example if patches 1 and 17 are to be sent, /// x[] = {1,1} and y[] = {0,1} which specifies the patches at /// indexes <1,0> and <1,1> (presuming the terrain size is 16x16 patches). /// </param> /// <param name="y"> /// Array of indexes in the grid of patches. /// </param> /// <param name="type"></param> /// <param name="pRegionSizeX"></param> /// <param name="pRegionSizeY"></param> /// <returns></returns> public static LayerDataPacket CreateLandPacket(TerrainData terrData, int[] x, int[] y, byte type) { LayerDataPacket layer = new LayerDataPacket {LayerID = {Type = type}}; TerrainPatch.GroupHeader header = new TerrainPatch.GroupHeader {Stride = STRIDE, PatchSize = Constants.TerrainPatchSize}; byte[] data = new byte[x.Length * Constants.TerrainPatchSize * Constants.TerrainPatchSize * 2]; BitPack bitpack = new BitPack(data, 0); bitpack.PackBits(header.Stride, 16); bitpack.PackBits(header.PatchSize, 8); bitpack.PackBits(type, 8); for (int i = 0; i < x.Length; i++) CreatePatchFromHeightmap(bitpack, terrData, x[i], y[i]); bitpack.PackBits(END_OF_PATCHES, 8); layer.LayerData.Data = new byte[bitpack.BytePos + 1]; Buffer.BlockCopy(bitpack.Data, 0, layer.LayerData.Data, 0, bitpack.BytePos + 1); return layer; }
// Create a land packet for a single patch. public static LayerDataPacket CreateLandPacket(TerrainData terrData, int patchX, int patchY) { int[] xPieces = new int[1]; int[] yPieces = new int[1]; xPieces[0] = patchX; // patch X dimension yPieces[0] = patchY; return CreateLandPacket(terrData, xPieces, yPieces); }