Mapping

Attribute-driven parameter control. Build your own behaviours instead of relying on fixed UIs.

Problem: Why Mapping Exists

Natsura needed a way to control growth that wasn't just “add more sliders to every node”.

For scalars, Mappings solve this: they turn attributes (and other data) into parameter values, so you can build your own behaviours on top of the simulation instead of waiting for new hard-coded parameters.


What Mapping Enables

Mapping lets a single parameter:

  • Vary along the plant (per point / per segment).
  • Change with time/age, distance, or depth in the structure.
  • React to world-space information (via effectors and attributes).
  • Contain controlled randomness instead of noise everywhere.
  • Switch modes based on conditions (age, generation, region, etc.).

Examples of what that means artistically:

  • Taper branches or trunks along their length.
  • Make forks more likely near the top of the tree than near the base.
  • Bend growth toward light or with wind.
  • Turn decorations on only after certain ages or generations.
  • Blend between “forms” (e.g. young vs old profile) smoothly.

Mapping doesn’t change the simulation itself.
It decides what numbers nodes see when they ask “what is my pitch/width/fork probability/etc. here?”.


How to Use Mapping (Artist View)

  1. Pick a parameter to drive
    • On a node like Grow, choose a parameter (e.g. Pitch, Width, Fork Probability).
  2. Promote it to a mapping
    • Instead of typing a single number, attach a map chain to that parameter’s socket.
  3. Add map nodes to the chain
    • Use nodes like Map, Map Attribute, Map Random, Map If, Map Blend, Map Constant.
    • Each node contributes part of the logic: read some data, remap it, blend it into the result.
  4. Preview the effect
    • Run the simulation. The parameter is now a per-point value that changes according to your chain.

You can always drop back to “just a number” if you don’t need mapping.
Mapping is there when a single slider stops being enough.


How Mapping Works (Broadly)

At a high level:

  • A map chain is a definition of how to compute one scalar value per point for a parameter.
  • Each map node adds one step: read an attribute, apply a ramp, add randomness, blend with the previous value, and so on.
  • Internally, this becomes a small VEX program plus data that is evaluated later during simulation.

If you know Houdini:

  • Think of mapping as a mini VOP/VEX graph aimed at a single parameter.
  • You work with map nodes instead of wiring VOP nodes directly, but the idea is the same: data flows in, code transforms it, you get a value out.

You don’t need to know any of the internals to use mapping.
All of the “program assembly” is hidden behind the map nodes.


Where Mapping Is Used Now

Today, mapping is mainly used on Grow to drive:

  • Length and width over repeats.
  • Pitch, roll, yaw, bending and spirals.
  • Fork probability and related branching behaviour.
  • Noise/variation strengths and other scalar controls.

Where Mapping Will Be Used

Mapping is being extended to:

  • Other core nodes that control structure and flow.
  • Decoration and scattering nodes (surface, cluster, selection).

The goal is that anywhere you see “this could be more interesting than a constant slider”, you can plug in a map chain.


Current Mapping Options and Combinations

Core map node types and how they combine:

  • Map Constant
    • Set a base value (e.g. default pitch or width).
    • Often the first node in a chain.
  • Map Attribute
    • Read an attribute such as u, age, root_distance, branch_id, or a custom one.
    • Remap it through ranges or ramps (classic taper along u lives here).
  • Map Random
    • Add controlled randomness.
    • Seeded by branch ID, point ID, or other attributes for stable variation.
  • Map If
    • Condition-based logic (e.g. “if age > 2, use this profile, otherwise use that one”).
    • Good for “only after this stage” or “different behaviour per generation”.
  • Map Blend
    • Blend between two map sub-chains.
    • Useful for mixing shapes (e.g. “between young and old taper” or “between calm and windy response”).
  • Map (general)
    • A more flexible building block that can act like a combination of the above behaviours depending on settings.

Interesting artistic combinations:

  • Attribute + Random
    • Taper width by u and then add small random variation → natural, non-uniform branches.
  • Effector + Attribute
    • Use an effector (e.g. light direction reduced to a scalar) multiplied by a ramp along u → branches that bend more toward light at the tips than at the base.
  • Map If + Random
    • Use Map If on generation to make higher generations more random and lower generations more stable.
  • Map Blend for style mixing
    • Two fully different map chains blended with a slider → smoothly interpolate between two “tree styles” without rebuilding the graph.

