4 stable releases

1.3.0 Oct 12, 2020
1.2.0 Oct 8, 2020
1.1.0 Oct 8, 2020
1.0.0 Oct 7, 2020

#67 in Data formats

MIT license

92KB
2K SLoC

Image density/height map to mesh generator

crates-io version

About

Crates used to generate 2D mesh from images representing density/height map.

Algorithm gets source image:

image source

Converts it into density/height values (here from alpha channell):

image values

Next makes steepness values:

image steepness

And builds mesh based on the highest steepness points:

image mesh

Rust API

Important modules

Typical use case would be to use two of them to create mesh from images but in case you have your own image handler, you can stick to the core module and produce density maps by yourself.

Working with chunks

Chunks are used for example for real-time terrain generation when you have a destructible heightmap and just want to update modified chunks at a time, instead of whole terrain.

let image = DynamicImage::ImageRgba8(
    image::open("../resources/heightmap.png")
        .expect("Cannot open file")
        .to_rgba(),
);
let count = 4;
let width = image.width() / count;
let height = image.height() / count;
let images = (0..(count * count))
    .into_iter()
    .map(|i| {
        let col = i % count;
        let row = i / count;
        let x = col * width;
        let y = row * height;
        let mut image = image.crop_imm(x, y, width + 1, height + 1);
        let settings = GenerateDensityImageSettings::default();
        let map = generate_densitymap_from_image(image.clone(), &settings)
            .expect("Cannot produce density map image");
        let settings = GenerateDensityMeshSettings {
            points_separation: 16.0,
            is_chunk: true,
            keep_invisible_triangles: true,
            ..Default::default()
        };
        let mesh = generate_densitymesh_from_points_cloud(vec![], map, settings)
            .expect("Cannot produce density mesh");
        apply_mesh_on_map(&mut image, &mesh);
        (col, row, image)
    })
    .collect::<Vec<_>>();
let mut image = DynamicImage::new_rgba8(width * count, height * count);
for (col, row, subimage) in images {
    image
        .copy_from(&subimage, col * width, row * height)
        .expect("Could not copy subimage");
}
image
    .save("../resources/heightmap.chunks.png")
    .expect("Cannot save output image");

Result of working with chunks might look like that:

image chunks

Real-time density mesh modifications

Imagine that you have a one big mesh, you want to modify variable size regions of this mesh and don't want to split it into chunks - for this use case there is specialized LiveDensityMesh type.

let image = DynamicImage::ImageRgba8(
    image::open("../resources/heightmap.png")
        .expect("Cannot open file")
        .to_rgba(),
);
let settings = GenerateDensityImageSettings::default();
let map = generate_densitymap_from_image(image.clone(), &settings)
    .expect("Cannot produce density map image");
let settings = GenerateDensityMeshSettings {
    points_separation: 16.0.into(),
    keep_invisible_triangles: true,
    extrude_size: Some(8.0),
    ..Default::default()
};
let mut live = LiveDensityMesh::new(map, settings.clone());
live.process_wait().expect("Cannot process live changes");
live.change_map(64, 64, 128, 128, vec![255; 128 * 128], settings.clone())
    .expect("Cannot change live mesh map region");
live.process_wait().expect("Cannot process live changes");
live.change_map(384, 384, 64, 64, vec![0; 64 * 64], settings)
    .expect("Cannot change live mesh map region");
live.process_wait().expect("Cannot process live changes");
let mut image = DynamicImage::ImageRgba8(
    generate_image_from_densitymap(live.map(), false).to_rgba(),
);
apply_mesh_on_map(&mut image, live.mesh().unwrap());
image
    .save("../resources/heightmap.vis.png")
    .expect("Cannot save output image");

With that we have added two solid rectangles and result looks like:

image live

CLI

Install / Update

cargo install density-mesh-cli --force

Example

density-mesh mesh -i image.png -o mesh.obj --obj

Options

density-mesh-cli 1.3.0
Patryk 'PsichiX' Budzynski <psichix@gmail.com>
CLI app for density mesh generator

USAGE:
    density-mesh.exe [SUBCOMMAND]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

SUBCOMMANDS:
    help     Prints this message or the help of the given subcommand(s)
    image    Produce density map image
    mesh     Produce density mesh
density-mesh.exe-image
Produce density map image

USAGE:
    density-mesh.exe image [FLAGS] [OPTIONS] --input <PATH> --output <PATH>

FLAGS:
    -h, --help         Prints help information
    -s, --steepness    Produce steepness image
    -V, --version      Prints version information
        --verbose      Display settings used

OPTIONS:
        --density-source <NAME>    Density source: luma, luma-alpha, red, green, blue, alpha [default: luma-alpha]
    -i, --input <PATH>             Input image file
    -o, --output <PATH>            Output image file
        --scale <INTEGER>          Image scale [default: 1]
density-mesh.exe-mesh
Produce density mesh

USAGE:
    density-mesh.exe mesh [FLAGS] [OPTIONS] --input <PATH> --output <PATH> <--json|--json-pretty|--yaml|--obj|--png>

FLAGS:
    -h, --help                        Prints help information
        --is-chunk                    Density map is a chunk, part of the bigger density map
        --json                        Produce JSON mesh
        --json-pretty                 Produce pretty JSON mesh
        --keep-invisible-triangles    Keep invisible triangles
        --obj                         Produce OBJ mesh
        --png                         Produce PNG mesh visualization
    -V, --version                     Prints version information
        --verbose                     Display settings used
        --yaml                        Produce YAML mesh

OPTIONS:
        --density-source <NAME>            Density source: luma, luma-alpha, red, green, blue, alpha [default: luma-
                                           alpha]
        --extrude-size <NUMBER>            Extrude size
    -i, --input <PATH>                     Input image file
        --max-iterations <INTEGER>         Maximum tries number when finding point to place [default: 32]
    -o, --output <PATH>                    Output mesh file
        --points-separation <NUMBER>       Points separation [default: 10]
        --scale <INTEGER>                  Image scale [default: 1]
        --steepness-threshold <NUMBER>     Steepness threshold [default: 0.01]
        --visibility-threshold <NUMBER>    VIsibility threshold [default: 0.01]

Dependencies

~6MB
~124K SLoC