VAT Bounding Box Metadata Packing

This table show how SideFX Labs VAT 3.0 / 4.0 pack pipeline configuration settings directly inside the mesh Bounding Box.
Engine material automatically read this parameters.
No manual copy-paste between Houdini and Unreal.

Axis / ChannelMode / ConditionMetadata PackedHoudini VEX (Encode)Unreal HLSL (Decode)Note
Bound Max XRigid Mode (mode == 1)Max RPF (Revolutions Per Frame)max_x += (1.0 - 1.0 / max_rpf) * 0.1 * scale1.0 / (1.0 - frac * 10)Sub-frame interpolation (motion blur/slowmo) taking shortcut when piece rotate >180 deg per frame.
Bound Max XFluid Mode (mode == 2)Lookup Table Depth Formatmax_x += code * 0.1 * scale (HDR = 0.7, LDR = 0.3)frac * 10 (if >= 0.5 is HDR, else LDR)Fluid points and topology change every frame. VAT cannot map one pixel to one fixed vertex like rigid. Fluid VAT generates extra lookup table texture to record which texture pixel the engine vertex ID should sample at frame X.
Bound Max YRigid, Sprite (mode == 1, 3) or custom active attributesMax Pscale (Max vertex scale)max_y += (1.0 - 1.0 / max_pscale) * 0.1 * scale1.0 / (1.0 - frac * 10)Restores 0-1 normalized custom attributes or pscale stored in position texture alpha back to real physical scale.
Bound Max ZAll ModesHDR flag & Split Pos flag & Row Count (Hundreds digit)code += 0.5 (if HDR), code += 0.25 (if double texture), then + hundred_row_count * 0.01 (skip logic)Decoded via complex steppingSee tables below.
Bound Min YAll ModesRow Count (Units & Tens digit)min_y -= (row_count % 100) * 0.01 * 0.1 * scalerint(100.0 * frac * 10)Stores units/tens digit of row count.
Bound Min ZPow of Two modeActive Pixels Ratio Xmin_z -= (1.0 - size_x / pow2_size_x) * 0.1 * scale1.0 - frac * 10
Bound Min XPow of Two modeActive Pixels Ratio Ymin_x -= (1.0 - size_y / pow2_size_y) * 0.1 * scale1.0 - frac * 10Same as Min Z but for Y axis.

(Note: frac is the pure decimal portion of the bounding box axis value extracted by the shader after removing the 10cm grid snap).

Bound Max Z Coding Detail

This channel pack 3 states in one decimal value.

1. Base Code Value (HDR & Split Position status)

Base Decimal ValueHDR status (normalize_data)Split Position status (split_pos)Description
0.00LDR (Normalized)Single Position Tex (1 Tex)Base code start at 0.0
0.25LDR (Normalized)Double Position Tex (2 Tex)Base code start at 0.25
0.50HDR (Unnormalized)Single Position Tex (1 Tex)Base code start at 0.50
0.75HDR (Unnormalized)Double Position Tex (2 Tex)Base code start at 0.75

2. Stepping & Skip Logic (Hundreds Row Count)

ROP add 0.01 to decimal for every 100 rows (so 500 rows is +0.05).
Stepping direction depends on base value:
If base is 0.00 or 0.50, it steps upwards (code += hundred_count * 0.01).
If base is 0.25 or 0.75, it steps downwards from the next quadrant (code += 0.25; code -= hundred_count * 0.01).

Also need to skip multiples of 0.1 (like 0.3, 0.4, 0.7, 0.8).
If hundreds digit > 9, offset gets extra 0.01. If > 18, add 0.02.
This is for backward compatibility. Old VAT version ROP hardcoded these numbers. Skipping prevents shader thinking old mesh assets have 30/40 hundred rows and exploding character to noodles.

Notes

The Bound Snapping

Before baking relative position (0 to 1) to texture, Houdini ROP snaps raw bounds to 10cm grid (using ceil/floor) first. This creates a slightly inflated bounds.
Then ROP use this inflated bounds as absolute baseline coordinates to calculate vertex 0-1 relative offset.
In engine, shader reads bounds (with decimals), strips metadata to get clean inflated bounds base. It then decode WorldPos:
WorldPos = TexValue * (BoundMax - BoundMin) + BoundMin.

Row Count: Why High & Low Part

Large asset can easily need up to 1500 rows. Float decimal cannot store large integer precisely.
Directly storing 1500 decimals reads as 1499.9998 in engine. You lose 1 row and whole vertex animation breaks.
So we split it to Low part (0-99) in Min Y, High part (hundreds 100-1900) in Max Z.