Point density pcfind

Vector Expressions

int max = chi("maxpts");
float rad = chf("rad");
int handle[] = pcfind(0,"P",@P,rad,max);
float density = fit(len(handle),0,max,0,1);
@Cd=(chi("viz"))?chramp("density",density,0):v@Cd;
@density=density;
Create basic point density mask with pcfind lookup

Vex Custom Functions

Vector Expressions

//random example function
float someVexFunction(vector pos; vector pos2; float distance)
{
    vector dist=distance(pos,pos2);
    if(distance<dist) return 0;
    else 
        return dist*dist;
}

float value = someVexFunction(v@P,point(1,"P",0),0.5);
You can make custom functions inside vex and if saved to file on disk, can be referenced in multiple wranglers with #include "$HIP/vex/custom_function.h"

VDB Relative Voxel Size

Vector Expressions

max(max(bbox(0, D_XSIZE),bbox(0, D_YSIZE)),bbox(0, D_ZSIZE))*(1/100)
  • Since VdbFromPolygons only have size parameter, in order to make it relative voxel count to scale of object, put this inside size parameter.
  • Control resolution with 1/100. add parameter instead of 1.
  • It takes biggest side of bbox and match voxel size to it.

VOP Export Group!

Vector Expressions

bind export:
group_somename
To export group from VOP since there's no setpointgroup anymore, you just need to bind export in with group_ appended. Its integer boolean value: 0 or 1.

Point density pcopen

Vector Expressions

int maxpt = 10;
float rad = 0.1;
int handle = pcopen(0,"P",@P,rad,maxpt);
f@density=fit(pcnumfound(handle),1,maxpt,0,1);
Create basic point density mask with pcopen lookup.

RK4 advect

Vector Expressions

vector vel =volumesamplev(1,"vel",@P);
vector vel2 =volumesamplev(1,"vel",@P+ vel*(@TimeInc*0.5));
vector vel3 =volumesamplev(1,"vel",@P+ vel2*(@TimeInc*.5));
vector vel4 =volumesamplev(1,"vel",@P+ vel3*(@TimeInc));
v@P += (vel + 2*vel2 + 2*vel3 + vel4) * (@TimeInc/6);
Advect point with velocity field using RK4 method

RK2/midpoint advect

Vector Expressions

vector vel =volumesamplev(1,"vel",@P);
vector vel2 =volumesamplev(1,"vel",@P + (vel*(@TimeInc*0.5)));
v@P += vel2 * (@TimeInc);
//POP Wrangle will hand substeps with just one volumesamplev
Advect point with velocity field RK2 method

Collapse Nonzero

Vector Expressions

int x = !!x;
or
int x = !!@class;
Collapse any nonzero value to 1.

Timeshift inside Looper

Vector Expressions