Advanced Topics (for Technical Users)

The rest of this section is aimed at tech artists, TDs, and developers.
If you only care about artistic control, you can stop reading here.


Internal Mental Model

Internally, a map chain is:

  • A structure: map nodes that each contribute a small VEX snippet and an optional primitive.
  • A skeleton program: assembled from all those snippets, with a placeholder output called <parm>.
  • A data store: primitives holding UI parameters (ramps, ranges, etc.) in a separate geometry stream.

At this stage, the chain does not know which concrete attribute it will write; <parm> is just “whatever this parameter will be”.


Discrete Stages: From Map Chain to Running Code

There are several distinct steps between “artist builds a map chain” and “code runs per point”:

  1. User builds the chain
    • Each map node contributes:
      • A VEX snippet that uses <parm> as its output symbol.
      • A primitive (in another stream) containing its UI data if needed.
    • Result: a chain that defines how to compute a value, but not where to store it.
  2. Binding <parm> to a real attribute
    • When a node ingests the chain for a specific parameter, it decides what <parm> actually means.
    • Example: for Grow’s Pitch on a given Grow, <parm> becomes @cluster_pitch.
    • All snippets in the chain are rewritten so <parm> is replaced by that attribute name.
  3. Program assembly
    • A code processor concatenates snippets from the chain (in order) into a single VEX block.
    • This block is now a complete program for computing that attribute.
  4. Compilation and APEX graph
    • The assembled VEX is fed into an Attrib VOP.
    • That VOP is compiled and wired into an APEX Invoke graph.
    • From this point on, the Grow node can call that graph to evaluate the mapping.

Each Grow with mapped parameters ends up with its own unique program per mapped parameter (or per group of parameters), but:

  • The VEX is compiled once per Grow.
  • Subsequent evaluations reuse the compiled program.

Primitives, Streams, and primuv

To avoid recompiling whenever a slider or ramp changes:

  • Mapping splits code and data.

Code:

  • Lives in the assembled VEX program constructed from snippets.

Data:

  • Lives on primitives in a separate geometry stream (typically stream 1).
  • Each map node’s point has a path attribute that points to its corresponding primitive.

At evaluation time:

  • The program runs over stream 0 (the simulated plant geometry).
  • It uses the path attribute and primuv() (and related calls) on stream 1 to read its parameters from primitives.

Typical pattern:

  • Treat an attribute (like u) as a UV (or a 1D parameter).
  • Use primuv() on stream 1 to sample @P.y (or other components) on a ramp curve primitive.
  • Use that value as part of the scalar being written to <parm>.

Because primitives can hold arbitrary data, this mechanism can support more complex map behaviours over time without changing the program structure.


Re-evaluation vs Re-compilation

This separation has a key consequence:

  • Changing map UI values (ramps, ranges, toggles) only changes the primitive data.
  • The compiled VEX program remains the same.
  • The APEX graph does not need to be rebuilt; it is simply re-evaluated with new primitive values.

You only pay the cost of rebuilding/compiling when:

  • The map chain structure changes (nodes added/removed/wired differently), or
  • You change the advanced VEX snippets themselves.

Customising and Extending Mapping

For advanced users:

  • Each map node’s VEX snippet is exposed under an Advanced section.
  • You can modify those snippets directly if you follow the <parm> convention and the expected inputs.
  • You can create new mapping behaviours by:
    • Writing new snippets,
    • Defining how they are combined in the chain, and
    • Creating primitives to store any additional parameters.

Beyond per-node customisation:

  • You can build compound map HDAs that internally contain multiple core map nodes wired together to express higher-level plant behaviours.
  • Artists then see one higher-level node, built from familiar low-level map components.

One-Line Mental Model

Mappings are deferred scalar programs:

  • Artists build them from map nodes.
  • Nodes bind them to real attributes and compile them once.
  • The compiled code is reused to fill per-point parameter values during simulation, driven by attributes, effectors, and primitives.