Logo   Home Table of Contents    News  Dev Blogs  Gallery  Timeline  Legend  Tags 
Join us on... 
Banner

Current: Month Day, Year

9.3.3 Macro JSON file

The macro.json file defines MakeHuman2's macro target system - a sophisticated mechanism for applying complex combinations of morphing targets based on hierarchical slider interactions. Macro targets allow a single slider (like "Gender" or "Age") to automatically calculate and apply dozens or even hundreds of underlying targets with appropriate weights. The macro system solves the problem of interdependent character attributes when you change gender, age, muscle, and weight, the system needs to apply different combinations of targets that work together harmoniously. The macro system calculates these combinations automatically.

File Locations and loading order

Per mesh there might be 3 different target folders, two of them can be used for macro.json file.
User constant targets <UserDataDir>/data/contarget/<basename>/macro.json This method is used to replace the read-only system-targets, this is used in case of a complete new base in the user folders (the system-targets are read-only. Especially when a mesh is downloaded as a packet. macro.json must be manually created.
System targets <SystemDir>/data/target/<basename>/macro.json System targets are supplied from MakeHuman team by installation. macro.json is part of the installation.

<basename> is the base mesh identifier (e.g., hm08, mh2bot)

To conclude: If contarget/<basename>/macro.json exists, the system file is ignored. Otherwise, system files are scanned.

File Format

The macro.json file consists of two main sections:

{ "components": { "component-name": { "values": ["value1", "value2", "value3"], "pattern": "target/path/prefix", "steps": [0.0, 0.5, 1.0], "sum": ["Slider1", "Slider2", "Slider3"] } }, "macrodef": [ { "name": "macro-name", "comp": ["component1", "component2"], "folder": "target_folder", "targets": [ {"name": "calculated-name", "t": "actual-target-file"} ] } ] }

Components Section

The components object defines reusable building blocks that describe how slider values map to target naming patterns. Each component represents a character dimension (gender, age, muscle, etc.).

The component structure is:

"component-name": { "values": ["label1", "label2", "label3"], "pattern": "target-path-prefix", "steps": [0.0, 0.5, 1.0], "sum": ["TargetName1", "TargetName2"] }

Property Reference

values

arrays of strings, required

List of naming labels for this component's states, it is used to construct target filenames. Number of values determines how many states the component has Examples: ["female", "male"] ["minmuscle", "averagemuscle", "maxmuscle"]
pattern

string, required

Path prefix to the target controlling this component, which must match a target defined in modelling.json. It is used to read current slider values. Examples: "macrodetails/Gender" "macrodetails-universal/Muscle"
steps

array of numbers, optional

Defines breakpoints on the slider (0.0 to 1.0 range). It is used for stepped interpolation between discrete states.The array length must equal values array length. When present, it creates interpolation between adjacent steps. Example: [0.0, 0.5, 1.0] means value1 at 0%, value2 at 50%, value3 at 100%
sum

array of strings, optional

Used for barycentric (3-way) sliders instead of stepped sliders. Lists the target names whose barycentric values sum to determine weights. It is mutually exclusive with steps - use one or the other. It must have exactly 3 entries for barycentric interpolation. Example: ["African", "Asian", "Caucasian"]

Component Types

Stepped component (2 states), example: Gender: "gender": { "values": ["female", "male"], "steps": [0.0, 1.0], "pattern": "macrodetails/Gender" } How it works:

- Slider at 0.0 → 100% female, 0% male
- Slider at 0.5 → 50% female, 50% male
- Slider at 1.0 → 0% female, 100% male
- Linear interpolation between the two states

Stepped component (3 states), example: Muscle: "muscle": { "values": ["minmuscle", "averagemuscle", "maxmuscle"], "steps": [0.0, 0.5, 1.0], "pattern": "macrodetails-universal/Muscle" } How it works:

- Slider 0.0 to 0.5 → interpolate between minmuscle and averagemuscle
- Slider 0.5 to 1.0 → interpolate between averagemuscle and maxmuscle
- At exactly 0.5 → 100% averagemuscle

Stepped component (4 states), example Age: "age": { "values": ["baby", "child", "young", "old"], "steps": [0.0, 0.1875, 0.5, 1.0], "pattern": "macrodetails/Age" } How it works:

- Four age ranges with different step sizes
- 0.0 to 0.1875 → baby to child transition
- 0.1875 to 0.5 → child to young transition
- 0.5 to 1.0 → young to old transition
- Non-uniform steps allow different aging speeds

Barycentric component (sum-based), example: Phenotype: "phenotype": { "values": ["african", "asian", "caucasian"], "pattern": "macrodetails/", "sum": ["African", "Asian", "Caucasian"] } How it works:

- Reads three barycentric slider values that sum to 1.0
- Each corner can be 0% to 100%
- Pattern + sum entry = full target path (e.g. macrodetails/African)
- All three values are read simultaneously
- Example: 60% african, 20% asian, 20% caucasian

Standard Components

Most base meshes use these standard components:
ComponentValues Type Purpose
genderfemale, maleStepped(2)Gender morphing
agebaby, child, young, oldStepped (4) Age progression
muscleminmuscle, averagemuscle, maxmuscleStepped (3)Muscle definition
weightminweight, averageweight, maxweightStepped (3)Body mass
phenotypeafrican, asian, caucasianBarycentricEthnic features
proportionuncommon, none, idealStepped (3)Beauty proportions
heightminheight, none, maxheightStepped (3)Height adjustment
cupsizemincup, averagecup, maxcupStepped (3)Breast size (female)
firmnessminfirmness, averagefirmness, maxfirmness Stepped (3)Breast firmness

Macrodef Section

The macrodef array defines macro target definitions - collections of targets that represent every possible combination of component states.

Macrodef Structure:

{ "name": "descriptive-macro-name", "comp": ["component1", "component2", "component3"], "folder": "target-subfolder", "targets": [ {"name": "component1-component2-component3", "t": "actual-file-name"} ] }

Property Reference

name

string, required

Descriptive name for this macro definition, it is used for logging and debugging and should indicate which components are involved. Example: "phenotype-gender-age" "gender-age-muscle-weight"
comp

array of strings, required

List of component names used in this macro, which must reference components defined in the components section. The order matters - determines how target names are constructed. Example: ["gender", "age", "muscle"]
folder

string, required

Subdirectory containing the target files for this macro, it is relative to the target directory. Examples: "macro_age" "macro_muscle" "macro_breast"
targets

array of objects, required

Complete list of all possible target combinations, each entry maps a calculated name to an actual target file, it's purpose is to re-use identical target files. Each entry consists of a 'name' based on component value combinations and 't', the actual target filename without .target extension. If 't' is null, it is average, so no target is needed. Example: {"name": "female-child-averagemuscle-averageweight-uncommon", "t": "female-child2old-allmuscle-allweight-uncommon"}

How Macro Calculation Works

When a macro slider changes (like Gender, Age, or Weight), the system:
  1. Reads current values of all relevant sliders
  2. Calculates component weights based on slider positions
  3. Generates target combinations by crossing all component values
  4. Applies interpolation weights for smooth transitions
  5. Maps calculated names to actual target files
  6. Applies targets with appropriate factors to the mesh

Step-by-Step Process

Step 1: Identify Influenced Macros

Each macro target in modelling.json has a macro_influence field:

"Gender": { "macro": "macrodetails/Gender", "macro_influence": [0, 1, 2, 3, 4] } This indicates which macrodef entries (by index) are affected by this slider.

Step 2: Read Component Values

For each macro definition, read the current slider values for all components:

# For macrodef with comp: ["gender", "age", "muscle"] gender_value = slider["macrodetails/Gender"].value / 100 # 0.0 to 1.0 age_value = slider["macrodetails/Age"].value / 100 muscle_value = slider["macrodetails-universal/Muscle"].value / 100

Step 3: Calculate Component Weights

For stepped components, interpolate between adjacent values:

# Gender with steps [0.0, 1.0] and values ["female", "male"] if gender_value <= 0.5: weights = {"female": 1.0 - (gender_value / 0.5), "male": gender_value / 0.5} else: weights = {"female": 0.0, "male": 1.0}

For barycentric components, read the three corner values:

# Phenotype with sum ["African", "Asian", "Caucasian"] weights = { "african": slider["macrodetails/African"].barycentric[0].value, "asian": slider["macrodetails/Asian"].barycentric[1].value, "caucasian": slider["macrodetails/Caucasian"].barycentric[2].value }

Step 4: Generate Target Combinations

Cross-multiply all component weights to generate every combination:

# Example: gender × age × muscle combinations = [] for gender_name, gender_weight in gender_weights.items(): for age_name, age_weight in age_weights.items(): for muscle_name, muscle_weight in muscle_weights.items(): combined_name = f"{gender_name}-{age_name}-{muscle_name}" combined_weight = gender_weight * age_weight * muscle_weight if combined_weight > 0.01: # Skip negligible weights combinations.append((combined_name, combined_weight))

Step 5: Map to Actual Targets

Look up each calculated name in the targetlink dictionary:

for calculated_name, weight in combinations: if calculated_name in targetlink: actual_target = targetlink[calculated_name] if actual_target is not None: apply_target(actual_target, weight)

Step 6: Apply Targets

Add all selected targets to the mesh with their calculated weights:

mesh.prepareMacroBuffer() for target_path, weight in target_applications: target_data = load_target(target_path) mesh.addTargetToMacroBuffer(weight, target_data) mesh.addMacroBuffer() # Apply all at once

Example Calculation

Scenario: Slider values are: Component Weights: gender: {"female": 0.25, "male": 0.75} age: {"young": 1.0} muscle: {"minmuscle": 0.5, "averagemuscle": 0.5}

Generated Combinations:

female-young-minmuscle: 0.25 × 1.0 × 0.5 = 0.125 female-young-averagemuscle: 0.25 × 1.0 × 0.5 = 0.125 male-young-minmuscle: 0.75 × 1.0 × 0.5 = 0.375 male-young-averagemuscle: 0.75 × 1.0 × 0.5 = 0.375

Result: 4 targets applied with weights 12.5%, 12.5%, 37.5%, 37.5%

Example Macro Definitions

Simple 2-Component Macro, Gender × Age (24 targets)

{ "name": "phenotype-gender-age", "comp": ["phenotype", "gender", "age"], "folder": "macro_age", "targets": [ {"name": "african-female-baby", "t": "african-female-baby"}, {"name": "african-female-child", "t": "african-female-child"}, {"name": "african-female-young", "t": "african-female-young"}, {"name": "african-female-old", "t": "african-female-old"}, {"name": "african-male-baby", "t": "african-male-baby"}, {"name": "african-male-child", "t": "african-male-child"}, {"name": "african-male-young", "t": "african-male-young"}, {"name": "african-male-old", "t": "african-male-old"}, ... ]

Combinations: 3 phenotypes × 2 genders × 4 ages = 24 targets

Complex 4-Component Macro, Gender × Age × Muscle × Weight (72 targets per gender)

{ "name": "gender-age-muscle-weight", "comp": ["gender", "age", "muscle", "weight"], "folder": "macro_muscle", "targets": [ {"name": "female-baby-averagemuscle-averageweight", "t": null}, {"name": "female-baby-averagemuscle-maxweight", "t": "female-baby-averagemuscle-maxweight"}, {"name": "female-baby-averagemuscle-minweight", "t": "female-baby-averagemuscle-minweight"}, {"name": "female-baby-maxmuscle-averageweight", "t": "female-baby-maxmuscle-averageweight"}, ... ] } Combinations:2 genders × 4 ages × 3 muscles × 3 weights = 72 targets

Note: Some targets may be `null` (base state) or reused across combinations.

Highly Complex 6-Component Macro, Gender × Age × Muscle × Weight × Cup × Firmness (Female only, 324 targets)

{ "name": "gender-age-muscle-weight-cup-firmness", "comp": ["gender", "age", "muscle", "weight", "cupsize", "firmness"], "folder": "macro_breast", "targets": [ {"name": "female-young-averagemuscle-averageweight-averagecup-averagefirmness", "t": null}, {"name": "female-young-averagemuscle-averageweight-averagecup-maxfirmness", "t": "young-averagemuscle-averageweight-averagecup-maxfirmness"}, {"name": "female-young-averagemuscle-averageweight-averagecup-minfirmness", "t": "young-averagemuscle-averageweight-averagecup-minfirmness"}, ... ] } Combinations: 1 gender × 4 ages × 3 muscles × 3 weights × 3 cups × 3 firmnesses = 324 targets (female only)

Target File Naming Strategy

Systematic Naming: Target files follow the exact naming pattern of component values: {folder}/{value1}-{value2}-{value3}-{...}.target Example: macro_muscle/female-young-maxmuscle-minweight.target Reuse and Aliasing: Some combinations can share the same target file to reduce duplication: {"name": "female-baby-minmuscle-averageweight", "t": "universal-baby-minmuscle-averageweight"}, {"name": "male-baby-minmuscle-averageweight", "t": "universal-baby-minmuscle-averageweight"} Both map to the same file because baby muscle/weight isn't gender-specific. Abbreviated Naming: Target filenames may use abbreviated forms to reduce redundancy: {"name": "female-young-averagemuscle-averageweight-averagecup-maxfirmness", "t": "young-averagemuscle-averageweight-averagecup-maxfirmness"} Gender prefix omitted when folder is female-specific.

Null Targets: Base state combinations may have null targets (no morph needed):

{"name": "female-young-averagemuscle-averageweight", "t": null} This represents the default state - no morphing applied.

Integration with other files

Base Configuration Start values of targets may be overridden by modifier-presets in base.json See: base.json for base configuration