Mini-Challenge #5. Are you ready?


#21

Hm, I wonder how we’ll look for correctness with such an unpredictable amount of animated nodes. I get 600 invariably but is that correct?

My function is not even optimized but the most interesting is, I think, after test it on different Max versions on my computer, I get so freakish and weird results:

Max 9 SP2 64: nodes:600 | time:8907 memory:4351776L
Max 2009 x32: nodes:600 | time:1922 memory:1520L
Max 2009 x64: nodes:600 | time:265 memory:105848L
Max 2011 x32: nodes:600 | time:688 memory:2712248L
Max 2011 x64: nodes:600 | time:516 memory:4877968L

So, as you see, its so easy to make wrong conclusions, and so difficult to reach meaningful conclusions.
Code optimization in Max is a (how to say…) pitfall (:


#22

the list of all animated controllers is more useful, but let’s talk now just about the list of animated nodes.


#23

that was my plan for tomorrow, but you showed it first.
at first glance everything looks right and complete… using the method is also easy to collect keyed controllers as well.
can we filter only material or ca sources?


#24

OK thanks for the clarification.

This is what I came up with last night. Looking at the code this morning it looks like the wrong way to go about doing it, but it executes quite quickly I guess. Surely the best method is one that picks up every node even if it is very slow, and once we know the complete total number of animated nodes we can then optimise. It’s interesting that everyone is getting different totals!

Results:
nodes:499
time:65 memory:94872L



(
	
	
	local animatedNodes = #()
	local found = false
	
	
	--Recursively work through object controllers to find any keys
	function findCtrlKeys obj index = (
		if obj.controller[index].keys.count > 0 then (
			append animatedNodes obj
			found = true
		)
		else (
			if obj.controller[index+1] != undefined and not found do findCtrlKeys obj (index+1)
		)
	)

	
	--Main function
	function findAnimatedNodes = (
	
		--Loop through all nodes
		for a in $* do (
			
			found = false
			
			--Find any keys in object controllers
			findCtrlKeys a 1
			
			--Find any keys in object base properties
			if not found do (
				propNames = getPropNames a
				for b in propNames while not found do (
					ctrl = (getPropertyController a.baseObject b)
					if ctrl != undefined do if ctrl.keys.count > 0 do (
						append animatedNodes a
						found = true
					)
				)
			)
			
			--Find any keys in object modifier properties
			if not found do (
				for b in a.modifiers do (
					propNames = getPropNames b
					for c in propNames while not found do (
						ctrl = (getPropertyController b c)
						if ctrl != undefined do if ctrl.keys.count > 0 do (
							append animatedNodes a
							found = true
						)
					)
				)
			)
			
		)
		
		--Loop through all materials
		--Add recursive function here
		
		return animatedNodes
		
	)
	
	
	--Run Function with timestamp
	t1 = timestamp()
	m1 = heapfree
	a = findAnimatedNodes()
	format "nodes:%
	time:% memory:%
" a.count (timestamp() - t1) (m1 - heapfree)
	
)


#25

heh… max, max, max… 2011 is slower than 2009… show you method please … it will be very interesting to see the bottleneck


#26

there is only one right number. it’s 753


#27

Seems you can do that by checking the superClassOf theGrandParent within the callback filter function. I haven’t verified that though.


#28

ok, nothing special in my function, as i said its not ever optimized (:

fn GetSubAnimsTree obj &res = (
	for i = 1 to obj.numSubs do (
		append res obj[i]
		GetSubAnimsTree obj[i] res
	)
)
fn findAnimatedNodes = 
(
	local nCount = objects.count
	local index = #{1..nCount}
	for i = 1 to nCount do
	(
		local subA = #(), numK = 0
		GetSubAnimsTree objects[i] &subA
		for a in subA where isController a.track while numK == 0 do
			if (v = a.track.keys.count) > 0 do numK += v
		index[i] = numK > 0
	)
	for id in index collect objects[id]
)

…but still confusing results (from version to version), right? (:


#29

no… in general case it will not work this way. the subanim depth might be deeper. probably we have to see dependents and filter them by superclass. but backward recursion will slow the method down dramatically.


#30

it seems like [i]they[/i] broke something in recursive function method… it will be interesting to see the difference in 2010 and 2012… i can do it tomorrow… actually it’s already today :slight_smile:

ps. don’t use [i] in code… it’s reserved for italic font.


#31

ok, will glad to see the difference (and why i not get 753 as well). :slight_smile:


#32

lo found them all :slight_smile:


#33
(
 	 local ts = timestamp()
 	 local mem = heapfree
 	 global animatedNodes = #()
 	 
 	 fn myCallbackFilterFunction theAnimatable theParent theSubAnimIndex theGrandParent theNode = 
 	 (	
 		 if isController theanimatable and theanimatable.keys.count>0 then
 		 (
 			 appendIfUnique animatedNodes theNode
 			 false
 		 )
 		 else true
 	 )
 	 
 	 with redraw off
 	 (	
 		 trackbar.filter = #all
 		 local filtind = maxops.trackbar.registerFilter myCallbackFilterFunction undefined "." 1 active:on
 		 select objects
 		 maxops.trackbar.redraw forceRedraw:on
 		 maxops.trackbar.unregisterfilter filtind
 		 clearSelection()
 		 format "Time: %ms
" (timestamp()-ts)
 		 format "Memory: %
" (mem-heapfree)
 		 format "Nodes Found: %
" ((animatedNodes).count)
 		 ok
 	 )
  )
 
 

^ This is great, I never would of realized it could of been done this way.
I have a question though, about the line:

local filtind = maxops.trackbar.registerFilter myCallbackFilterFunction undefined "." 1 active:on

Does the fn myCallbackFilterFunction want 5 parameters? I see you are passing 3. I don’t understand how that’s working… Are you passing ‘undefined’ for ‘theAnimatable’ and then “.” for theParent and then “1” for theSubAnimIndex? How do you pass theNode parameter? You guys write code that flies over my head sometimes… even after digging into the maxscript help where the fn came from. Great challenge!


#34

I am never calling the filter function directly in the code. 3dsmax calls the function for every animatable in the scene, and supplies all 5 parameters.

http://docs.autodesk.com/3DSMAX/14/ENU/MAXScript%20Help%202012/files/GUID-45E2F1A9-AFBB-413F-AEAD-7C9816BA94E-2065.htm


#35

Cheers for sharing Lo, very interesting method. I was way off :smiley:


#36

Sure, my pleasure.

Denis, I did not understand: is this the method you’re using as well or do you have a different trick up your sleeve to be shown later?


#37

lo,
i know that you see the shortcoming of your method. it’s the selecting. the performance of the method depends on background callbacks. the method needs more tests… e.g. with opened material editor or trackview…


#38

no… it was my plan B for today… i use subanim recursive check. i will show it as soon as i get the max.


#39

Yep, awesome work!

My question still remain - why I get just 600 nodes?

Well, my Explorer8 not show any forums formating issue with [i] inside

 block,
but just in case, i attach my code as text file.

#40

You’re right. Callbacks should be taken into account. How about this:

fn longSelectionCallback =
(
	-- do something long and annoying
	sleep 1
)

-- add evil selection callbacks for testing
callbacks.addScript #selectionSetChanged "longSelectionCallback()" id:#testCB
global whenCB = when select objects[1] changes id:#testCB do longSelectionCallback()

(	
	local ts = timestamp()
	local mem = heapfree
	global animatedNodes = #()
	
	fn myCallbackFilterFunction theAnimatable theParent theSubAnimIndex theGrandParent theNode = 
	(		
		if isController theanimatable and theanimatable.keys.count>0 then
		(
			appendIfUnique animatedNodes theNode
			false
		)
		else true
	)
	
	with redraw off
	(	
		trackbar.filter = #all
		local filtind = maxops.trackbar.registerFilter myCallbackFilterFunction undefined "." 1 active:on
		disableRefMsgs()
		local sel = getCurrentSelection()
		select objects
		maxops.trackbar.redraw forceRedraw:on
		maxops.trackbar.unregisterfilter filtind		
		select sel
		enableRefMsgs()
		format "Time: %ms
" (timestamp()-ts)
		format "Memory: %
" (mem-heapfree)
		format "Nodes Found: %
" ((animatedNodes).count)
		ok
	)
	
	callbacks.removeScripts id:#testCB
	deleteChangeHandler whenCB	
)

This way no callbacks get called. I checked it with trackview and material editor open, they made no difference.