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);
        }
Example #2
0
        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)));
            }
        }