[Closed] rendpickupframes to array
Hi. Is there an easy way to convert the rendpickupframes string (e.g “1,2,4-6”) to an array (#(1,2,4,5,6))? Maybe a standard function that can do this?
Hi David,
here is a function to do the job.
function rendFramesAsArray sRendFrames =
(
while ((findString sRendFrames " ") != undefined) do
sRendFrames = substituteString sRendFrames " " ""
local asFrames = filterString sRendFrames ","
local aiFrames = #()
for sFrame in asFrames do
(
if ((findString sFrame "-") == undefined) then
(
append aiFrames (sFrame as Integer)
)
else
(
local asFrameSpan = filterString sFrame "-"
local iStartFrame = asFrameSpan[1] as Integer
local iEndFrame = asFrameSpan[2] as Integer
for i = iStartFrame to iEndFrame do
append aiFrames i
)
)
return aiFrames
)
format "frames: %
" (rendFramesAsArray "1,3,5-12")
-- output
-- frames: #(1, 3, 5, 6, 7, 8, 9, 10, 11, 12)
- Enrico
Oh wow. Thank you very much. This is exactly what i needed. Did you just write this, or did you have it lying around? It is really very nice.
Hi, I’ve just written it, honestly I didn’t know about the rendpickupframes, and had to look for it in the reference. Lately I’ve been working a lot with strings, so this came out plainly.
- Enrico
haha. You’re the man Enrico .
Uh and I will have to check out some of the scripts on your website. They look very useful. Especially the facial rigging toolset.
David
Here is a shorter version:
fn rendPickupAsArray =
(
theStr="#{"+rendpickupframes +"}"
for i = theStr.count to 1 by -1 where theStr[i] == "-" do
theStr=replace theStr i 1 ".."
(execute theStr) as array
)
Unfortunately, neither of these versions deals correctly with negative frames.
It is possible to do in the first script but would slightly complicate the code, and it my solution it is not even possible because bitarrays can only store positive indices.
What is worse, mine doesn’t even handle 0 because a bitarray cannot contain a 0 index. I might try to fix that.
gee, that IS a shorter version. and a very different approach. What does a wavery bracket define? It’s not an array, but something closely related, i guess.
It is called a bitarray and is used to store large amounts of index data, like vertex and face selections. Instead of using multiple bytes to store a pointer to an arbitrary value like an array, it stores a single BIT inside a byte with tells you whether the index is used or not.
For example,
#{1,5,10} means that the first, fifth and tenth bits have to be set to true (thus using only two bytes).
The other problem my code has is that it does not even handle ZERO because an Index in MAXScript and in a bitarray is 1-based. So I will have to add some additional handling for negative AND zero values to make my function worthy.
The bitarray performs an automatic sorting and removal of existing elements too.
So #{10,1…5, 3} as array is expanded automatically to #(1,2,3,4,5,10), removing the duplicate 3 and moving the 10 where it belongs.
Note that you can loop through a bitarray and you will get indices because it is converted to array implicitly:
a = #{10,1..5, 3}
for i in a do print i
1
2
3
4
5
10
OK
but if you index into the bitarray, you get true and false:
for i = 1 to a.count do print a[i]
true
true
true
true
true
false
false
false
false
true
OK
Go read the Help
I tried to. But the search function refused to search for "#{ }". But Bitarray it did find :-D
Your explanation is a lot more usefull than the one in the helpfile, though. Bitarrays seem to be very versatile and convenient.
About the negative frames. I believe you don’t have to worry about them too much. The rendpickupframes doesn’t really accept negative values. Even max can’t handle them and throws a “this is not a valid frame number list”- error. Handling a zero however would be quite important.
David
using bitarray to convert rendpickupframes to array is the most elegant way
but it has some shortcomings – it doesn’t handle negative (which is not important) and ZERO frames, doesn’t keep the order of frames, and kills repeated frames…
here is my version:
fn rendpickupframesToArray rendframes:rendpickupframes =
(
arr = #()
for s in (filterstring rendframes ",") do
if (v = s as integer) != undefined then append arr v else
if (vv = filterstring s "-").count == 2 do for v = (vv[1] as integer) to (vv[2] as integer) do append arr v
arr
)
rendpickupframesToArray rendframes:"0,1,7,1,14,14,,9,3-12,0-10"
The ZERO frame problem is the only thing I see as an issue (as I mentioned earlier in the thread).
I cannot imagine why anyone would want repeated frames in the sequence though. I give you the order thing because I remember I had to force the Deadline team to add a flag to disable the reordering of frames in our own software – sometimes it is useful to be able to render out of order.
Usually rendpickupframes mode is used for preview purpose. There are several reasons to use repeated frames and different then straight frame order.
- Making a delay at the begging or at the end of movie clip
- Rendering forward and reverse animation in the same clip
…
It might be very helpful to preview character motions for example.
Is it better to render image per frame once and compose them in any sequence after that? Yes. If the rendering process takes a significant time. But if it doesn’t?
Negative frames… Is it not silly that Max allows to render negative frames in “Active Time Segment” and “Range” modes, and doesn’t do it in “Frames” mode? Why? Because many years ago some MAX developer couldn’t parse rendpickupframes string right.
Rendpickupframes could be used for those actions, but it really can’t.
e.g “20-5” will not work. And even if it would, the result would be exactly the same as rendering “5-20”, because it’s the frame numbers that count when playing imagesequences back. Therefore no reversing at all. And delaying your animation doesn’t work either for the same reason. “3,3,3” will render three times the same frame, but with the same name, implicitly overwriting the old file. You get only one frame.
If the rendpickupframes would parse more intelligently, instead of denying half of the strings and ignoring the other, it would be a really cool feature. If the script counted all the frames that should be rendered first, and then named them after their occurence that would be very handy. “12-5,4,3,3” should render a sequence of 15 frames, starting with frame 12 as frame_0000.jpg, and ending with frame_0013.jpg and frame_0014.jpg both beeing frame 3. But that’s not how it works
David
“7-5” is not working but “7,6,5” works.
you can use #postRenderFrame callback to save last render with any filename…
counter = 0
fn saveCurrentRender =
(
data = callbacks.notificationParam()
frame = data[13]
bmp = getLastRenderedImage()
file = GetDir #temp + "\\_lastRender_" + ((counter += 1) as string) + ".jpg"
bmp.filename = file
save bmp
close bmp
format "frame:% counter:% filename:%
" frame counter file
)
callbacks.removescripts id:#the_render
callbacks.addscript #preRender "counter = 0" id:#the_render
callbacks.addscript #postRenderFrame "saveCurrentRender()" id:#the_render