[Closed] Lights follow particles in viewport – but not during rendering?!?
Hi,
search doesn’t work at the moment what is the reason for that?
When I post a search the browser just shows a blank white screen – think the problem isn’t local…
anyway – here is the question:
I’ve written a Script Operator script which places lights at the particle positions and keep following the animation of the particles. Everything works fine in the viewport. And when I render the current frame it does what it should do: Lights are flying around in the scene.
But when I want to render a sequence the lights don’t follow the particles anymore.
It keeps rendering the light state of the first frame the whole time. Particles itself are moving fine – just the lights don’t follow anymore.
I tried to use “Every Step Update” – but didn’t work either…
Must be quite a common issue I think. Should I post the script or is there a quick answer?
Thanks a lot in advance
Uli
Thanks for having a look at it!
here is the code:
on ChannelsUsed pCont do
(
pCont.usePosition = true
pcont.useTime = true
)
on Init pCont do
(
--print "----------- init!!! -------------"
global lightArray
lightArray = #()
-- this function deletes any light in the array "lightArray"
global delLightArray
fn delLightArray =
(
t = 0
for o in lightArray do
(
if o != undefined and ((o as string) != "<Deleted scene node>") then
(
delete o
t += 1
--format "deleted: %
" t
)
)
lightArray = #()
)
callbacks.removeScripts id:#particlelights
callbacks.addScript #filePreSave "delLightArray()" id:#particlelights persistent:true
callbacks.addScript #filePreOpenProcess "delLightArray()" id:#particlelights persistent:true
delLightArray() --delete all lights which might have been set up earlier
global lightTemplate = $Omni_Template -- obviously there must be a light named "Omni_Template" in the scene
global lastNumParticles = 0
global count = 0
global addCount = 1
-- this function creates instances of the template light in the scene and positions them at the particle positions
global addLightsToParticles
fn addLightsToParticles lpCont =
(
count = lpCont.NumParticles()
if (count > lastNumParticles) then
(
addCount = count - lastNumParticles
for i = 1 to addCount do append lightArray (instance lightTemplate)
lastNumParticles = count
)
-- positioning of lights
for i in 1 to lightArray.count do
(
lpCont.particleIndex = i
lightArray[i].Position = lpCont.particlePosition
)
)
)
on Proceed pCont do
(
addLightsToParticles pCont
)
on Release pCont do
(
delLightArray()
)
To make it easier and to be able to test it straight away I’ve attached the max 2009 file.
I have no idea if this is a good way to do it. Any advice about the code is much apreciated.
It took me quite a while to figure out all these callback events to make the whole thing stable while fiddling around with the birth time/amount and with loading and saving the scene and things…
Anyway – it’s not worth anything when it doesn’t render properly.
Thanks
Uli
(btw: I used the plugin greeble – but it has absolutely nothing to do with the rendering – just ignore it)
Max 2009 file does not make it easier for me since I don’t have it installed. Still using 2008 at home and in the office.
Anyway, here is what I can tell you about how PFlow works and why it probably doesn’t work for you:
The on Init() handler is responsible for preparing any values, variables and for any object creation. While you are defining a function that is supposed to perform object creation within that handler, you are illegally trying to call it from the on Proceed() handler. There is a warning in the MAXScript Reference which explains that at render time, object creation and deletion is a big no-no. This is because all renderers enumerate the scene objects ONCE before the rendering starts and then assume that whatever was there on the first frame of rendering will still be around on later frames (because when Max 1.0 was designed, there was no scripting to mess things up).
You should be running your positioning code in the local scope of the on Proceed() handler and never try to create or delete lights from within that handler, only from the on Init() handler.
There is no need for callbacks. The on Init() and on Release() handlers will be called quite often (add some prints to see), and if you load a new scene and play back or render the PFlow, the on Init() handler will be called to create any lights you might need in the on Proceed() handler.
The one thing that you cannot know and will have to just live with is how many particles (and thus how many lights) you will need in the animation. Thus, you will have to assume a max. number of lights, pre-create them and then use them for the first N particles where N is the number of lights created. If you expect to move 100 lights, create as many and either don’t create more than 100 particles, or just don’t do anything for particles with index > 100. You could set the multiplier to 0 to turn them off and set it to 1.0 whenever you move them. In my case below, I just moved mine far away from the origin since they had a strong decay.
Here is some code taken straight from my PFlow DVD.
In this code, I assume that there will be 8 lights bouncing around and I only create these lights once if they don’t exist yet. I actually never delete them, but it would be possible to do so via the on Release() handler, I just didn’t see a need to remove them since the point of the scene is to have lights following the particles and constant adding and deleting of objects is a PITA and makes things slower.
There is also a bit of code moving the lights to the position of the particle where it will be after the integration step instead of where it is before that. Otherwise, the lights would always be one step behind:
on ChannelsUsed pCont do
(
pCont.useSpeed = true
pCont.usePosition = true
)
on Init pCont do
(
global theBounceLights = $BounceLight_* as array
if theBounceLights.count == 0 do
theBounceLights = for i = 1 to 8 collect
(omnilight attenDecay:2 name:(uniquename "BounceLight_"))
for o in theBounceLights do o.pos = [0,0,-100000]
)
on Proceed pCont do
(
theStep = ((pCont.getParticleSystem()).getIntegrationStep()).frame
count = pCont.NumParticles()
minCount = amin #(count,theBounceLights.count)
for i in 1 to minCount do
(
pCont.particleIndex = i
theBounceLights[i].pos = pCont.particlePosition + pCont.particleSpeed*TicksPerFrame*theStep
)
)
on Release pCont do
(
)
WOW – thank you so much for this in depth explanation and solution.
I can’t tell you how much I appreciate it.
To prevent myself from bothering you all the time, I decided to learn scripting a bit more properly than I used to do. You’ve got a rough idea of my scripting knowledge – which of your DVDs would you recommend me to buy? (don’t say “all of them”
thanks again
Uli
Tricky.
You do have the basic knowledge, so “MAXScript for the Masses” might be a bit below your abilities.
“The Matrix: Explained” would be useful if you intend to do lots of vector and matrix math but it is rather advanced.
The two PFlow DVDs are somewhere in between, although they assume you have some of the basic vector math knowledge from the Matrix DVD. If you intend to do mainly PFlow scripting, the two Acts are for you (they would have solved your particle lights problem directly).
You will have to make the call since only you know what your goals are. “The Matrix:Explained” is the more general one, “The PFlow Script Show” Acts are very specific and rather niche products.
If you can afford all 3, get them. If you can get only two, get the PFlow bundle. If you can afford only one, get The Matrix. If you cannot afford any, keep on posting here