[Closed] Baking a Psys to Animated Meshes
I have a particle system with fragments that I want to convert to meshes with keyframes.
I’m working from Bobo’s Snapshot Particles script, which snapshots the psys just fine.
The issue I’m running into is getting the snapshot-ed mesh’s transform to match to the particle’s transform. It seems like the ordering of the particles changes with every frame (so the snapshot meshes wildly rotate around and scale weird instead of just looking like their particle shape counter parts).
Anyone know why this doesn’t work as expected? Am I missing something?
Here is a link to a scene to test the code below on.
(
global bakerRL
try destroyDialog bakerRL catch()
rollout bakerRL "Psys Baker "
(
progressBar pbProgress1 "" pos:[5,5] width:198 height:30 value:0 color:[0,255,0]
progressBar pbProgress2 "" pos:[5,35] width:198 height:20 value:0 color:[255,0,0]
on bakerRL open do
(
if (selection.count == 1 and classof selection[1] == PF_Source) then
(
local pick_pflow = selection[1]
local holder_mesh = Editable_Mesh()
local theTotal = pick_pflow.numParticles()
with undo off
(
local newMeshArr = #()
newMeshArr[theTotal] = 0
--snapshot each particles mesh
i = 0
finish = false
while not finish do
(
i += 1
pick_pflow.particleIndex = i
current_mesh = pick_pflow.particleShape
if current_mesh == undefined then
finish = true
else
(
new_mesh = Editable_Mesh()
new_mesh.mesh = current_mesh
new_mesh.transform = pick_pflow.particleTM
newMeshArr[i] = new_mesh
local prog = 100 * (i) / theTotal
pbProgress1.value = prog
)
)
--now we can set the transforms for all the timeline keys
animButtonState = true
local myAnimStart = ((animationRange.start as integer)/TicksPerFrame)
local myAnimEnd = ((animationRange.end as integer)/TicksPerFrame)
for j = myAnimStart to myAnimEnd do
(
sliderTime = j
for g = 1 to theTotal by 1 do
(
pick_pflow.particleIndex = g
newMeshArr[g].transform = pick_pflow.particleTM
)
myPercent = 100 * j / myAnimEnd
pbProgress2.value = myPercent
)
animButtonState = false
)
)
)
)
createDialog bakerRL 210 60 100 100 style:#(#style_toolwindow, #style_sysmenu)
)
Anyone’s help is appreciated.
haven’t tried your example, but I know this script by Ofer Zelichover gives good results for baking pflows:
lo, thanks for the pointer to that script. i figured out what the issues were:
Any particle flow can contain any number of particles at any given frame, and the <PF_Source>.numParticles() method reflects this by returning the total number of particles ‘alive’ at the current frame. this number changes as operators add or subtract particles from the particle flow. the <PF_Source>.particleIndex changes as well, so you can’t rely on correlating a node to a particleIndex. Instead, you have to use <PF_Source>.particleID, which (I think) is a unique ID assigned at birth to a particle. In the code example below, I’m creating two arrays and storing references to nodes in one, and particleIDs in the other, so later on I can set the transforms of the snapshotted meshes.
The other issue is that particles can be born on arbitrary frames, so I’ve got to step through every frame checking/collecting unique particleIDs. If the particle flow spawns particles on a negative frame, or outside the timeline’s range, the script below ignores them, which is not elegant, and is difficult to check for (I think).
Here is a script that does what I want it to do on the archive I provided a link to.
It’s not pretty, and I need to refactor it to step through the timeline’s frames only once, but it will bake a particle system to meshes with keyframes.
( --select a particle flow to operate on, then run this script
(
source = ""
source += "using System;
"
source += "using System.Runtime.InteropServices;
"
source += "class WindowsGhosting
"
source += "{
"
source += " [DllImport(\"user32.dll\")]
"
source += " public static extern void DisableProcessWindowsGhosting();
"
source += "}
"
csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
compilerParams.GenerateInMemory = on
compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
assembly = compilerResults.CompiledAssembly
windowsGhosting = assembly.CreateInstance "WindowsGhosting"
windowsGhosting.DisableProcessWindowsGhosting()
)
global bakerRL
try destroyDialog bakerRL catch()
rollout bakerRL "Psys Baker "
(
label myLbl "Building meshes for all particles..." pos:[5,4] width:200
progressBar pbProgress1 "" pos:[5,20] width:198 height:20 value:0 color:[0,255,0]
progressBar pbProgress2 "" pos:[5,45] width:198 height:20 value:0 color:[255,0,0]
label myLbl2 "Baking animation to meshes..." pos:[5,70] width:200
progressBar pbProgress3 "" pos:[5,87] width:198 height:20 value:0 color:[0,255,0]
progressBar pbProgress4 "" pos:[5,107] width:198 height:20 value:0 color:[255,0,0]
on bakerRL open do
(
if (selection.count == 1 and classof selection[1] == PF_Source) then
(
local pick_pflow = selection[1]
local myAnimStart = ((animationRange.start as integer)/TicksPerFrame)
local myAnimEnd = ((animationRange.end as integer)/TicksPerFrame)
--local myAnimEnd = 10 --testing
with undo off
(
print "Welcome to Psys Baker v1."
local nodesArr = #()
local IDsArr = #()
local theLayer = layerManager.newLayerFromName ("BakedPsysMeshes")
fn checkID myID myIDsArr =
(
check = false
for i=1 to myIDsArr.count do
(if myID == myIDsArr[i] then check = true)
return check
)
--for every frame, check for new particle IDs, if found, add to array with reference to corresponding scene node
for j = myAnimStart to myAnimEnd do
(
sliderTime = j
i = 0
finish = false
while not finish do
(
i += 1
pick_pflow.particleIndex = i
current_mesh = pick_pflow.particleShape
if current_mesh == undefined then
finish = true
else
(
--is the particleID a unique ID (have we collected it already?)
local newID = pick_pflow.particleID
if (checkID newID IDsArr) then
( --if true, then skip this particle, we've already collected it
) else
( --if false, then collect this particle ID and create a mesh for it, store both in arrays to be used later on
new_mesh = Editable_Mesh()
new_mesh.mesh = current_mesh
new_mesh.transform = pick_pflow.particleTM
--put the new meshes onto a layer of their own
theLayer.addNode new_mesh
append nodesArr new_mesh
append IDsArr newID
--we can correlate node to ID using these 2 associated arrays
)
local prog = 100 * (i) / myAnimEnd
pbProgress1.value = prog
)
local prog = 100 * (j) / myAnimEnd
pbProgress2.value = prog
)
)
print "Collection complete."
local theCount = nodesArr.count
format "Total unique particles: %. Total meshes built: %.
" (IDsArr.count) (nodesArr.count)
if theCount != IDsArr.count then print "Associated array sizes do not match, errors will be present."
print "Baking animations to meshes..."
--now we can set the transforms for all the timeline keys
animButtonState = true
for j = myAnimStart to myAnimEnd do
(
sliderTime = j
for g = 1 to theCount by 1 do
(
try(pick_pflow.particleID = IDsArr[g]; nodesArr[g].transform = pick_pflow.particleTM)catch()
--this will return an index out of range error if the particle hasn't been born yet, so try()catch() it
local myPercent = 100 * g / theCount
pbProgress3.value = myPercent
)
local myPercent = 100 * j / myAnimEnd
pbProgress4.value = myPercent
)
animButtonState = false
print "Baking complete."
)
) else (messagebox "Please select one particle flow to bake.")
)
)
createDialog bakerRL 210 130 100 100 style:#(#style_toolwindow, #style_sysmenu)
)