using System; using System.IO; using UnityEngine; namespace Gaia { public class Scanner : MonoBehaviour { [Tooltip("The name of the stamp as it will be stored in the project. Initally based on the file name.")] public string m_featureName = $"{DateTime.Now}"; [Tooltip("The type of stamp, also controls which directory the stamp will be loaded into.")] public GaiaConstants.FeatureType m_featureType = GaiaConstants.FeatureType.Mountains; [Range(0f, 1f)] [Tooltip("Base level for this stamp, used by stamper to cut off all heights below the base. It is the highest point of the stamp around its edges.")] public float m_baseLevel; [HideInInspector] [Range(0.1f, 1f)] [Tooltip("Scan resolution in meters. Leave this at smaller values for higher quality scans.")] public float m_scanResolution = 0.1f; [Tooltip("The material that will be used to display the scan preview. This is just for visualisation and will not affect the scan.")] public Material m_previewMaterial; private HeightMap m_scanMap; private Bounds m_scanBounds; private int m_scanWidth = 1; private int m_scanDepth = 1; private int m_scanHeight = 500; private Vector3 m_groundOffset = Vector3.zero; private Vector3 m_groundSize = Vector3.zero; private void OnEnable() { MeshFilter meshFilter = GetComponent(); if (meshFilter == null) { meshFilter = base.gameObject.AddComponent(); } meshFilter.hideFlags = HideFlags.HideInInspector; MeshRenderer meshRenderer = GetComponent(); if (meshRenderer == null) { meshRenderer = base.gameObject.AddComponent(); } meshRenderer.hideFlags = HideFlags.HideInInspector; } private void Awake() { base.gameObject.SetActive(value: false); } public void Reset() { m_featureName = ""; m_scanMap = null; m_scanBounds = new Bounds(base.transform.position, Vector3.one * 10f); m_scanWidth = (m_scanDepth = (m_scanHeight = 0)); m_scanResolution = 0.1f; m_baseLevel = 0f; base.transform.localRotation = Quaternion.identity; base.transform.localScale = Vector3.one; } public void LoadRawFile(string path, GaiaConstants.RawByteOrder byteOrder, ref GaiaConstants.RawBitDepth bitDepth, ref int resolution) { if (string.IsNullOrEmpty(path)) { Debug.LogError("Must supply a valid path. Raw load Aborted!"); } Reset(); m_featureName = Path.GetFileNameWithoutExtension(path); m_scanMap = new HeightMap(); m_scanMap.LoadFromRawFile(path, byteOrder, ref bitDepth, ref resolution); if (!m_scanMap.HasData()) { Debug.LogError("Unable to load raw file. Raw load aborted."); return; } m_scanWidth = m_scanMap.Width(); m_scanDepth = m_scanMap.Depth(); m_scanHeight = m_scanWidth / 2; m_scanResolution = 0.1f; m_scanBounds = new Bounds(base.transform.position, new Vector3((float)m_scanWidth * m_scanResolution, (float)m_scanWidth * m_scanResolution * 0.4f, (float)m_scanDepth * m_scanResolution)); m_baseLevel = m_scanMap.GetBaseLevel(); MeshFilter meshFilter = GetComponent(); if (meshFilter == null) { meshFilter = base.gameObject.AddComponent(); meshFilter.hideFlags = HideFlags.HideInInspector; } MeshRenderer meshRenderer = GetComponent(); if (meshRenderer == null) { meshRenderer = base.gameObject.AddComponent(); meshRenderer.hideFlags = HideFlags.HideInInspector; } meshFilter.mesh = GaiaUtils.CreateMesh(m_scanMap.Heights(), m_scanBounds.size); if (m_previewMaterial != null) { m_previewMaterial.hideFlags = HideFlags.HideInInspector; meshRenderer.sharedMaterial = m_previewMaterial; } } public void LoadTextureFile(Texture2D texture) { if (texture == null) { Debug.LogError("Must supply a valid texture! Texture load aborted."); return; } Reset(); m_featureName = texture.name; m_scanMap = new UnityHeightMap(texture); if (!m_scanMap.HasData()) { Debug.LogError("Unable to load Texture file. Texture load aborted."); return; } m_scanWidth = m_scanMap.Width(); m_scanDepth = m_scanMap.Depth(); m_scanHeight = m_scanWidth / 2; m_scanResolution = 0.1f; m_scanBounds = new Bounds(base.transform.position, new Vector3((float)m_scanWidth * m_scanResolution, (float)m_scanWidth * m_scanResolution * 0.4f, (float)m_scanDepth * m_scanResolution)); m_baseLevel = m_scanMap.GetBaseLevel(); MeshFilter meshFilter = GetComponent(); if (meshFilter == null) { meshFilter = base.gameObject.AddComponent(); meshFilter.hideFlags = HideFlags.HideInInspector; } MeshRenderer meshRenderer = GetComponent(); if (meshRenderer == null) { meshRenderer = base.gameObject.AddComponent(); meshRenderer.hideFlags = HideFlags.HideInInspector; } meshFilter.mesh = GaiaUtils.CreateMesh(m_scanMap.Heights(), m_scanBounds.size); if (m_previewMaterial != null) { m_previewMaterial.hideFlags = HideFlags.HideInInspector; meshRenderer.sharedMaterial = m_previewMaterial; } } public void LoadTerain(Terrain terrain) { if (terrain == null) { Debug.LogError("Must supply a valid terrain! Terrain load aborted."); return; } Reset(); m_featureName = terrain.name; m_scanMap = new UnityHeightMap(terrain); if (!m_scanMap.HasData()) { Debug.LogError("Unable to load terrain file. Terrain load aborted."); return; } m_scanMap.Flip(); m_scanWidth = m_scanMap.Width(); m_scanDepth = m_scanMap.Depth(); m_scanHeight = (int)terrain.terrainData.size.y; m_scanResolution = 0.1f; m_scanBounds = new Bounds(base.transform.position, new Vector3((float)m_scanWidth * m_scanResolution, (float)m_scanWidth * m_scanResolution * 0.4f, (float)m_scanDepth * m_scanResolution)); m_baseLevel = m_scanMap.GetBaseLevel(); MeshFilter meshFilter = GetComponent(); if (meshFilter == null) { meshFilter = base.gameObject.AddComponent(); meshFilter.hideFlags = HideFlags.HideInInspector; } MeshRenderer meshRenderer = GetComponent(); if (meshRenderer == null) { meshRenderer = base.gameObject.AddComponent(); meshRenderer.hideFlags = HideFlags.HideInInspector; } meshFilter.mesh = GaiaUtils.CreateMesh(m_scanMap.Heights(), m_scanBounds.size); if (m_previewMaterial != null) { m_previewMaterial.hideFlags = HideFlags.HideInInspector; meshRenderer.sharedMaterial = m_previewMaterial; } } public void LoadGameObject(GameObject go) { if (go == null) { Debug.LogError("Must supply a valid game object! GameObject load aborted."); return; } Reset(); m_featureName = go.name; GameObject gameObject = UnityEngine.Object.Instantiate(go); gameObject.transform.position = base.transform.position; gameObject.transform.localRotation = Quaternion.identity; gameObject.transform.localScale = Vector3.one; Collider[] componentsInChildren = gameObject.GetComponentsInChildren(); for (int i = 0; i < componentsInChildren.Length; i++) { UnityEngine.Object.DestroyImmediate(componentsInChildren[i]); } Transform[] componentsInChildren2 = gameObject.GetComponentsInChildren(); foreach (Transform transform in componentsInChildren2) { if (transform.gameObject.activeSelf) { transform.gameObject.AddComponent(); } } m_scanBounds.center = gameObject.transform.position; m_scanBounds.size = Vector3.zero; MeshCollider[] componentsInChildren3 = gameObject.GetComponentsInChildren(); foreach (MeshCollider meshCollider in componentsInChildren3) { m_scanBounds.Encapsulate(meshCollider.bounds); } m_scanWidth = (int)Mathf.Ceil(m_scanBounds.size.x * (1f / m_scanResolution)); m_scanHeight = (int)Mathf.Ceil(m_scanBounds.size.y * (1f / m_scanResolution)); m_scanDepth = (int)Mathf.Ceil(m_scanBounds.size.z * (1f / m_scanResolution)); m_scanMap = new HeightMap(m_scanWidth, m_scanDepth); Vector3 min = m_scanBounds.min; Vector3 origin = min; origin.y = m_scanBounds.max.y; for (int j = 0; j < m_scanWidth; j++) { origin.x = min.x + m_scanResolution * (float)j; for (int k = 0; k < m_scanDepth; k++) { origin.z = min.z + m_scanResolution * (float)k; if (Physics.Raycast(origin, Vector3.down, out var hitInfo, m_scanBounds.size.y)) { m_scanMap[j, k] = 1f - hitInfo.distance / m_scanBounds.size.y; } } } UnityEngine.Object.DestroyImmediate(gameObject); if (!m_scanMap.HasData()) { Debug.LogError("Unable to scan GameObject. GameObject load aborted."); return; } m_scanBounds = new Bounds(base.transform.position, new Vector3((float)m_scanWidth * m_scanResolution, (float)m_scanWidth * m_scanResolution * 0.4f, (float)m_scanDepth * m_scanResolution)); m_baseLevel = m_scanMap.GetBaseLevel(); MeshFilter meshFilter = GetComponent(); if (meshFilter == null) { meshFilter = base.gameObject.AddComponent(); meshFilter.hideFlags = HideFlags.HideInInspector; } MeshRenderer meshRenderer = GetComponent(); if (meshRenderer == null) { meshRenderer = base.gameObject.AddComponent(); meshRenderer.hideFlags = HideFlags.HideInInspector; } meshFilter.mesh = GaiaUtils.CreateMesh(m_scanMap.Heights(), m_scanBounds.size); if (m_previewMaterial != null) { m_previewMaterial.hideFlags = HideFlags.HideInInspector; meshRenderer.sharedMaterial = m_previewMaterial; } } public string SaveScan() { if (m_scanMap == null || !m_scanMap.HasData()) { Debug.LogWarning("Cant save scan as none has been loaded"); return null; } string gaiaAssetPath = GaiaUtils.GetGaiaAssetPath(m_featureType, m_featureName); GaiaUtils.CompressToSingleChannelFileImage(m_scanMap.Heights(), gaiaAssetPath, TextureFormat.RGBA32, exportPNG: false); gaiaAssetPath = GaiaUtils.GetGaiaStampAssetPath(m_featureType, m_featureName); gaiaAssetPath += ".bytes"; float[] obj = new float[5] { m_scanWidth, m_scanDepth, m_scanHeight, m_scanResolution, m_baseLevel }; byte[] array = new byte[obj.Length * 4]; Buffer.BlockCopy(obj, 0, array, 0, array.Length); m_scanMap.SetMetaData(array); m_scanMap.SaveToBinaryFile(gaiaAssetPath); return gaiaAssetPath; } private void UpdateScanner() { base.transform.localRotation = Quaternion.identity; base.transform.localScale = Vector3.one; m_scanBounds.center = base.transform.position; } private void OnDrawGizmosSelected() { UpdateScanner(); Gizmos.color = Color.blue; Gizmos.DrawWireCube(m_scanBounds.center, m_scanBounds.size); m_groundOffset = m_scanBounds.center; m_groundOffset.y = m_scanBounds.min.y + (m_scanBounds.max.y - m_scanBounds.min.y) * m_baseLevel; m_groundSize = m_scanBounds.size; m_groundSize.y = 0.1f; Gizmos.color = Color.yellow; Gizmos.DrawCube(m_groundOffset, m_groundSize); } } }