Python Texture Generator

The Python texture generator will call a python function to get the array of texels

Properties

Property name

Default value

Description

All common properties, see Texture Generators

PythonScriptPath

<empty>

The path to the python script, relative to the root of the project.

FunctionToCall

"main"

The python function, defined in the specified script.

Arguments

<empty>

A string which will be passed to the function.

Function Definition

Below is the expected signature for the generation function:

func(dimensions: tuple, format: int, args: str, logger: logging.Logger) -> tuple[<bufferable value>, tuple | None, int]

Let’s go over each argument & return value:

Arguments:

  • dimensions is a tuple of length 3 representing the requested dimensions, which you enter in the Dimensions field of the texture generator UI.

  • format is an int representing the request image format, also specified in the UI.You can query more information about the format using the pkfx module detailed in The pkfx Module.

  • args is the Arguments field specified in the UI, passed directly to the function.

  • logger is a logging.Logger. Anything logged to this logger will be shown in the Generation Report panel.

Return values:

Only the first value is required. Returning 1-3 values is valid as long as they are the proper type.
  • The first value is a bufferable value. Any value that exposes python’s buffer API will work here.
    This notably includes python’s own bytearray & Numpy arrays.

  • The second value is the returned dimensions. If this value is returned then the texture generator will check that the returned image’s dimensions matches these dimensions and abort if they do not.
    By default, the generator allows the returned image to have any dimensions.
    This value can be None if you wish to return a third value without enforcing dimensions.

  • The third value is the format of the returned image. This value is probably the result of pkfx.get_format_by_name. More information in The pkfx Module.

The pkfx Module

We provide a pkfx module to expose relevant helpers to the generation script. Below are all the exposed functions:

get_format_by_name(name: str) → int | None
Gets an image format by its name. Valid format names are shown when picking a Format in the generator UI.

get_pack_root() → str | None
Gets an absolute, normalized path to the root of the pack/project that contains this script.

Format info helpers:
format_is_fp(format: int) → bool | None
format_is_gamma_corrected(format: int) → bool | None
format_channel_count(format: int) → int | None
format_pixel_size(format: int) → int | None
format_name(format: int) → str | None

Managing The Python Environment

By default, only the standard library is distributed in the embed python the PopcornFX Editor uses.
However, the pip module is kept in the environment and can be used to install additional packages.
We suggest using the PYTHONNOUSERSITE=1 environment variable to ensure pip will install packages at the correct location. Here is an example command, run from a bash shell:

PYTHONNOUSERSITE=1 /c/Program\ Files/Persistant\ Studios/PopcornFX-2.23.0/bin/python/python.exe -m pip install numpy

Currently each editor version uses a different python environment, found in its installation location, under bin\python (e.g. C:\Program Files\Persistant Studios\PopcornFX-2.23.0\bin\python). This will most likely change in future versions.

You can also specify a python environment to use for a specific project. The setting can be found under Project Settings → Assets → Baking → Python Environment
You should specify a path under which you can find the Lib & DLLs folders of a python installation.

Using a custom python environment only allows you to change which standard library and other packages you use. Python itself will always be loaded from the bundled Python.

Example Script

This example script can be used as a starting point to understand the Python generator.
It depends on the popular numpy package, which isn’t distributed with the default python environment.

import random
import numpy as np
import pkfx

def main(dims, format, arg):
    x, y, z = dims
    r = random.randint(0, 255)
    bytes = np.ndarray((z, y, x, 3), dtype=np.uint8)
    for i in range(x):
        for j in range(y):
            for k in range(z):
                bytes[k][j][i][0] = (i / x) * 255
                bytes[k][j][i][1] = (j / y) * 255
                bytes[k][j][i][2] = r

    return bytes, None, pkfx.get_format_by_name('BGR8')

Note the dimensions the Numpy array is created with. The first three dimensions are reversed compared to the given dimensions.
We reverse the dimensions in the generator for compatibility with multiple image-loading libraries, which use a different convention for storing images.
The last dimension is also always used for the channel count for the same reason.

This script always returns an image of the requested dimensions, but ignores the request format and always returns BGR8.