Crate immense

source ·
Expand description

immense describes 3D structures with simple composable rules and outputs them as Wavefront object files you can plug into your renderer of choice.

Demo

 Rule::new().push(vec![
     Replicate::n(1, vec![Tf::saturation(0.8), Tf::hue(160.0)]),
     Replicate::n(36, vec![Tf::rz(10.0), Tf::ty(0.1)]),
     Replicate::n(36, vec![Tf::ry(10.0), Tf::tz(1.2), Tf::hue(3.4)]),
    ],
    cube(),
)

Table of Contents

  1. Intro
  2. Composing Rules
    1. Recursion
    2. Randomness
  3. Color
  4. Ergonomics Macros
  5. Custom Meshes

Intro

In immense, you create a Rule that describes your structure, which is ultimately composed of meshes. immense provides some builtin meshes, such as [cube][self::rule::builtin::cube], and you can create your own rules by using these builtins which you’ll see in the next section.

After you’ve built your Rule, you can export the meshes it expands to as a Wavefront object file for the next part of your workflow, whether that is rendering it in Blender, printing it in your 3D printer, or importing it into your game!

Composing Rules

Let’s start with a cube. You probably want to write your meshes to a file and watch them in a viewer with autoreload. Meshlab is a great viewer (and much more) that can reload your meshes when changed. Check out ExportConfig to see what options you can set that will work best for your rendering or printing workflow.

use immense::*;
use std::fs::File;

let rule = cube();
let meshes = rule.generate();
let mut output_file = File::create("my_mesh.obj")?;
write_meshes(ExportConfig::default(), meshes, &mut output_file)?;

We can translate the cube with the Tf::t* family of functions which generate translate transforms. We’ll apply [Tf::tx][rule::transforms::Transform::tx] by creating our own rule and invoking the cube rule with a transform.

let rule = Rule::new().push(Tf::tx(3.0), cube());

We can replicate transforms with [Replicate][rule::transforms::Replicate] which generates multiple invocations of a subrule, each with more applications of the same transform applied to it.

let rule = Rule::new().push(Replicate::n(3, Tf::ty(1.1)), cube());

Notice that our translation is 1.1 and that that is 0.1 more than the length of our cube. That’s no coincidence! All the built in meshes are 1 in length so that you can use convenient measurements like this, even when deep in a transform stack.

Recursion

You can generate rules recursively with the api we’ve covered so far, but doing so would put your entire rule tree in memory at one time, which can become a problem. immense provides a trait, ToRule, so you can give it types that can instantiate rules when needed.

struct RecursiveTile {
    depth_budget: usize,
}

impl ToRule for RecursiveTile {
    fn to_rule(&self) -> Rule {
        let rule = Rule::new()
            .push(vec![Tf::t(0.25, 0.25, 0.0), Tf::s(0.4)], cube())
            .push(vec![Tf::t(-0.25, -0.25, 0.0), Tf::s(0.4)], cube())
            .push(vec![Tf::t(-0.25, 0.25, 0.0), Tf::s(0.4)], cube());
        if self.depth_budget > 0 {
            rule.push(
                vec![Tf::t(0.25, -0.25, 0.0), Tf::s(0.4)],
                RecursiveTile {
                    depth_budget: self.depth_budget - 1,
                },
            )
        } else {
            rule
        }
    }
}

let rule = RecursiveTile {
    depth_budget: 3
}.to_rule();

Randomness

Using ToRule to delay rule construction, we can sample some random values each time our type builds a rule.

struct RandCube;

impl ToRule for RandCube {
    fn to_rule(&self) -> Rule {
        Rule::new().push(
            *thread_rng()
                .choose(&[Tf::tx(0.1),
                          Tf::tx(-0.1),
                          Tf::tx(0.2),
                          Tf::tx(-0.2)])
                .unwrap(),
            cube(),
        )
    }
}

let rule = Rule::new().push(Replicate::n(4, Tf::ty(1.0)),
                            RandCube {});

Color

immense can export some colors alongside your mesh, by linking the object file output to an mtl file (material library). Set the output mtl file in export_colors and immense will write out colors.

You can specify colors overrides and transforms in HSV color space using Ogeon’s palette. See [Tf::color][crate::rule::transforms::Transform::color], [Tf::hue][crate::rule::transforms::Transform::hue], [Tf::saturation][crate::rule::transforms::Transform::saturation], [Tf::value][crate::rule::transforms::Transform::value].

Ergonomics Macros

immense provides two ergonomics macros that make defining rules and transform sequences a little easier once you have an intuition for their semantics. They are rule! and tf!, which help compose rules and transform sequences respectively.

They transform the demo code above into:

rule![
    tf![
        Tf::saturation(0.8),
        Tf::hue(160.0),
        Replicate::n(36, vec![Tf::rz(10.0), Tf::ty(0.1)]),
        Replicate::n(36, vec![Tf::ry(10.0), Tf::tz(1.2), Tf::hue(3.4)]),
    ] => cube(),
]

Custom Meshes

You can create meshes on your own and use them as rules by calling Mesh::from if you format your meshes according to object file format.

Meshes can be expensive to allocate. immense handles the primitives on your behalf, but if you introduce your own meshes you must be careful not to allocate them more than once. One million references to a sphere are fine, one million spheres will probably kill the process.

An example is the [sphere][self::rule::builtin::sphere] builtin which allows you to create a potentially expensive sphere estimation:

let sphere: Rc<Mesh> = sphere(/*resolution=*/4);
let rule = Rule::new().push(Tf::s(2.0), sphere);

Macros

An ergonomics macro for defining rules out of transformed subrule invocations.
An ergonomics macro for listing transforms that will apply in order and branch on replications.

Structs

Configuration for Wavefront object file output.
Linear HSV color space.
A custom mesh definition described by a set of vertices, normals, and faces.
An iterator that iterates over a Rule’s generated meshes.
An OutputMesh can be written out in an object file.
Replicates a transform n times.
A hue type for the RGB family of color spaces.
A composition of subrules to expand until meshes are generated.
A Transform, when applied, modifies a mesh. When applied to a rule, it transforms all the meshes that rule eventually expands to. Transforms may be translations, scales, rotations, etc.

Enums

immense Error type.
A policy for grouping meshes in the object file.
A TransformArgument is a transform that should be applied to the invocation of a Rule.

Traits

A trait for types that can become rules.

Functions

A cube of size 1 whose center is at the origin.
An icosphere of diameter 1.
A sphere of the given resolution. Produces 20 * 4 ^ resolution polygons to estimate the sphere.
Initializes a vertex for a custom mesh.
Writes out meshes as a Wavefront object file to the given Write sink.

Type Definitions

An ergonomic alias for Transform.
A type for custom mesh vertices. Initialize with vertex.