int matching = match(s@name,prim(1,"name",0);
(!matching)?removeprim(0,@primnum,1):0;
or
(i@class!=prim(1,"class",0))?removeprim(0,@primnum,1):0;
Allows animated or controlled timeshift per iteration in a loop by filtering geometry input.
  1. Create wrangler node and input geometry from looper to 2nd input,
  2. Then geometry outside of looper in 1st input.
  3. Match name, If name match as a piece inside looper(2nd input),
  4. Delete everything else.
Then you can set timeshift and offset geometry.

Vex Noises

Vector Expressions

vector freq = {1,1,1};

vector offset = {0,0,0};

float amp = 0;

int turb = 5;

float rough = 0.5;

float atten = 1;

onoise(@P*freq - offset, turb, rough, atten) * amp

snoise(@P*freq - offset, turb, rough, atten) * amp

anoise(@P*freq - offset, turb, rough, atten) * amp

vop_correctperlinNoiseVF(@P*freq - offset, turb, rough, atten) * amp

vop_correctperlinNoiseVV(@P*freq - offset, turb, rough, atten) * amp

vop_simplexNoiseVF(@P*freq - offset, turb, rough, atten) * amp

vop_simplexNoiseVV(@P*freq - offset, turb, rough, atten) * amp

vop_perlinNoiseVF(@P*freq - offset, turb, rough, atten) * amp

vop_perlinNoiseVV(@P*freq - offset, turb, rough, atten) * amp
Showcases a variety of noise functions for procedural effects in VEX. referenced from Timucin Ozger

Split Name

Vector Expressions

s@name=split(s@objname,"/")[-1];
Extracts the final segment of objname, helpful for isolating names in nested paths.

Delete Random

Vector Expressions

float treshold = pow(chf("treshold"),chf("pow"));
(rand(@ptnum)<treshold)?removepoint(0,@ptnum):0;
Randomly removes points based on a probability threshold—useful for scattering.

Intersections

Vector Expressions

v@P.y+=10; //offset ray origin
vector pos;
float u,v;
intersect(1,@P,{0,-100,0},pos,u,v); // intersect in negative Y
v@P=(pos!=v@P)?pos:v@P; // keep only hits
Projects points downwards and adjusts positions when intersections are found.

Normalize Pieces

Vector Expressions

vector size = getbbox_size(0);
vector center = getbbox_center(0);

vector pos=v@P-center;
vector scale = 2/max(size);

matrix m=maketransform(0,0,0,0,scale);
@P=pos*m;
Centers and scales geometry per piece to normalize size—great for modular assets. Put it in foreach looper to iterate over each piece.

Pscale from ID

Vector Expressions

@pscale = fit01( chramp( "r" , rand( i@id ) , 0) , chf( "min" ) , chf( "max" ) ) * chf( "global" );
Assigns pscale based on hashed randomness tied to point ID, ensuring consistency.

Uniqueval Loop

Vector Expressions

int count = nuniqueval(0,"point","name");
for (int i = 0; i < count; i++)
{
string val = uniqueval(0,"point","name",i);
}
Iterates through unique values of a point attribute—ideal for per-group operations.

16bit VDB's

Vector Expressions

setprimintrinsic(0, 'vdb_is_saved_as_half_float', @primnum, chi('use16bit'));

// See if this is vector type
string vecmask = chs('vecvolume');
if (match(vecmask, @name))
{
    setprimintrinsic(0, 'vdb_vector_type', @primnum, 'contravariant relative');
}
Configures VDBs to use half-precision and correct vector orientation to save memory.

Global Variables

Vector Expressions

f@Frame
f@Time
i@SimFrame
f@SimTime
f@TimeInc

v@P
i@ptnum
i@vtxnum
i@primnum
i@elemnum
i@numpt
i@numvtx
i@numprim
i@numelem

//Volume Wrangle
f@density
v@center
v@dPdx, v@dPdy, v@dPdz  //Store the change in P that occurs in the x, y, and z voxel indices.
i@ix, i@iy, i@iz        //Voxel indices. For dense volumes (non-VDB) these range from 0 to resolution-1.
i@resx, i@resy, i@resz
Shows built-in VEX variables available in various contexts like SOPs or volumes.

Data Types

Vector Expressions

f@    float
u@    vector2
v@    vector3
p@    vector4
i@    int
2@    matrix
3@    matrix3
4@    matrix
s@    string
Lists common attribute types in VEX, showing syntax for attribute declarations.

Relax Based on Pscale

Vector Expressions

int handle[] = pcfind_radius(0,"P","pscale",@pscale,@P,@pscale*1.5,2);
pop(handle,0); //remove itself from array. pop() or removeindex()

vector pos,dir;
float pscale,dist,dirlen;
foreach(int pt; handle)
  {
    pos=point(0,"P",pt);
    pscale=point(0,"pscale",pt);
    dirlen=length(v@P-pos);
    dist=(pscale+@pscale-dirlen)/2; //get distance
    dir=normalize(v@P-pos)*(dist);
    v@P+=dir;
  }
Pushes points apart proportionally to their pscale, ensuring they don’t overlap visually.

Short IF statement

Vector Expressions

@value = (a > b) ? 1 : 0;
@value = (a > b) ? (a < c) ? 2 : 1 : 0 ;
Compact conditional logic using the ternary operator; useful for readability in inline expressions.

Loop over array

Vector Expressions

float arrayName [];
float value[];
int countarray= len ( arrayName);
for(int i = 0; i<countarray; ++i)
{
 value [i] = ch("parm") == 0 ? ch("parm2"): 1 ;
}
Iterates over an array and assigns values based on a condition using a ternary operator.

While loop

Vector Expressions

int count;
while(conditionis !=1)
{
 conditionis=rind(a+b);
 count+=1;
 if(count>10)
  break;
}
A basic while loop to perform iterative operations, with count to prevent infinite looping.

Data Type conversion

Vector Expressions

// interger >> string
int number = 123;
string text = itoa(number);

// string >> integer
string text = '123';
int number = atoi(text);
conversion of data types

Data Types

Vector Expressions

*** Integers ***
int myInteger = 1;
i@myInteger = 1;

// Floats
float myFloat = 4.14;
f@myFloat = 3.14;

// Strings
string myStiring = 'C:/cache/animation.abc';
s@myString = 'C:/cache/animation.abc';

// Arrays
string variations[] = {'A','B','C'};
string variables[] = array(variable_A, variable_B, variable_C);
s[]@variations = {'A','B','C'};
this is standard vex data types

MPV Player in houdini tabs

Python

from PySide2 import QtWidgets, QtCore
import subprocess
import os, hou

class MPVPanel(QtWidgets.QWidget):
    def browse_file(self):
        path = hou.ui.selectFile(
            title="Select Video File",
            file_type=hou.fileType.Any,
            pattern=".mp4 .mov .mkv .avi *.webm",
            collapse_sequences=False
        )
        if path:
            full_path = hou.expandString(path)
            self.url_input.setText(full_path)
    def init(self, parent=None):
        super().init(parent)
        self.setLayout(QtWidgets.QVBoxLayout())
        url_layout = QtWidgets.QHBoxLayout()
        self.url_input = QtWidgets.QLineEdit(self)
        self.url_input.setPlaceholderText("Paste YouTube link or browse for video file...")
        url_layout.addWidget(self.url_input)
        self.browse_button = QtWidgets.QPushButton("Files", self)
        self.browse_button.setFixedWidth(55)
        self.browse_button.clicked.connect(self.browse_file)
        url_layout.addWidget(self.browse_button)

    self.layout().addLayout(url_layout)
    buttons_layout = QtWidgets.QHBoxLayout()

    self.res_dropdown = QtWidgets.QComboBox(self)
    self.res_dropdown.addItems(["auto", "1080", "720", "480", "360"])
    self.res_dropdown.setCurrentText("480")
    self.res_dropdown.setFixedWidth(80)
    buttons_layout.addWidget(self.res_dropdown)
    buttons_layout.addStretch()

    self.play_button = QtWidgets.QPushButton("Play Video", self)
    self.play_button.setMinimumWidth(80)
    buttons_layout.addWidget(self.play_button)
    self.play_button.clicked.connect(self.on_play_clicked)
    buttons_layout.addStretch()
    self.layout().addLayout(buttons_layout)

    self.stop_button = QtWidgets.QPushButton("X", self)
    self.stop_button.setFixedWidth(12)
    self.stop_button.setFixedHeight(12)
    self.stop_button.clicked.connect(self.on_close_clicked)
    close_lay = QtWidgets.QHBoxLayout()
    close_lay.addStretch()
    close_lay.addWidget(self.stop_button)
    self.layout().addLayout(close_lay)
    self.stop_button.hide()
    self.video_container = QtWidgets.QWidget(self)
    self.layout().addWidget(self.video_container)
    self.video_container.setAttribute(QtCore.Qt.WA_NativeWindow)
    self.video_container.show()
    self.label = QtWidgets.QLabel("", self.video_container)
    self.label.setAlignment(QtCore.Qt.AlignCenter)
    self.label.setStyleSheet("color: white; font-size: 24px; background-color: black;")
    self.label.hide()

    self.mpv_process = None

def on_play_clicked(self):
    url = self.url_input.text().strip()
    if not url:
        self.label.setText("Please enter a video URL")
        self.label.show()
        return

    self.res_dropdown.hide()
    self.url_input.hide()
    self.browse_button.hide()
    self.play_button.hide()
    self.stop_button.show()

    res = self.res_dropdown.currentText()
    if res == "auto":
        ytdl_format = "bestvideo+bestaudio/best"
    else:
        ytdl_format = f"bestvideo[height<={res}]+bestaudio/best"
    self.url = url
    self.label.setText("Buffering video...")
    self.label.show()
    self.launch_mpv(ytdl_format)

def on_close_clicked(self):
    if self.mpv_process:
        self.mpv_process.terminate()
        if not self.mpv_process.waitForFinished(3000):
            print("MPV did not exit, forcing kill")
            self.mpv_process.kill()
            self.mpv_process.waitForFinished(3000)
        self.mpv_process = None

def launch_mpv(self, ytdl_format):

    if hasattr(self, "mpv_process") and self.mpv_process:
        self.mpv_process.terminate()
        self.mpv_process.wait()

    wid = int(self.video_container.winId())
    print(f"Launching MPV into XID: {wid}")

    self.mpv_process = QtCore.QProcess(self)
    self.mpv_process.setProgram("mpv")
    self.mpv_process.setArguments([
        self.url,
        f"--wid={wid}",
        "--vo=gpu-next",
        "--hwdec=no",
        "--no-terminal",
        "--really-quiet",
        "--cache=yes",
        "--cache-secs=30",
        f"--ytdl-format={ytdl_format}"
    ])

    env = QtCore.QProcessEnvironment.systemEnvironment()
    host_python = "/usr/bin/python3"
    env.insert("PYTHONHOME", os.path.dirname(os.path.dirname(host_python)))
    env.insert("PYTHONPATH", "")
    self.mpv_process.setProcessEnvironment(env)
    self.mpv_process.finished.connect(self.on_mpv_finished)

def on_mpv_finished(self, exitCode, exitStatus):
    print(f"MPV exited with code {exitCode}, status {exitStatus}")
    self.mpv_process = None

    self.res_dropdown.show()
    self.url_input.show()
    self.browse_button.show()
    self.play_button.show()
    self.stop_button.hide()
    self.label.hide()


def onCreateInterface():
    return MPVPanel()
Its a script that help you import mpv player using libmpv inside houdini tabs. Can take local files or youtube links. Good for references or flipbook exports to watch and loop while working.

Loop in parameter

HScript

{
    if(ch("path")==0)
        return 2;
    else if(ch("path")==1)
        return 1;
    else
        return 0;
    }
Uses expression logic to drive parameter values based on a choice, useful for variation or state control.

Gas Reduce info

HScript

dopfield(stamps("../..", "DOPNET", "../.."),stamps("../..", "OBJID", arg(dopnodeobjs("../.."),0)), "temperature", "Options", 0, "max")
GAS REDUCE node applies operations (e.g. max, avg) on a field and saves the result (e.g. temperature/max) for use in parameters or code. In Dest Data put temperature/max , in Source put temperature , set operation. It will push max data inside field. You can read those data on parameter with this code

Camera Scale Fix

HScript

origin(opinputpath(".",0), "", "SX")
origin(opinputpath(".",0), "", "SY")
origin(opinputpath(".",0), "", "SZ")
Locks object scale in camera space—ideal for imported render camera from Maya

Resize Only Current Cluster

Gas Microsolvers

  1. To set resize for each cluster in pyro sim, you need to break in dynamic resize node and inside foreach in wrangler pull dopfield data of cluster number:
dopfield(stamps("../..", "DOPNET", "../.."),stamps("../..", "OBJID", arg(dopnodeobjs("../.."),0)), "init", "Options", 0, "init_cluster")
  1. Then beneath objectmerge (sop source data), generate id from name. atoi(split(@name,"_")[1]) assuming name look like cluster_0, cluster_1, etc..
  2. Then you can compare and remove (id!=clusterid)?removeprim(0,@primnum,1):0;
Targets pyro resize operations to the active cluster only, optimizing simulation focus and performance. Check Trail cluster data node on Assets page, you can see this method implemented in explanation video

Repeat Solver

Gas Microsolvers

  1. In dopsolver beneath substep gas node create repeatsolver and turn on override minimum with data
  2. Create applydata node beneath smokeobject node and plug switch value node in it.
  3. In switch value node, set dataname field to match name with minimum Solve pass data field from gasrepeat
  4. Set swich node default operation to Set always and you can animate parameter for repeats.. ( many explosion have quick expansion on begining and then slows down. this trick can achieve that look!)
Enables dynamic control of substep behavior to simulate rapid then decelerating effects (e.g., explosions).