Hi,

The data format is listed in the help section of the script. The actual packing of data is fairly simple although a little non intuitive. The bulk of the script relates to the ui and prep tools section.

This is the text defining the data layout in the help section of the script (ignore the \r markup ):

---------------------------------\r

Hierachy Painter Output\r

---------------------------------\r

Child data:\r

Vert Alpha: parent[X][X] (0-1) \r

Vert Color: parent position XYZ (0-1)\r

UV channel 3: child pivot position XY (WS)\r + child [X][X],[X][Y] (0-1)\r

UV channel 4 R: child pivot position Z (WS)\r + child [X]Z\r

UV channel 4 G: parent[X]Y * Z sign\r

Parent Information: \r

Vert Alpha: parent[X][X] (0-1)\r

Vert Color: parent position XYZ (0-1)\r

UV channel 3: 0\r

UV channel 4 R: 0\r

UV channel 4 G: parent[X][Y] (0-1) * Z sign\r

---------------------------------\r

Per Object Painter Output\r

---------------------------------\r

With Optimize Off:\r

Vert alpha : Custom Falloff\r

Vertex color : X Vector (rotation) (0-1)\r

UV channel 3: Pivot position XY (WS)\r

UV channel 4 R: Pivot position Z (WS)\r

UV channel 4 G: Random value (0-1)\r

With Optimize On:\r

Vert alpha : Empty\r

Vertex color : Empty\r

UV channel 3: Pivot position XY(WS)\r

UV channel 4 R: Pivot position Z (WS)\r

UV channel 4 G: Random value (0-1)

This is the code specifically used to paint **per object** values (I’ll point out the necessary parts after) :

```
fn paintLocalValues paintVectorOrRotation:1 optimized:false=(
try
with redraw off (
localMaxOperations=LeafArray.count
for i=1 to LeafArray.count do(
currMesh = LeafArray*
pivotPos=currMesh.pos*255
if optimized==true then (
polyop.setVertColor currMesh 3 #all [pivotPos[1],pivotPos[2],0]
polyop.setVertColor currMesh 4 #all [pivotPos[3],(random 0.0 255.0),0]
) else (
localBoundingBox=nodeGetBoundingBox currMesh currMesh.transform
localBoundingBoxDist= distance localBoundingBox[1] localBoundingBox[2]
if paintVectorOrRotation == 1 then (
myAngle=((((normalize currMesh.transform[1])*[1,-1,1])+1)/2)*255 -- Branch x axis vector --make the value 0-1 in unreal -- unreal inverts vert color y
) else (
myAngle= in coordsys world quatToEuler2 (inverse currMesh.rotation);
myAngle= [myAngle.x,myAngle.y,myAngle.z]
myAngle=(myAngle*(256.0/360.0))+128
)
polyop.setVertColor currMesh 0 #all myAngle
polyop.setVertColor currMesh 3 #all pivotPos
polyop.setVertColor currMesh 4 #all [pivotPos[3],(random 0.0 255.0),0]--leaf pivot (0-1), leaf z rotation
--Paint alpha per vertex
for v=1 to (getNumVerts currmesh) do (
if keyboard.escPressed do ResumeEditing()
currVert=polyop.getVert currMesh v
currVertBaseObj=polyop.getVert currMesh.baseobject v
gradBBX=[0,localBoundingBox[1][1],0]
gradBBXTwo=[0,localBoundingBox[2][1],0]
gradBBY=[0,localBoundingBox[1][2],0]
gradBBYTwo=[0,localBoundingBox[2][2],0]
gradBBZ=[0,localBoundingBox[1][3],0]
gradBBZTwo=[0,localBoundingBox[2][3],0]
finXScale = (distance gradBBX gradBBXTwo)
finYScale = (distance gradBBY gradBBYTwo)
finZScale = (distance gradBBZ gradBBZTwo)
finXVal= (pow ((distance [0,currVertBaseObj[1],0] gradBBX)/finXScale) leafWingFalloffPowerX ) + (pow((distance [0,currVertBaseObj[1],0] gradBBXTwo)/finXScale) leafWingFalloffPowerX)
finYVal= (pow ((distance [0,currVertBaseObj[2],0] gradBBY)/finYScale) leafWingFalloffPowerY ) + (pow((distance [0,currVertBaseObj[2],0] gradBBYTwo)/finYScale) leafWingFalloffPowerY)
finZVal= (pow ((distance [0,currVertBaseObj[3],0] gradBBZ)/finZScale) leafWingFalloffPowerZ ) + (pow((distance [0,currVertBaseObj[3],0] gradBBZTwo)/finZScale) leafWingFalloffPowerZ)
distanceToPivot=distance currVert currMesh.pos/255
finalAlpha= clamp (((pow (distanceToPivot*leafLengthFalloff)leafLengthFalloffPower) +(finYVal*leafWingFalloffY)+(finXVal*leafWingFalloffX)+(finZVal*leafWingFalloffZ))*255) 0 255
polyop.setVertColor currmesh -2 v [finalAlpha,finalAlpha,finalAlpha]
)
)
updateProgBar maxOperations:localMaxOperations currentOperation:i bar:2
)
) catch(displayAutoErrorMessage())
)
```

A lot of that code is for the custom alpha values. You probably just need:

