Bounds GetBounds(PointCloudBounds bounds) { Bounds result = new Bounds(); if (Normalize) { result.center = Vector3.zero; result.extents = Vector3.one; } else if (Center) { result.center = Vector3.zero; result.extents = new Vector3( (float)(bounds.MaxX - bounds.MinX) * 0.5f, (float)(bounds.MaxY - bounds.MinY) * 0.5f, (float)(bounds.MaxZ - bounds.MinZ) * 0.5f); } else { result.SetMinMax( new Vector3((float)bounds.MinX, (float)bounds.MinY, (float)bounds.MinZ), new Vector3((float)bounds.MaxX, (float)bounds.MaxY, (float)bounds.MaxZ)); } return(result); }
unsafe PointCloudPoint[] Convert(AssetImportContext context, byte *ptr, int stride, int count, List <PointElement> elements, PointCloudBounds bounds, out bool hasColor) { var name = Path.GetFileName(context.assetPath); var counts = new NativeArray <int>(JobsUtility.MaxJobThreadCount, Allocator.TempJob); try { var points = new PointCloudPoint[count]; fixed(PointCloudPoint *output = points) { double scaleX, scaleY, scaleZ; double centerX, centerY, centerZ; if (Normalize) { centerX = -0.5 * (bounds.MaxX + bounds.MinX); centerY = -0.5 * (bounds.MaxY + bounds.MinY); centerZ = -0.5 * (bounds.MaxZ + bounds.MinZ); scaleX = 2.0 / (bounds.MaxX - bounds.MinX); scaleY = 2.0 / (bounds.MaxY - bounds.MinY); scaleZ = 2.0 / (bounds.MaxZ - bounds.MinZ); } else if (Center) { centerX = -0.5 * (bounds.MaxX + bounds.MinX); centerY = -0.5 * (bounds.MaxY + bounds.MinY); centerZ = -0.5 * (bounds.MaxZ + bounds.MinZ); scaleX = scaleY = scaleZ = 1.0; } else { centerX = centerY = centerZ = 0.0; scaleX = scaleY = scaleZ = 1.0; } var job = new PointCloudConvertJob() { X = GetInputAccess(PointElementName.X, elements, ptr, stride), Y = GetInputAccess(PointElementName.Y, elements, ptr, stride), Z = GetInputAccess(PointElementName.Z, elements, ptr, stride), Color = GetColorAccess(context, elements, ptr, stride), Output = output, Transform = GetTransform(), OutputCenterX = centerX, OutputCenterY = centerY, OutputCenterZ = centerZ, OutputScaleX = scaleX, OutputScaleY = scaleY, OutputScaleZ = scaleZ, Counts = (int *)counts.GetUnsafePtr(), ThreadIndex = 0, }; var h = job.Schedule((int)count, 65536); while (!h.IsCompleted) { System.Threading.Thread.Sleep(100); int processed = counts.Sum(); float progress = (float)((double)processed / count); EditorUtility.DisplayProgressBar($"Importing {name}", $"{processed:N0} points", progress); } hasColor = job.Color.Color != null; return(points); } } finally { EditorUtility.ClearProgressBar(); counts.Dispose(); } }
unsafe bool LasConvert(AssetImportContext context, byte *ptr, int stride, int count, ref LasHeader header, PointCloudBounds bounds, PointCloudPoint *points) { var name = Path.GetFileName(context.assetPath); var counts = new NativeArray <int>(JobsUtility.MaxJobThreadCount, Allocator.TempJob); try { double scaleX, scaleY, scaleZ; double centerX, centerY, centerZ; if (Normalize) { centerX = -0.5 * (bounds.MaxX + bounds.MinX); centerY = -0.5 * (bounds.MaxY + bounds.MinY); centerZ = -0.5 * (bounds.MaxZ + bounds.MinZ); scaleX = 2.0 / (bounds.MaxX - bounds.MinX); scaleY = 2.0 / (bounds.MaxY - bounds.MinY); scaleZ = 2.0 / (bounds.MaxZ - bounds.MinZ); } else if (Center) { centerX = -0.5 * (bounds.MaxX + bounds.MinX); centerY = -0.5 * (bounds.MaxY + bounds.MinY); centerZ = -0.5 * (bounds.MaxZ + bounds.MinZ); scaleX = scaleY = scaleZ = 1.0; } else { centerX = centerY = centerZ = 0.0; scaleX = scaleY = scaleZ = 1.0; } var job = new LasConvertJob() { LasRGB8BitWorkaround = LasRGB8BitWorkaround, Input = ptr, Stride = stride, Output = points, Transform = GetTransform(), InputScaleX = header.ScaleX, InputScaleY = header.ScaleY, InputScaleZ = header.ScaleZ, InputOffsetX = header.OffsetX, InputOffsetY = header.OffsetY, InputOffsetZ = header.OffsetZ, OutputCenterX = centerX, OutputCenterY = centerY, OutputCenterZ = centerZ, OutputScaleX = scaleX, OutputScaleY = scaleY, OutputScaleZ = scaleZ, Counts = (int *)counts.GetUnsafePtr(), ThreadIndex = 0, }; bool hasColor = false; if (header.PointDataFormat == 2) { job.ColorInput = ptr + 20; hasColor = true; } else if (header.PointDataFormat == 3 || header.PointDataFormat == 5) { job.ColorInput = ptr + 28; hasColor = true; } else if (header.PointDataFormat == 7 || header.PointDataFormat == 8 || header.PointDataFormat == 10) { job.ColorInput = ptr + 30; hasColor = true; } var h = job.Schedule((int)count, 65536); while (!h.IsCompleted) { System.Threading.Thread.Sleep(100); int processed = counts.Sum(); float progress = (float)((double)processed / count); EditorUtility.DisplayProgressBar($"Importing {name}", $"{processed:N0} points", progress); } return(hasColor); } finally { EditorUtility.ClearProgressBar(); counts.Dispose(); } }
PointCloudData ImportLas(AssetImportContext context) { long size = new FileInfo(context.assetPath).Length; using (var file = MemoryMappedFile.CreateFromFile(context.assetPath, FileMode.Open)) { using (var view = file.CreateViewAccessor(0, size, MemoryMappedFileAccess.Read)) { view.Read <LasHeader>(0, out var header); if (Encoding.ASCII.GetString(header.Signature) != "LASF") { throw new Exception("Incorrect LAS file signature"); } if (header.PointDataFormat > 10) { throw new Exception($"Unsupported LAS file format: {header.PointDataFormat}"); } long offset = header.PointDataOffset; int stride = header.PointDataSize; long count = header.PointDataCount; if (header.VersionMajor > 1 || header.VersionMajor == 1 && header.VersionMinor >= 4) { if (count == 0) { count = (long)header.PointDataCountLong; } } if (count > MaxPointCount) { Debug.LogWarning($"Too many points ({count:n0}), truncating to {MaxPointCount:n0}"); count = MaxPointCount; } var bounds = new PointCloudBounds() { MinX = header.MinX, MinY = header.MinY, MinZ = header.MinZ, MaxX = header.MaxX, MaxY = header.MaxY, MaxZ = header.MaxZ, }; unsafe { byte *ptr = null; view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr); try { var points = new PointCloudPoint[(int)count]; fixed(PointCloudPoint *output = points) { bool hasColor = LasConvert(context, ptr + offset, stride, (int)count, ref header, bounds, output); var transform = GetTransform(); var b = GetBounds(bounds); b.center = transform.MultiplyPoint3x4(b.center); b.extents = transform.MultiplyVector(b.extents); return(PointCloudData.Create(points, GetBounds(bounds), hasColor, transform.MultiplyPoint3x4(bounds.Center), transform.MultiplyVector(bounds.Extents))); } } finally { view.SafeMemoryMappedViewHandle.ReleasePointer(); } } } } }
PointCloudData ImportLaz(AssetImportContext context) { var name = Path.GetFileName(context.assetPath); using (var laz = new Laszip(context.assetPath)) { long count = laz.Count; if (count > MaxPointCount) { Debug.LogWarning($"Too many points ({count:n0}), truncating to {MaxPointCount:n0}"); count = MaxPointCount; } var bounds = new PointCloudBounds() { MinX = laz.MinX, MinY = laz.MinY, MinZ = laz.MinZ, MaxX = laz.MaxX, MaxY = laz.MaxY, MaxZ = laz.MaxZ, }; double scaleX, scaleY, scaleZ; double centerX, centerY, centerZ; if (Normalize) { centerX = -0.5 * (bounds.MaxX + bounds.MinX); centerY = -0.5 * (bounds.MaxY + bounds.MinY); centerZ = -0.5 * (bounds.MaxZ + bounds.MinZ); scaleX = 2.0 / (bounds.MaxX - bounds.MinX); scaleY = 2.0 / (bounds.MaxY - bounds.MinY); scaleZ = 2.0 / (bounds.MaxZ - bounds.MinZ); } else if (Center) { centerX = -0.5 * (bounds.MaxX + bounds.MinX); centerY = -0.5 * (bounds.MaxY + bounds.MinY); centerZ = -0.5 * (bounds.MaxZ + bounds.MinZ); scaleX = scaleY = scaleZ = 1.0; } else { centerX = centerY = centerZ = 0.0; scaleX = scaleY = scaleZ = 1.0; } bool hasColor = laz.HasColor; var transform = GetTransform(); var points = new PointCloudPoint[(int)count]; try { for (long i = 0; i < count; i++) { if ((i % (128 * 1024)) == 0) { float progress = (float)((double)i / count); EditorUtility.DisplayProgressBar($"Importing {name}", $"{i:N0} points", progress); } var p = laz.GetNextPoint(); double x = (p.X + centerX) * scaleX; double y = (p.Y + centerY) * scaleY; double z = (p.Z + centerZ) * scaleZ; var pt = transform.MultiplyVector(new Vector3((float)x, (float)y, (float)z)); byte intensity; if (LasRGB8BitWorkaround) { intensity = (byte)(p.Intensity >> 0); } else { intensity = (byte)(p.Intensity >> 8); } uint color = (uint)(intensity << 24); if (hasColor) { if (LasRGB8BitWorkaround) { byte r = (byte)(p.Red >> 0); byte g = (byte)(p.Green >> 0); byte b = (byte)(p.Blue >> 0); color |= (uint)((b << 16) | (g << 8) | r); } else { byte r = (byte)(p.Red >> 8); byte g = (byte)(p.Green >> 8); byte b = (byte)(p.Blue >> 8); color |= (uint)((b << 16) | (g << 8) | r); } } else { color |= (uint)((intensity << 16) | (intensity << 8) | intensity); } points[i] = new PointCloudPoint() { Position = pt, Color = color, }; } } finally { EditorUtility.ClearProgressBar(); } var unityBounds = GetBounds(bounds); unityBounds.center = transform.MultiplyPoint3x4(unityBounds.center); unityBounds.extents = transform.MultiplyVector(unityBounds.extents); return(PointCloudData.Create(points, unityBounds, hasColor, transform.MultiplyPoint3x4(bounds.Center), transform.MultiplyVector(bounds.Extents))); } }