Engineering Dynamics Company
Point Cloud Toolkit™
Point Cloud Processing & Visualization for Blender
Importing, filtering, and surfacing laser-scan point clouds in Blender.
This guide walks through installing the add-on, finding its panels, and completing the common tasks, with a reference for every control. For a terse feature list, see README.md.
A note on values: defaults are shown in parentheses. Distance controls follow the scene's unit system in Blender; their defaults are given here in metres, Blender's base unit (1 unit = 1 m).
Point Cloud Toolkit adds an EDC Toolkits tab to the 3D View sidebar (press the N key) with four tool sections that share one point-cloud selection, plus a display section for the viewport:
| Section | What it does |
|---|---|
| Import Point Cloud | Multi-format import — PLY (ASCII or binary), PTX, PTS, E57, LAS/LAZ, and PCD — as a coloured mesh with a Geometry Nodes point display |
| Point Cloud Display | Viewport-only controls — Points Visible % and Point Size — for the selected cloud |
| Filter Point Cloud | Voxel subsampling and Statistical Outlier Removal, always to a new cloud (the original is never modified) |
| Create Ground Surface | Drape a regular grid over the cloud: per-cell ground-percentile height sampling, spike rejection, hole filling, clip-to-object, and a colour texture baked from the cloud |
| Create 3D Surface | Full 3D reconstruction via Open3D (Poisson / Ball Pivoting / Alpha Shape) for vertical and overhanging geometry, with automatic texture baking, tiling, bulge trimming, and long-triangle trimming |
The add-on targets Blender 4.x and is numpy-vectorized throughout, so it stays fast on million-point clouds.
PointCloudToolkit-<version>.zip file.The add-on's preferences let you rename the sidebar tab — set it to an existing tab's name to share a tab with other add-ons.
On Blender 4.2+ the same zip also installs as an extension (Preferences → Get Extensions → Install from Disk), and updates automatically when added from the EDC Software repository.
If you only use part of the toolkit, you can hide the rest. Open Edit → Preferences → Add-ons, expand Point Cloud Toolkit, and use the Features checkboxes:
| Feature | Hides |
|---|---|
| Import Point Cloud | The import panel (PLY / PTX / PTS / E57 / LAS / PCD) |
| Filter Point Cloud | The subsample / outlier-removal panel |
| Create Ground Surface | The draped ground-grid panel |
| Create 3D Surface | The Open3D 3D reconstruction panel |
All four are on by default. Unchecking one removes its panel from the sidebar immediately — no restart needed. Documentation always stays visible, so the user guide and provenance report remain reachable.
Nothing is uninstalled: the tools are only hidden, and re-checking a box brings the panel back with its settings intact.
Note. The Filter settings still apply when you build a surface, even with the Filter Point Cloud panel hidden — subsampling and outlier removal run as part of Create Ground Surface and Create 3D Surface. Hide the panel only if you're happy with the current filter settings.
Click Save Preferences (or enable Auto-Save Preferences) so your choices persist the next time you open Blender.
.blend first so the texture JPG can be written next to it.Imports PLY (ASCII or binary), PTX, PTS, E57, LAS/LAZ, and PCD files as a coloured mesh point cloud displayed through a Geometry Nodes modifier (also available on File → Import). The imported cloud is automatically selected as the Point Cloud source used by the other sections.
Every imported cloud gets its own Geometry Nodes display tree; the controls for it are in Point Cloud Display, immediately below.
x y z, x y z intensity, x y z r g b, or x y z intensity r g b. RGB is taken as 0–255 unless the values are already within 0–1. A scan with intensity but no colour is shown as greyscale, stretched over that file's own intensity range — PTS does not say whether an intensity is -2048…2047, 0–255 or 0–1, and stretching is the only reading that works without being told. It affects display only. The leading point-count line is used where present and not required, since exporters differ; several scans concatenated into one file are read as one cloud.Points whose coordinates are not finite (NaN or Inf — truncated writes, a scanner error row) are dropped, and the count is logged. This matters more than it sounds: the check for "is this cloud far from the origin?" is a maximum over the coordinates, so a single NaN made it false and a georeferenced scan imported unrecentered at 500,000 m — losing exactly the precision recentering exists to protect, silently. A single Inf was worse: it made the centroid infinite, so every point in the file came out unusable.
pct_global_shift / pct_import_scale) so the original coordinates can be recovered — the same idea as CloudCompare's Global Shift. Three modes:Knowing which frame a cloud landed in. Two scans of one site overlap only if they share a shift, so the import says which happened rather than leaving it to the console:
A cloud already near the origin — anything local, or a georeferenced one scaled down from centimetres — is moved by nothing, so it leaves no offset to inherit. That is not the same as not having imported, and it is not the same as the offset being gone: if an earlier cloud did set one, it stays in play and the panel says so rather than naming the wrong cloud.
pct_shift_reused) and appears in the Provenance Report as *Aligned with earlier import*.Manual Shift X/Y/Z is only shown in Manual mode. It is only read there, and showing it under Auto made it look like a value that would be used.
Importing several clouds that share a georeference: the shift applied to the first cloud is remembered on the scene (pct_session_shift), and Auto reuses it for later imports as long as that offset keeps the new cloud near the origin — so multiple scans of the same site import aligned to each other (like CloudCompare reusing one global shift for a session), instead of each getting its own centroid. A cloud from a different, far-away site still gets its own shift. Each cloud also records its own offset in the custom properties pct_global_shift / pct_import_scale, so file = (local + global_shift) / scale recovers the originals.
pye57 package and LAZ needs laspy[lazrs] — both install automatically the first time such a file is opened, or via the Install E57 / LAZ Support Now button. Compressed PCD uses Open3D.A second view of the selected cloud's Geometry Nodes modifier: it mirrors every input the display tree declares, labelled as the tree labels it, in the tree's own order — the same rows the Modifier Properties panel shows, in the sidebar where you are already working. Nothing here touches the data; filtering, surfacing and baking always use every point, whatever the viewport is showing.
As imported, that is:
Because the panel mirrors rather than matches by name, editing the display tree is reflected straight away: add an input and it appears here, remove one and its row goes with it. Values are per-cloud — each import has its own display tree, so adjusting one never changes another.
Which cloud these edit. The active object, when it is a cloud — unlike the processing tools below, which follow the pinned Point Cloud. The two rules disagree as soon as a second cloud is imported, because importing pins the new one: selecting the first and moving a slider would have changed the second, which looks exactly like the panel not working. Display is about the thing you are looking at, and the Showing: line names it. The pinned cloud is still the fallback, so the panel is not empty when something else is selected.
This section is not one of the four tools, so it stays available even with every tool hidden in Preferences. A cloud too dense to navigate is a problem you have before you reach any of them.
How this works, and why it needed to. Blender offers no way for a Python panel to draw a geometry nodes modifier input — its own Modifier Properties panel is built in C, and layout.prop() needs a struct and a property name. Blender 5.x keeps each socket's value inside an ID property group, which prop() refuses as its data argument outright.
So each row here is one of the add-on's own properties, wired to the selected cloud: reading it reads the modifier, and setting it writes the modifier. Nothing is stored on the side, so the panel and the Modifier Properties panel cannot drift apart — there is one copy of the number and it lives on the modifier. Up to eight inputs are mirrored, which is well past the two the display tree declares; an input the modifier holds no number for (a Geometry input, or one removed from the tree) gets no row.
Where the value physically lives has moved between releases — 3.x and 4.x use an ID property on the modifier, 5.x an entry under modifier.properties.inputs — so the add-on finds it rather than assuming, and Diagnose Display Storage prints the whole shape to the System Console if it ever cannot.
If the rows are missing. A cloud imported by an older version, or one whose modifier never had a value written for an input, gives the panel nothing to mirror and the rows do not appear. Restore Display Controls writes the values back onto the selected cloud's modifier — taking each input's own default from the node group, or the import defaults (Points Visible % 100, Point Radius 0.01) when the group has none — and the rows come back. It only fills inputs the modifier is actually missing, so a value you have already set is never overwritten, and inputs that hold no number (a Geometry input) are left alone. If an input still cannot be placed, the System Console names it; Diagnose Display Storage is the next step.
Drapes a regular grid over the cloud to build a drivable ground surface. Distance controls are given in metres (1 unit = 1 m).
This is the setting that decides whether a wood comes out. Spike Reject compares each cell against a local ground reference built by opening the grid at this width, so an object *wider* than the setting carries the reference up with it and is read as grade — the spikes stay, and no amount of lowering Spike Reject (Above) will touch them. A belt of trees is routinely 20–40 m across, so a scan full of vegetation spikes usually wants Spike Width raised to comfortably exceed the widest continuous stand, not Spike Reject lowered. The cost is that real rises narrower than the setting are flattened too, so it wants to exceed the vegetation and stay under the terrain: a wide belt of trees on genuinely rolling ground is the case where clipping the cloud beats tuning either number.
.blend — save the .blend first.You can drape one or more aligned aerials / orthophotos over a created surface instead of the colour baked from the point cloud — useful when a recent aerial reads better than the scan colours. Both textures are kept, so you can switch back at any time. This works on ground and 3D surfaces.
If the cloud was recentered on import, aligning the aerials visually over the surface already puts them in the right (shifted) space — no coordinate math needed. Tip: if your aerials are just tiles of one orthomosaic, merging them into a single image first (e.g. QGIS / gdal_merge) lets you use one plane instead.
Adjust one setting at a time and re-create the surface to see its effect.
| If you see… | Try |
|---|---|
| Too many low points / the surface dips into potholes or drops below the roadway | Lower Below-Grade Reject (e.g. 0.5 → 0.2 m) so stray below-ground returns are cut and refilled; if whole areas sag, raise Ground Percentile a little (e.g. 10 → 20) so it samples higher. |
| Spikes or bumps sticking up from trees, poles, walls, or parked cars | Lower Spike Reject (Above) (e.g. 1.0 → 0.5 m) so shorter spikes are caught. |
| A whole stand of trees comes through as spikes, and lowering Spike Reject changes nothing | Raise Spike Width (Max) past the width of the stand (20–40 m is normal for a tree belt). Anything wider than this setting is read as grade, so the height threshold never applies to it. |
| Real curbs, embankments, or hills being flattened | Raise Spike Reject (Above) (less aggressive) and/or lower Spike Width (Max) so only narrow objects are removed. |
| The road colour is tinted by vehicles or foliage | Lower Color Height Tolerance (e.g. 0.25 → 0.1 m) so only near-ground points contribute colour. |
| Gaps or holes in the surface | Enable Fill Holes and raise Max Fill Distance to bridge farther. |
| Surface invented over a true void (e.g. across a ditch or off the road edge) | Lower Max Fill Distance, or set Clip To Object to the area of interest. |
| Surface is too heavy / slow to work with | Raise Resolution (Cell Size), enable Subsample (Voxel), or add a Decimate modifier. |
| Blurry texture | Raise Texture Resolution, or use Texture Tiles for more total pixels. |
Full 3D reconstruction via Open3D (installed automatically on first run) for vertical and overhanging geometry — walls, embankments, vehicles, structures. For drivable ground, Create Ground Surface remains the better tool. Distance controls are given in metres (1 unit = 1 m).
.blend (a pct_computed_normal attribute on the cloud) so it survives save and reload. The system console prints whether a run reused the cache or recomputed.These appear only when a built 3D surface is the active object. After a build the cloud is often what is selected, and the panel says so rather than simply not showing them.
This is a different fault from a bulge, and Trim Bulges cannot reach it. A triangle spanning a hole has all three corners on real scanned points, so measuring vertices against the cloud finds nothing wrong with it. Its *edges* are what give it away.
Max Edge Length at 0 measures the surface itself — a low quantile of its edge lengths, times a wide factor — so the same setting works whether the scan is in metres or feet, and however dense it is. A low quantile rather than the median on purpose: once webbing is a large fraction of the surface, the median lands *between* the good and bad triangles and the automatic threshold floats out of reach of both. Type a length when you want to be precise; the operator reports how many faces it removed, and refuses rather than emptying the object if every face exceeds the threshold. Unlike Trim Bulges it needs no Open3D.
Poisson is the usual starting point; change one control at a time and rebuild.
| If you see… | Try |
|---|---|
| Surface balloons out past the scan (Poisson invents geometry in empty areas) | Raise Density Trim (e.g. 0.05 → 0.2), or run Trim Bulges (To Cloud) after the build; lowering Poisson Depth also helps. |
| Webbing across holes, doorways or scan shadows (long thin triangles bridging a gap) | Run Trim Long Triangles after the build. Trim Bulges will not remove these — their corners are all on the cloud. |
| Holes or gaps in the surface | Ball Pivoting: raise BPA Radius Multiplier to span wider gaps. Alpha Shape: raise Alpha. Poisson: lower the depth. |
| Surface is too smooth / fine detail lost | Raise Poisson Depth (e.g. 9 → 10–11). |
| Surface is noisy or lumpy | Lower Poisson Depth, or raise Normal Neighbors (k) for smoother normals. |
| Walls or overhangs look inside-out or patchy | Set Normal Orientation to Consistent (the Up option can misorient vertical faces). |
| Reconstruction is very slow | Turn on Use Existing Normals (if the cloud has them), use the Up orientation, and/or Subsample (Voxel) first. The first run also caches its normals, so later rebuilds skip the slow step. |
| Blurry texture | Raise Texture Resolution or use Texture Tiles; for flat, top-down terrain, Planar UV is the sharpest. |
The Documentation panel has an Open User Guide button (this guide) and a Provenance Report (Active) button. The report writes a self-contained HTML document for the active cloud or surface — the source file and its SHA-256 hash, point/geometry counts, coordinate extents in both Blender and original (georeferenced) space, the import scale and global shift, and the tool/Blender versions — for documentation and chain-of-custody. It's saved next to the .blend (or a temp folder if unsaved) and opened in your browser. The "Prepared by" field is left blank for you to fill in; it is not taken from the OS login.
Optional Python packages install themselves into Blender's Python when first needed — no manual pip work is required. An internet connection is needed the first time each feature is used.
| Package | Needed for | When it installs |
|---|---|---|
open3d | 3D reconstruction, compressed PCD | First Reconstruct 3D Surface (large download; may need a Blender restart) |
pye57 | E57 import | First E57 file opened, or the install button in the panel |
laspy[lazrs] | LAZ import | First LAZ file opened, or the install button in the panel |
.blend. If the file isn't saved yet, the texture is packed into the .blend instead — save the .blend and rebake (or re-create the surface) to write the JPG to disk.pip install <package> with Blender's Python and restart Blender.Point Cloud Toolkit is © Engineering Dynamics Company, written by Anthony Cornetto, and is free software licensed under the GNU General Public License, version 2 or later — see the bundled LICENSE file. Official builds, updates, and support are provided by Engineering Dynamics Company; visit edccorp.com or contact EDC support.