Notifications
Clear all

[Closed] Addnewkeys problem

I have written a bit of code in order to record the movement of an object using the following which stores the position of an object every 1/30th sec or so in an array called ’ posobject’ :


theTimer = dotNetObject "System.Windows.Forms.Timer" 
posobject = #()
fn storepos = ( 
	   append posobject $.pos;
 
				   ) 
 
dotnet.addEventHandler theTimer "tick"  storepos 
theTimer.interval = 30
theTimer.start() 
 
 

I then stop the recording using theTimer.stop(), and play back using the following :


 
 slidertime = 0
 for t =  1 to 1000 do (
  slidertime = t;
 		pc = $.pos.controller;
		 pc.value  = posobject[t];
		$.pos = posobject[t];
  addnewkey pc t
 
					)

Which works fine as playback but for some reason all the keys on the object’s position controller are the same value, so I haven’t baked the animation – what am I doing wrong?
Thanks for your time – you too Denis.

14 Replies

I’m not sure what you mean, but do you use “animate on” ?

I mean that the keyed value remains at initial value for all keys. I ran some more tests and it seems that the problem is that although some new psotions are keyed, most aren’t because moving the object somehow interupts the timer, so it doesn’t tick over. Or it could be that the time taken to store the position in the array takes longer than 30 milliseconds. How can this be and what is the work around? Help! Bobo? Paul? ANyone?

there were several ways to capture free move.
i played with:
#1. timer solution:
a) mxs timer
b) forms.timer
c) timers.timer
#2: callback solution:
a) when transform construct
b) NodeEventCallback controllerOtherEvent

the best (most smooth capture) i’ve got with System.Windows.Forms.Timer

here is a snippet to play with:


  try(destroydialog free_move_dialog) catch()
  rollout free_move_dialog "Free Move Capture" width:200
  (
  	local source, target
  	struct capture_struct (time, pos)
  	
  	fn restore =
  	(
  		SC_RESTORE = 0xF120
  		WM_SYSCOMMAND = 0x112
  	  
  		windows.SendMessage (windows.getchildhwnd 0 free_move_dialog.title)[1] WM_SYSCOMMAND SC_RESTORE 0
  	)
  
  	group "Create And Capture: "
  	(
  		button create_bt "Create Sample" width:180 align:#left offset:[-3,0] tooltip:"Create Source-Target Sample/RC - Reset"
  		checkbutton source_bt "Source" width:89 align:#left offset:[-3,0] tooltip:"Start/Stop Capture Source/RC - Select Source" across:2
  		button target_bt "Target" width:89 align:#right offset:[3,0] tooltip:"Apply Captured to Target/RC - Select Target"
  	)
  	group "Reduce Keys: "
  	(
  		spinner threshold_sp "Threshold: " range:[0, 1, 0.2] type:#float scale:0.01 fieldwidth:50 align:#right offset:[3,0] 
  		button reduce_bt "Reduce Captured" width:180 align:#left offset:[-3,0] tooltip:"Apply Captured to Target and Reduce Keys"
  	)
  	local timer = dotnetobject "System.Windows.Forms.Timer"
  	
  	local tracker = #()
  
  	fn onTick s e = if not keyboard.escpressed and isvalidnode source then
  	(
  		append tracker (capture_struct time:(timeStamp()) pos:source.pos)
  	)
  	else 
  	(
  		s.Stop()
  		source_bt.state = off
  	)
  	
  	on create_bt pressed do undo "Create Free Move Sample" on
  	(
  		delete objects
  		centerPivot (source = box name:"Free_Source" wirecolor:green width:10 length:10 height:10 boxmode:on) 
  		centerPivot (target = box name:"Free_Target" wirecolor:yellow width:12 length:12 height:12 boxmode:on)
  		
  		source.pos = target.pos = [0,0,0]
  	)
  	on create_bt rightclick do undo "Reset Free Move Sample" on
  	(
  		if isvalidnode source do source.pos.track = createinstance Position_XYZ
  		if isvalidnode target do target.pos.track = createinstance Position_XYZ
  	)
  
  	on source_bt changed state do if isvalidnode source then
  	(
  		if state then
  		(
  			select source
  			max move
  			
  			tracker = #(capture_struct time:(timeStamp()) pos:source.pos)
  			timer.Start()
  		)
  		else timer.Stop()
  	)
  	else source_bt.state = off
  	
  	fn trackingToKeys track:tracker round:on threshold: = if track.count > 0 do
  	(
  		fn roundFrame t = (t + 0.5) as integer
  		
  		c = Position_XYZ()
  		s = track[1].time
  		animate on for k in track do 
  		(
  			t = (k.time-s)/1000.0*frameRate
  			if round do t = roundFrame t -- round to 1 frame
  			at time t c.value = k.pos
  		)
  		if threshold != unsupplied do reduceKeys c threshold 1f
  		c
  	)
  
  	on target_bt pressed do undo "Set Captured" on if isvalidnode target do
  	(
  		timer.Stop()
  		source_bt.state = off
  		target.pos.track = trackingToKeys()
  		select target 
  	)
  	on reduce_bt pressed do undo "Set Reduce Captured" on if isvalidnode target do
  	(
  		timer.Stop()
  		source_bt.state = off
  		target.pos.track = trackingToKeys round:off threshold:threshold_sp.value
  		restore()
  		select target 
  	)
  	on reduce_bt rightclick do undo "Set Raw Captured" on if isvalidnode target do
  	(
  		timer.Stop()
  		source_bt.state = off
  		target.pos.track = trackingToKeys round:off
  		select target 
  	)
  	
  	on source_bt rightclick do undo "Select Source" on if isvalidnode source do select source
  	on target_bt rightclick do undo "Select Target" on if isvalidnode target do select target
  	
  	on free_move_dialog open do
  	(
  		timer.Interval = 10 --(1000./framerate) -- every frame
  		dotnet.addEventHandler timer "Tick" onTick
  
  		source = getnodebyname "Free_Source"
  		target = getnodebyname "Free_Target"
  	)
  )
  createdialog free_move_dialog
  

