Contributing#

Installation#

Install uv if you don’t already have it: curl -LsSf https://astral.sh/uv/install.sh | sh

Then install the repo for development:

git clone https://github.com/princeton-vl/infinigen.git
cd infinigen
uv sync --extra dev

Formatting#

Run this once to enable the formatting hooks:

uv run pre-commit install
  • auto-code-formatting is mandatory and very important

  • pre-commit will format your files whenever you commit. if it detects any changes, it cancels the commit, and you have to git add and commit them again. This is quite annoying but is intended only as a backstop.

  • to avoid pre-commit firing too often, ive added a .vscode/settings.json which should auto-format your files upon save.

Procedural Generator Conventions#

All procedural generators#

When creating any procedural generator there will be two public functions, e.g:

  • bricks(vector, param1, param2 ...) (called the generate function)

  • bricks_rand(rng, vector, ...overrides) (called the rand function)

You can have other functions, but they wont be exposed to the documentation page, and should be “private” and ideally marked with an underscore like def _my_helper_function(). All functions which make assets (Object, Material, Texture, Collection/Scene) must be public and follow the generate/rand conventions.

Both functions can only use very simple function arguments types:

The _rand has restricted control flow rules:

  • Absolutely no for / while loops

  • No if statements, EXCEPT for the sole case of doing if argument is None to fill in default arguemnts

  • Almost all control flow should just use a pf.control.choice() with constant weights. If you strictly need if/for/while, either put them inside the generate function (not _rand), or we will have to add special functions to pf.control which are planned but WIP

Randomness can only come from rng input arguments, not np.random.uniform() or import random. Only the _rand function has an rng, not the regular function, which means the generate

Material functions#

Must have a vector: ProcNode[Vector]input. This determines the coordinate space for the material, which is plugged into all noise / bricks etc, or else they will crash.

Input variable conventions:

  • vector determines the coordinate space (not coord or similar).

    • units are in meters - you must have correct scale for your material.

    • the vector is 3D, but you can ignore z and use only x and y if you wish.

  • roughness for the main roughness parameter, if one exists. avoid using surface_roughness etc.

Output variable conventions:

  • surface output for any BSDF / non-volume shaders

  • volume for volume shader outputs

  • displacement for displacement vector outputs. Avoid using height as a float

See concrete.py for an example.

Tools#

Rendering#

See the landing page for the everyday render commands (quick render, GT render, many renders locally).

View frames as a video slideshow instead:

sudo apt-get install ffmpeg vlc libx264-dev # if needed
ffmpeg -y -r 1 -pattern_type glob -i "outputs/bricks/*/image*.png" -c:v libx264 -crf 18 -pix_fmt yuv420p out.mp4
vlc out.mp4

Generate samples from all materials: bash scripts/integration_v2/launch.sh outputs/new_renders <number parallel jobs>

View Material samples: uv run scripts/integration_v2/compare.py --scan-dir outputs

Integration-render staging lane#

Test changes to integration-render infrastructure on the integration-staging branch before merging them. A push to that branch writes a baseline only under integration/staging/; it cannot replace develop_latest or main_latest. Open a disposable PR targeting integration-staging to exercise the same changed-only gate, archive, coverage reduction, and viewer flow that a normal PR uses.

For an ad-hoc canary, dispatch Integration Render with validation=true. This also uses the staging archive and skips retention pruning. Verify the uploaded coverage-integration artifact contains asset_coverage.json, and that gating_report.json has the expected mode and selected generators. Use a small fixture change for each gate path: generator source (selected asset), workflow/integration script (full render), and unrelated documentation (zero selected assets).

Testing#

Run linting

uv run ruff format
uv run ruff check --fix

Run unit tests

uv run pytest

Transpiling assets to python#

See the procfunc transpiling example for a worked end-to-end example.

Transpile a material from a file

uv run python -m procfunc.transpiler.main input.blend --materials materialname1 materialname2 --output newfile.py

Transpile and infer parameter distributions for all subcomponents:

uv run python -m procfunc.transpiler.main input.blend --materials materialname1 materialname2 --output newfile.py --transforms colors_to_hsv_definition infer_nodegroup_distributions

example result: file will contain some extra _rand functions e.g. brick_concrete_rand shown below:

def brick_concrete_rand(
    rng: pf.RNG,
    coord: t.SocketOrVal[pf.Vector],
):
    # Code generated by procfunc v0.3.0

    color_hsv = pf.random.uniform(
        rng,
        np.array([0.0, 0.09677423, 0.093]),
        np.array([0.06349206, 0.57142854, 0.294]),
    )
    color = pf.color.hsv_to_rgba(color_hsv)
    roughness = pf.random.uniform(rng, 0.7, 0.9409)
    specular_ior_level = pf.random.uniform(rng, 0.0753, 0.1941)
    color_noise_1_spread = pf.random.uniform(rng, 0.8, 0.8)
    color_noise_2_sharpness = pf.random.uniform(rng, 0.25, 0.25)

    brick_concrete_result = brick_concrete(
        rng=rng,
        coord=coord,
        color=color,
        roughness=roughness,
        specular_ior_level=specular_ior_level,
        color_noise_1_spread=color_noise_1_spread,
        color_noise_2_sharpness=color_noise_2_sharpness,
    )
    return brick_concrete_result