Coder Social home page Coder Social logo

hmstl's Introduction

Heightmap to STL

hmstl is a simple program to convert heightmap images to 3D models. The output format is STL.

Prerequisites

hmstl requires libtrix, my rudimentary C library for generating STL files from triangle lists.

Build

Compile hmstl with:

make hmstl

Usage

By default, hmstl can be used as a filter to convert heightmap images on standard input to STL models on standard output. The following options are also supported:

  • -i INPUT read heightmap image from the specified INPUT file. Otherwise, read heightmap image from standard input.
  • -o OUTPUT write STL data to the specified OUTPUT file. Otherwise, write STL data to standard output.
  • -z SCALE multiple heightmap values by SCALE. Default: 1
  • -b HEIGHT set base thickness to HEIGHT. Default and minimum: 1
  • -s terrain surface only; omits base walls and bottom
  • -a output ASCII STL instead of default binary STL

The following options apply a mask to the heightmap. Only the portion of the heightmap visible through the mask is output. This can be used to generate models of areas with non-rectangular boundaries.

  • -m MASK load mask image from the specified MASK file. Dimensions must match heightmap dimensions.
  • -t THRESHOLD consider mask values equal to or less than THRESHOLD to be opaque. Default: 127 (in range 0..255)
  • -h as an alternative to -m, use the heightmap as its own mask; elevations below THRESHOLD are considered masked.
  • -r reverse mask interpretation (swap transparent and opaque areas)

Supported input image formats include JPG (excluding progressive JPG), PNG, GIF, and BMP. Color images are interpreted as grayscale based on pixel luminance (0.3 R, 0.59 G, 0.11 B).

Example

Test scene heightmap

Create an STL model of tests/scene.pgm, the heightmap image above, with the following command. The -z option is used to scale height values; an appropriate value depends on dataset resolution and desired exaggeration.

hmstl -z 0.25 < tests/scene.png > tests/scene.stl

Here is the output displayed in Meshlab:

Test scene STL file

Here is a contrived masking example using the same heightmap and a compound oval mask:

hmstl -z 0.25 -i tests/scene.png -m tests/mask.png -o tests/blob.stl

Masked model

Here is a photo of a Makerbot printing of the scene-thick sample model:

Printed model of sample scene

Scale and Orientation

Each pixel in the input heightmap is output as a unit quad (1 unit extent in xy plane) comprised of two triangles. By default, Z values are assumed to use the same units; use the -z option to set the correct scale.

The upper left pixel of the input heightmap is output centered at x/y 0/h, where h is the y extent of the heightmap, with the bottom of the model at z 0. Because the output pixel quads are 1 unit square centered over the pixel coordinates, the actual upper left extent of the output model is at -0.5, h + 0.5.

Post-Processing

Meshlab's Quadric Edge Collapse Decimation filter is suitable for simplifying hmstl output to reduce the number of faces without losing important features. Use various constraints such as Preserve Boundary or Planar Simplification to ensure original edges are preserved.

License

Freely distributed under an MIT License. See the LICENSE files for details.

Acknowledgements

Heightmap images are loaded using Sean Barrett's public domain stb_image.c library.

hmstl's People

Contributors

anoved avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

hmstl's Issues

Generate binary STL output

Text mode is convenient for a proof of concept, but generates huge files for relatively small heightmaps.

heightmap surface is not perfectly watertight

I really like your program but encountered an issue:

The calculation of z coordinates for Vertex 1, 2, 3, 4 can result in differences of 1 ULP based on the pixel vertex location.
So if a point P on the map is V1 for pixel H, but point P on the map is V3 for pixel H2, then Z calculations for point P may differ by 1 ULP.

I believe this is due to rounding introduced in the for loop in avgnonneg().

For my application I rounded z values to 0.001 and fixed the issue for my application.
Similarly you may want to add a z rounding parameter.

Normal Vectors

The current version seems to output (0, 0, 0) for normal vectors of each face.

This causes stl models to not render when loaded for some graphics. In my case, webgl, three.js stl loader.

Support non-rectangular borders

to allow, for example, printing exactly the city limits. Take an additional bitmap masker layer input. Trickiest part is generating walls; for terrain, simply check whether the mask is set. Might make sense to take the approach of moving base generation (walls and bottom) into a separate utility.

Support more heightmap image formats

At minimum, PBMs for mask images, as well as ASCII mode PGM heightmaps.

It would be reasonable to use a library like netpbm to support those formats.

However, if using an external library to load images, might as well use something capable of reading other image formats (like PNG, TIFF, or JPG) directly. PGM was chosen initially just because it was feasible to write a parser with minimum effort.

Supporting other image formats directly could help in a scenario where elevation data is being generated/extracted from a large data set (SRTM, etc) on demand. The fewer conversion steps that must be managed, the cleaner the final application.

Restore original triangle strip mesh mode

The center point pattern generates many more facets without adding much surface detail. Triangle strips are a common and well established approach to representing a surface.

The main reason I adopted the center point pattern was I thought it made it slightly easier to consult the mask image. That's all that needs to be resolved in reverting to triangle strips.

Path overlay

Don't know if this would be best as part of hmstl or as a separate program. Given a route - a sequence of waypoint coordinates - generate a route model of constant thickness draped over the terrain mesh. The route model should be printable simultaneous with the terrain mesh using a dual extruder printer. The bottom surface of the route mesh should fit flush against the terrain, meaning it should be composed of triangles of the same scale (or, I suppose, smaller faces that lie flush against them).

Route coordinates can reasonably be required to lie within the extent of the height map and be given in terms of the height map pixel coordinates (eg, user must convert geographic coordinates to HM pixels first).

When/how to generate:

  • at same time as terrain mesh
  • separately, using same input height map and triangulation algorithm
  • separately, using terrain mesh stl as input, and "dropping feelers down" from superimposed route to find first intersection with surface?

The right approach may be to generate 2d path at max elevation first (offset to either side of waypoint path for thickness). Matching mesh tris exactly and entirely does not seem feasible if arbitrary direction lines are to be allowed.

Building with libtrix?

Hi there,

This project looks great, thanks for all your hard work. I am new to C and am having some difficulty getting the project to build with the the libtrix dependency.

/usr/bin/ld: cannot find -ltrix
collect2: error: ld returned 1 exit status
make: *** [hmstl] Error 1

Do you have any advice for building hmstl from scratch with your libtrix library? I am using Ubuntu if that changes anything!

Output simplification with Meshlab

To note in readme: Meshlab's Quadric Edge Collapse Decimation filter is suitable for simplifying hmstl output to reduce the number of faces without removing too much detail. Use various constraints such as Preserve Boundary and Planar Simplification to ensure the original edges are preserved.

Document coordinate transform

Add a section to the Readme explaining the output coordinate system - each pixel of the input is treated as one unit, and the -z option is used (by the user - nothing too clever or automatic) to adjust the z values to the same unit scale (if desired).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.