```
**store the x axis**
myAngle=((((normalize currMesh.transform[1])*[1,-1,1])+1)/2)*255 -- Branch x axis vector --make the value 0-1 in unreal -- unreal inverts vert color y. Basically, we are just constant bias scaling the values to a 0-1 range and then multiplying it by 255 for 3DS's vertex colors scale.
polyop.setVertColor currMesh 0 #all myAngle -- feed the vector into the models vertex colors.
```

and

**store the pivot point**

```
pivotPos=currMesh.pos*255
polyop.setVertColor currMesh 3 #all pivotPos --- this stores the X and Y values of the pivot point in R and G of uv Channel 3 (uv channel 2 in unreal. Unreals uvs are 0 based and max is 1 based)
polyop.setVertColor currMesh 4 #all [pivotPos[3],(random 0.0 255.0),0] -- just stores a random per element value in G
```

When testing to see if your values are being packed and read correctly in Unreal you can use the debug scalar or debug float 3 material functions. That will make the conversion process less confusing.

Btw improvements could be made to the way that pivot painter data is packed. Instead of using several channels to pack a rotation vector it could be done with just 1 uv channel. x(in the 0-1 range) + y(in the 0-.1) and z (in the .00-.01 range) added together into a single scalar value.

The current method for packing leaf rotation vectors is very poor as well. It kind of borders on random due to floating point accuracy. But this is the data format for the hierarchy painter if anyone is interested (note that the uv count that the script was initially written for was very limited):

```
fn convertWPtoUV Pos:[0,0,0] = (
global temp=(Pos/shaderWSMultiplier)*[128,128,255]+[128,128,0]
global tempx= clamp (temp[1]) 0 255
global tempy= clamp (temp[2]) 0 255
global tempz= clamp (temp[3]) 0 255
global temp=[tempx,tempy,tempz]
)
fn paintLeaves = ( -- paints branches and leaves
try
with redraw off (
global branchLocalBoundingBox=nodeLocalBoundingBox currBranchObj
branchLocalBoundingBox = distance branchLocalBoundingBox[1] branchLocalBoundingBox[2]
branchAngle=(normalize currBranchObj.transform[1])*[1.0,-1.0,1.0] -- Branch x axis vector (float 3) -- invert y for vert color
if branchAngle[3]<0 then (zSign=-1.0) else (zSign=1.0)
branchAngle=((branchAngle+[1.0,1.0,1.0])/2.0)*255.0 -- Branch x axis vector (float 3)
branchAngle=[clamp branchAngle[1] 0 255,branchAngle[2],branchAngle[3]]
global uv4Green=(branchAngle[2]*zSign)
branchPos=convertWPtoUV pos:(currBranchObj.pos*[1,-1,1]) -- 0-1 range flip the y axis
polyop.setVertColor currBranchObj -2 #all [branchAngle[1],0,0] -- set vert color for branch to branch position
polyop.setVertColor currBranchObj 0 #all branchPos -- set vert color for branch to branch position
polyop.setVertColor currBranchObj 3 #all [0,0,0] -- can be used for pixel shading if needed
polyop.setVertColor currBranchObj 4 #all [0,uv4Green,0]
fnpaintleavesMaxOp=leafArray.count
for i = 1 to leafArray.count do (
currObj=leafArray*
pivotPos=currObj.pos*255
pivotPos=[ceil pivotPos[1],ceil pivotPos[2],ceil pivotPos[3]]
xAxis=(((normalize currObj.transform[1])*.5)+.5)*255 -- leaf's x - axis
xAxis=[clamp (xAxis[1]) 20 240, clamp (xAxis[2]) 20 240, clamp (xAxis[3]) 20 240] -- padding is added to avoid the lack of precision -- only works for near meshes
if pivotpos[1]>0 then (pivotpos[1]+=xAxis[1]) else (pivotpos[1]-=xAxis[1])
if pivotpos[2]>0 then (pivotpos[2]+=xAxis.y) else (pivotpos[2]-=xAxis.y)
if pivotpos[3]>0 then (pivotpos[3]+=xAxis.z) else (pivotpos[3]-=xAxis.z)
polyop.setVertColor currObj -2 #all [branchAngle[1],0,0]--branch - y axis
polyop.setVertColor currObj 0 #all branchPos--branch position
polyop.setVertColor currObj 3 #all pivotPos -- + x transform in the remainder
polyop.setVertColor currObj 4 #all [pivotPos[3],uv4Green,0]--leaf pivot (ws), random value or constant bias scaled X with Z sign. use absolute value then scale bias to find x. Derive z from x and y to find z. Apply z sign to z channel.
updateProgBar maxOperations:fnpaintleavesMaxOp currentOperation:i bar:3
)
) catch(displayAutoErrorMessage())
)
```

so basically you constant bias scale the branch x axis vector. the find the sign of z and apply that as the sign of one of the other channels (y). so if Z was negative then we would multiply the y channel by -1. (In the shader we derive z from x and y but the returned z value is always positive so we need to store and reapply the sign after we use derive z in the shader)

you can probably read through the rest but if there are any specific questions i would be happy to help.

by the way, **polyop.setVertColor currBranchObj -2** refers to vertex alpha. **polyop.setVertColor currObj 0 #all branchPos** is vertex color