_c4d_noise

Demonstrates working with noise functions.

_c4d_noise.py

"""Demonstrates working with noise functions.

Topics:
    - Simple noise functions (Noise, Turbulence, WavyTurbulence)
    - C4DNoise class and its usage

Related:
    io/_c4d_basebitmap.py
"""
__author__ = "Ferdinand Hoppe"
__copyright__ = "Copyright (C) 2026 MAXON Computer GmbH"
__license__ = "Apache-2.0 License"
__version__ = "2027.0.0"
__date__ = "07/09/2026"

import c4d
import mxutils


doc: c4d.documents.BaseDocument # The active Cinema 4D document.
op: c4d.BaseObject | None # The currently selected object in the Cinema 4D document, can be None.

def GenerateBitmap(data: list[float], width: int, height: int) -> c4d.bitmaps.BaseBitmap:
    """Generates a bitmap from an array of floats.
    """
    if width < 1 or height < 1:
        raise ValueError("Bitmap dimensions must be positive.")
    if len(data) != width * height:
        raise ValueError("Data size must match the bitmap dimensions.")

    bmp: c4d.bitmaps.BaseBitmap = c4d.bitmaps.BaseBitmap()
    if bmp.Init(width, height, c4d.COLORMODE_RGBw) != c4d.IMAGERESULT_OK:
        raise RuntimeError("Failed to initialize bitmap")

    rowBuffer: bytearray = bytearray(width * c4d.COLORBYTES_RGBf)
    byteView: memoryview = memoryview(rowBuffer)
    floatView: memoryview = byteView.cast("f")
    
    for y in range(height):
        for x in range(width):
            value: float = data[y * width + x]
            offset: int = x * 3
            floatView[offset] = value
            floatView[offset + 1] = value
            floatView[offset + 2] = value

        bmp.SetPixelCnt(0, y, width, byteView, c4d.COLORBYTES_RGBf, c4d.COLORMODE_RGBf, c4d.PIXELCNT_0)

    return bmp


def main() -> None:
    """Runs the example.
    """
    print("\n--- Simple Noises -----------------------------------------------------------------\n")
    # NOTE: Cinema uses positional arguments only. The named arguments in this example have been
    # added for clarity, but they are ignored by Cinema 4D. You must pass the arguments in the order
    # shown here.

    # The size of our noise array.
    sizeX: int = 256
    sizeY: int = 256
    scale: float = 0.01 # The 'zoom' factor for the noise functions. One unit is a relatively
                        # large distance in noise space, so if we would use raw pixel coordinates,
                        # we would not see much detail because we are zoomed out to far.

    # In c4d.utils.noise are some easy to use no-instantiation noise functions such as Noise, 
    # Turbulence, and WavyTurbulence.

    # Generate a 256x256 Noise array and display it in the Picture Viewer. p is the sample position
    # and t the sample time (for animated noise).
    data: list[float] = [
        c4d.utils.noise.Noise(p=c4d.Vector(x * scale, y * scale, 0), t=0)                        
        for y in range(sizeY)
        for x in range(sizeX)
    ]
    c4d.bitmaps.ShowBitmap(GenerateBitmap(data, sizeX, sizeY), "noise.Noise")

    # Generate a 256x256 Turbulence array and display it in the Picture Viewer. oct are the number 
    # of octaves for the turbulence.
    data = [
        c4d.utils.noise.Turbulence(p=c4d.Vector(x * scale, y * scale, 0), oct=4, abs=True, t=0)  
        for y in range(sizeY)
        for x in range(sizeX)
    ]
    c4d.bitmaps.ShowBitmap(GenerateBitmap(data, sizeX, sizeY), "noise.Turbulence")

    # Generate a 256x256 WavyTurbulence array and display it in the Picture Viewer.
    data = [
        c4d.utils.noise.WavyTurbulence(p=c4d.Vector(x * scale, y * scale, 0), t=0, oct=4, start=0.25)
        for y in range(sizeY)
        for x in range(sizeX)
    ]
    c4d.bitmaps.ShowBitmap(GenerateBitmap(data, sizeX, sizeY), "noise.WavyTurbulence")

    print("\n--- C4D Noises --------------------------------------------------------------------\n")

    # And then there is the instantiatable class c4d.utils.noise.C4DNoise, which provides access to
    # all iconic Cinema 4D noises.
    noise: c4d.utils.noise.C4DNoise = c4d.utils.noise.C4DNoise(seed=1234)
    noise.InitFbm(lMaxOctaves=8, rLacunarity=2, h=0.5)

    # Generate a 256x256 box noise array and display it in the Picture Viewer. t is the noise type, 
    # two_d indicates whether the noise is 2D or 3D, p is the sample position, time is here the
    # sample time, octaves is the number of octaves, absolute indicates whether to take the absolute
    # value of the noise, sampleRad is the sample radius, detailAtt is the detail attenuation, and
    # t_repeat is the temporal repeat. Every argument after p is optional.
    data = [
        noise.Noise(t=c4d.NOISE_BOX_NOISE, two_d=True, p=c4d.Vector(x * scale, y * scale, 0), 
                    time=0, octaves=4, absolute=False, sampleRad=0.25, detailAtt=0.25, t_repeat=0)
        for y in range(sizeY)
        for x in range(sizeX)
    ]
    c4d.bitmaps.ShowBitmap(GenerateBitmap(data, sizeX, sizeY), "C4DNoise - BoxNoise")

    # And the same for a ober noise, see the docs for all noise types.
    data = [
        noise.Noise(t=c4d.NOISE_OBER, two_d=True, p=c4d.Vector(x * scale, y * scale, 0), 
                    time=0, octaves=4, absolute=False, sampleRad=0.25, detailAtt=0.25, t_repeat=0)
        for y in range(sizeY)
        for x in range(sizeX)
    ]
    c4d.bitmaps.ShowBitmap(GenerateBitmap(data, sizeX, sizeY), "C4DNoise - Ober")


if __name__ == "__main__":
    c4d.ClearPythonConsole()
    main()