it makes two objects: Source (green) and Target (yellow)
– to start the capturing use “Source” button
– to stop the capturing use ESC or “Source” button OFF or “Target” button
– apply the captured free move to the Target use “Target” button or “Reduce Captured” …

get details reading the code…

hope it helps.

Wow. Thanks Denis. If ever I’m in the big apple, I’l buy you a beer…or two. Will explore this code. I was begining to suspect that maxscript is too slow to use for motion capture.

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

this code definitely might be improved, but i hope it’s a good start.
the things to make it better:
#1: read captured data to memory stream to safe MAX memory
#2: average with weights positions that go to the same frame
#3: use mouse down/up events to start/stop capturing

Hmmm…it looks like your code runs into the same problem as mine – it’s only capturing quite slow movements. Whenever the source is moved around in anything but the most simple way, it caves in…it doesn’t seem to be able to replicate a circular movement at all…maybe we are asking too much of maxscript and we need a C++ version.

2 Replies
(@denist)
Joined: 11 months ago

Posts: 0

mxs has very good callback support. i don’t think that c++ can do it better. another way that i didn’t try is to use view redraw event…

how fast is your move? set timer interval to 5 ms, don’t round frame…
it must be high priority callback to do correct capture on transform change… max doesn’t have it.
do your moves slower than needed, and scale the animation after…

(@denist)
Joined: 11 months ago

Posts: 0

does it mean i will not get my beer?

hm… technically there is another way to capture… our problem is that we are in the same thread with max. we have to wait for all max messages (mouse move, transform change, viewport redraw, etc.) being received.
but we can catch only mouse move messages from other prioritized thread and recalculate the max object movement after.
try to do it yourself or call for any volunteer who wants to practice in low-level mouse hooks.

hm… technically there is another way to capture… our problem is that we are in the same thread with max. we have to wait for all max messages (mouse move, transform change, viewport redraw, etc.) being received.
but we can catch only mouse move messages from other prioritized thread and recalculate the max object movement after.

Yes I was thinking that was the problem. Where I need the most keys – during movement, there are hardly any, yet when it’s just sitting there I get one on every frame. The reverse of what should be happening. The movements I want to capture are quite rapid…facial animation requires it. I really want this to work as it’s a tremendous time saver when doing facial animation with many morph targets loaded onto the one control. It’s also great fun.
What about a per-frame lo res screen capture and then track the movements at leisure using standalone tracking s/w?
I’d buy you a beer anyway denis – I like your style.

low res screen capture doesn’t help. max screen redrawing is in the same thread.
you can hide everything and move only a little dummy… it should be captured well, but it doesn’t make any sense. the goal as i got is to see the mesh/skin deformation to make a right decision about a good move.

i’m thinking… maybe you overcomplicated the problem. i was many years in facial animation and i didn’t need innovations. are you doing anything cool that i couldn’t catch?

I don’t think even the dummy would be captured well. But Yes, you’re right…need immediate visual feedback of course. I was thinking screen capture using third party s/w. Camtasia or something like that I suppose. I’d Like a more integrated elegant solution however. Willing to entertain any possiblity for this.
Yes, I think I’m doing cool stuff…working on a new approach to facial animation that is intuitive, fast and fun, minimizing hand keyframing which is a drag. Not over-complicating but simplifying. I’m quite lazy and always looking for short cuts. I have finished much of the maxscript structure of it…pretty basic (like my maxscript skills) but works well, but this last step requires something outside of max to work unfortunately.

Page 1 / 2