// Re-build the preview mesh based on a specified RAW file public void UpdatePreviewMesh(LBRaw rawFile) { if (rawFile != null) { volumePreviewMesh = rawFile.CreatePreviewMesh(new Vector3(-0.5f, 0f, -0.5f), 65); volumePreviewMeshSet = true; } else { volumePreviewMesh = null; volumePreviewMeshSet = false; } }
/// <summary> /// Clone constructor /// </summary> /// <param name="lbRaw"></param> public LBRaw(LBRaw lbRaw) { if (lbRaw == null) { SetClassDefaults(); } else { if (string.IsNullOrEmpty(this.dataSourceName)) { this.dataSourceName = "unknown"; } else { this.dataSourceName = lbRaw.dataSourceName; } // Heightmap data if (lbRaw.rawHeightData == null) { this.rawHeightData = null; this.rawMinHeight = 0; this.rawMaxHeight = 0; this.rawSourceWidth = 0; this.rawSourceLength = 0; this.sourceFileType = SourceFileType.RAW; } else { // A shallow copy (clone) works here because it is a primative/immutable type // This should create a true copy rather than a reference to the input array this.rawHeightData = lbRaw.rawHeightData.Clone() as byte[]; this.rawMinHeight = lbRaw.rawMinHeight; this.rawMaxHeight = lbRaw.rawMaxHeight; this.rawSourceWidth = lbRaw.rawSourceWidth; this.rawSourceLength = lbRaw.rawSourceLength; this.sourceFileType = lbRaw.sourceFileType; } } }
/// <summary> /// Load a PNG file from disk into rawHeightData (which is stored in LB as 16-bit little endian [windows] RAW) /// </summary> /// <param name="filePath"></param> /// <param name="showErrors"></param> /// <returns></returns> public static LBRaw ImportHeightmapPNG(string filePath, bool showErrors) { LBRaw lbRaw = null; string methodName = "LBRaw.ImportHeightmapPNG"; // Perform basic validation if (string.IsNullOrEmpty(filePath)) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - PNG file is not available"); } } else if (!System.IO.File.Exists(filePath)) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - PNG file does not exist: " + filePath); } } else if (!System.IO.Path.HasExtension(filePath)) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - the filename does not have the raw extension. File: " + filePath); } } else if (System.IO.Path.GetExtension(filePath).ToLower() != ".png") { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - the filename is not of type png. File: " + filePath); } } else { byte[] pngData = null; // RAW data is stored as 16-bit, consisting of a little and big endian (8bit + 8bit) try { pngData = System.IO.File.ReadAllBytes(filePath); } catch (System.Exception ex) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - could not read RAW file " + ex.Message); } } if (pngData == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - could not get heights from file: " + filePath); } } else if (pngData.Length < 3) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - PNG data file is empty or invalid: " + filePath); } } else { lbRaw = new LBRaw(); if (lbRaw == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - could not create LBRaw instance"); } } else { // This assumes that the RAW file is square lbRaw.rawHeightData = null; lbRaw.dataSourceName = System.IO.Path.GetFileName(filePath); lbRaw.sourceFileType = SourceFileType.PNG; // Create a very small texture. Texture2D.LoadImage will auto-resize it. Texture2D texture = new Texture2D(2, 2, TextureFormat.RGBA32, false, true); if (texture == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - could not create Texture2D"); } } else { // Load the PNG raw data and auto-resize the texture texture.LoadImage(pngData, false); // Is the width 2 ^ n where n is an integer float log2 = Mathf.Log(texture.width, 2); if (log2 - (int)log2 < 0.0001f) { // Make the width (2 ^ n) + 1 lbRaw.rawSourceWidth = texture.width + 1; } else { // Scale down to the nearest 2 ^ n width where n is an integer - then add 1 lbRaw.rawSourceWidth = ((int)Mathf.Pow(2f, (int)log2)) + 1; } // Assume this is a square image lbRaw.rawSourceLength = lbRaw.rawSourceWidth; //Debug.Log("PNG tex size: " + texture.width + "x" + texture.height + " resize to " + lbRaw.rawSourceWidth + "x" + lbRaw.rawSourceWidth); // resize to a square texture with width = 2^n + 1 LBTextureOperations.TexturePointScale(texture, lbRaw.rawSourceWidth, lbRaw.rawSourceLength); // Get the pixel colours from the base texture (mipmap = 0) Color[] colours = texture.GetPixels(); if (colours == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - could not get pixels from imported PNG file"); } } else { // TEST CODE //LBEditorHelper.SaveMapTexture(texture, filePath + "_2.png", 2048); // RAW data is stored as 16-bit, consisting of a little and big endian (8bit + 8bit) lbRaw.rawHeightData = new byte[lbRaw.rawSourceWidth * lbRaw.rawSourceLength * 2]; if (lbRaw.rawHeightData == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - could not create rawHeightData array"); } } else { int rawHeightDataLength = lbRaw.rawHeightData.Length; ushort sampleMinValue = ushort.MaxValue; ushort sampleMaxValue = ushort.MinValue; ushort sample16bit = 0; for (int byteIndex = 0; byteIndex < rawHeightDataLength - 1; byteIndex += 2) { //pixel = colours[byteIndex / 2].grayscale; sample16bit = (ushort)(colours[byteIndex / 2].grayscale * 65535f); // LB stores RAW data in little endian (Windows) format, which is more suited to Intel processors. // Windows: little endian lbRaw.rawHeightData[byteIndex + 1] = System.Convert.ToByte(sample16bit >> 8); lbRaw.rawHeightData[byteIndex] = System.Convert.ToByte(sample16bit & 255); // keep track of the min/max values in the input RAW data if (sample16bit > sampleMaxValue) { sampleMaxValue = sample16bit; } if (sample16bit < sampleMinValue) { sampleMinValue = sample16bit; } } if (sampleMinValue == ushort.MaxValue) { sampleMinValue = ushort.MinValue; } lbRaw.rawMinHeight = sampleMinValue; lbRaw.rawMaxHeight = sampleMaxValue; //Debug.Log("PNG RAW min/max: " + sampleMinValue + "," + sampleMaxValue); } } } } } } return(lbRaw); }
/// <summary> /// Load a RAW file from disk into rawHeightData (which is stored in LB as 16-bit little endian [windows] RAW) /// </summary> /// <param name="filePath"></param> /// <param name="isMac"></param> /// <param name="showErrors"></param> /// <returns></returns> public static LBRaw ImportHeightmapRAW(string filePath, bool isMac, bool showErrors) { LBRaw lbRaw = null; string methodName = "LBRaw.ImportHeightmapRAW"; // Perform basic validation if (string.IsNullOrEmpty(filePath)) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - RAW file is not available"); } } else if (!System.IO.File.Exists(filePath)) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - RAW file does not exist: " + filePath); } } else if (!System.IO.Path.HasExtension(filePath)) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - the filename does not have the raw extension. File: " + filePath); } } else if (System.IO.Path.GetExtension(filePath).ToLower() != ".raw") { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - the filename is not of type raw. File: " + filePath); } } else { byte[] rawData = null; // RAW data is stored as 16-bit, consisting of a little and big endian (8bit + 8bit) try { rawData = System.IO.File.ReadAllBytes(filePath); } catch (System.Exception ex) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - could not read RAW file " + ex.Message); } } if (rawData == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - could not get heights from file: " + filePath); } } else if (rawData.Length < 3) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - RAW data file is empty or invalid: " + filePath); } } else { lbRaw = new LBRaw(); if (lbRaw == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - could not create LBRaw instance"); } } else { // This assumes that the RAW file is square lbRaw.rawHeightData = null; lbRaw.dataSourceName = System.IO.Path.GetFileName(filePath); lbRaw.rawSourceWidth = Mathf.RoundToInt(Mathf.Sqrt(rawData.Length / 2)); lbRaw.rawSourceLength = Mathf.RoundToInt(Mathf.Sqrt(rawData.Length / 2)); lbRaw.sourceFileType = SourceFileType.RAW; // RAW data is stored as 16-bit, consisting of a little and big endian (8bit + 8bit) lbRaw.rawHeightData = new byte[lbRaw.rawSourceWidth * lbRaw.rawSourceLength * 2]; if (lbRaw.rawHeightData == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - could not create rawHeightData array"); } } else { int rawHeightDataLength = lbRaw.rawHeightData.Length; ushort sampleMinValue = ushort.MaxValue; ushort sampleMaxValue = ushort.MinValue; ushort sample16bit = 0; for (int byteIndex = 0; byteIndex < rawHeightDataLength - 1; byteIndex += 2) { // LB stores RAW data in little endian (Windows) format, which is more suited to Intel processors. if (isMac) { // Mac: big endian lbRaw.rawHeightData[byteIndex + 1] = rawData[byteIndex]; lbRaw.rawHeightData[byteIndex] = rawData[byteIndex + 1]; sample16bit = (ushort)(rawData[byteIndex] << 8 | rawData[byteIndex + 1]); } else { // Windows: little endian lbRaw.rawHeightData[byteIndex + 1] = rawData[byteIndex + 1]; lbRaw.rawHeightData[byteIndex] = rawData[byteIndex]; sample16bit = (ushort)(rawData[byteIndex + 1] << 8 | rawData[byteIndex]); } // keep track of the min/max values in the input RAW data if (sample16bit > sampleMaxValue) { sampleMaxValue = sample16bit; } if (sample16bit < sampleMinValue) { sampleMinValue = sample16bit; } } if (sampleMinValue == ushort.MaxValue) { sampleMinValue = ushort.MinValue; } lbRaw.rawMinHeight = sampleMinValue; lbRaw.rawMaxHeight = sampleMaxValue; } } } } return(lbRaw); }