viewport.activeViewport problem in callback

Become a member of the CGSociety

Connect, Share, and Learn with our Large Growing CG Art Community. It's Free!

THREAD CLOSED
 
Thread Tools Search this Thread Display Modes
  06 June 2011
viewport.activeViewport problem in callback

I am trying to write a small set of functions that link all viewports to the same rotation.
It is working, except that whenever any other window gets focus, max hangs for several minutes. I have narrowed it down to the viewport.activeViewport being set inside the callback timer function. Here is the code, with the offending line marked in orange:

global linkViewports
struct linkViewportsStr
(
	callbackString = "linkViewports.startLinkTimer()",
	linkTimer,
	lastTM = matrix3 0,
	
	fn areMatricesIdentical a b = a[1]==b[1] and a[2]==b[2] and a[3]==b[3] and a[4]==b[4],
	
	fn setCallbacks state =
	(
		format "Settings callbacks to %\n" state
		if state then callbacks.addScript #viewportChange callbackString id:#linkViewports
		else callbacks.removeScripts id:#linkViewports
	),
	
	fn doSync =
	(
		format "Entered doSync fn\n"
		local masterView = viewport.activeViewport --check which viewport is active (the master)
		local masterTm = viewport.GetTM() --get current viewport transform (for rotation part)
		if not areMatricesIdentical lastTM masterTM do --if the new transform is different than the old transform
		(
			setCallbacks off --we must disable the callback temporarily, otherwise we will get stuck in an infinite loop!
			format "Syncing viewports\n"
			lastTM = masterTM
			with redraw off --disable redrawing
			(
				for v = 1 to viewport.numViews do if v!=masterView do --for each viewport except the master
				(
					if viewport.getType index:v != #view_persp_user do continue --ignore orthographic views and cameras
					format "Settings viewport no. %\n" v
					viewport.activeViewport = v
					local slaveTm = viewport.GetTM() --get the slave viewport transform (for position part)
					viewport.setTM (matrix3 masterTm.row1 masterTm.row2 masterTm.row3 slaveTM.row4) -- set the transform
				)
				viewport.activeViewport = masterView --return the view to the one we started from	
			)		
			setCallbacks on --reenable the callbacks
		)
	),
	
	fn linkTimerHandler sender args =
	(		
		if mouse.buttonStates.numberSet == 0 do --if we have stopped moving (no mouse buttons are pressed)
		(
			format "Stopping linkTimer\n"
			sender.stop() --stop the timer
			linkViewports.doSync() --perform the synchronization
		)
	),	
	
	fn startLinkTimer = 
	(
		if not linkViewports.linkTimer.enabled and mouse.buttonStates.numberSet == 0 do 
		(
			format "Starting linkTimer\n"
			linkviewports.linkTimer.start() --start the timer
		)
	),		
	
	fn initializeLinkStruct =
	(
		callbacks.removeScripts id:#linkViewports
		linkTimer = dotNetObject "System.Timers.Timer" 500 --create a timer with 500ms interval
		dotNet.addEventHandler linkTimer "Elapsed" linkTimerHandler --when the timer ticks, run the link callback
	),
	
	_init = initializeLinkStruct() --a variable that executes the initializeLinkStruct function when the struct instance is created
)

linkViewports = linkViewportsStr() --create struct instance
linkViewports.setCallbacks on --turn link on


I am on max2009 64.
 
  06 June 2011
why do you use timer? i would use redraw views callback.
 
  06 June 2011
As usual you are right.
Without the timer it works fine, even with #viewportChange callback.
I added the timer so it wouldn't redraw all the time, but I can just add the mousebuttons check in the callback function and it's ok.

Maybe the problem was that the timer callback runs on a different thread than the 3dsmax UI thread.

Thanks for the help.
 
  06 June 2011
Here is the working (and shorter) code if anyone is interested:

global linkViewports
struct linkViewportsStr
(
	callbackString = "linkViewports.doSync()",	
	lastTM = matrix3 0, --a variable to store the last transform
	
	fn areMatricesEqual a b = a[1]==b[1] and a[2]==b[2] and a[3]==b[3] and a[4]==b[4],
	
	fn setLinkActive state =
	(
		callbacks.removeScripts id:#linkViewports
		if state do callbacks.addScript #viewportChange callbackString id:#linkViewports
	),
	
	fn doSync =
	(
		if mouse.buttonStates.numberSet == 0 and viewport.getType() == #view_persp_user do --if the mouse is not pressed and this is a perspective viewport
		(		
			local masterView = viewport.activeViewport --check which viewport is active (the master)
			local masterTm = viewport.GetTM() --get current viewport transform (for rotation part)
			if not areMatricesEqual lastTM masterTM do --if the new transform is different than the old transform
			(
				local offset = masterTm.row4-lastTm.row4 --offset in position since last update
				lastTM = masterTM --save new transform for next update
				setLinkActive off --before changing the viewports, we must disable the callback temporarily, otherwise we will get stuck in an infinite loop!
				with redraw off --disable redrawing
				(
					viewport.activeViewport = 1
					for v = 1 to viewport.numViews do if v!=masterView and viewport.getType index:v == #view_persp_user do --for each perspective viewport except the master
					(
						viewport.activeViewport = v
						local slaveTm = viewport.GetTM() --get the slave viewport transform (for position part)
						viewport.setTM (matrix3 masterTm.row1 masterTm.row2 masterTm.row3 (slaveTm.row4+offset)) -- set the transform
					)
					viewport.activeViewport = masterView --return the view to the one we started from	
				)		
				setLinkActive on --reenable the callbacks
			)
		)
	)	
)

linkViewports = linkViewportsStr() --create struct instance

linkViewports.setLinkActive on --turn link on
 
  06 June 2011
Originally Posted by lo: As usual you are right.
Without the timer it works fine, even with #viewportChange callback.
I added the timer so it wouldn't redraw all the time, but I can just add the mousebuttons check in the callback function and it's ok.

Maybe the problem was that the timer callback runs on a different thread than the 3dsmax UI thread.

Thanks for the help.


timer has to work. i use a timer for some viewport things. but not is in this case.
 
  06 June 2011
the only command which causes the problem is setting viewport.activeViewport property.
For example, viewport.GetTM and viewport.setTM do not cause the problem.
 
  06 June 2011
Originally Posted by lo: the only command which causes the problem is setting viewport.activeViewport property.
For example, viewport.GetTM and viewport.setTM do not cause the problem.


i like the method with timer

unRegisterRedrawViewsCallback syncViews

global ViewHwnd = for w in (windows.getChildrenHWND #max) where w[4] == "ViewPanel" do exit with w[1]
WM_SETREDRAW = 0x000B

global DirtyViewport
global ViewTransform = matrix3 1
global LastView = undefined

fn dirtyViewportDraw = (DirtyViewport = on)
fn syncViews s e = if viewport.numViews > 1 and DirtyViewport and (viewport.getType()) == #view_persp_user do
(
	s.Stop()
	atm = viewport.GetTM()
	ctm = ViewTransform
	av = viewport.activeViewport
	if LastView == av and ((atm[1] != ctm[1]) or (atm[2] != ctm[2]) or (atm[3] != ctm[3])) do
	(
		windows.sendmessage ViewHwnd WM_SETREDRAW 0 1
		
		for v=1 to viewport.numViews where v != av and (viewport.getType index:v) == #view_persp_user do
		(
			viewport.activeViewport = v
			vtm = viewport.GetTM()
			viewport.setTM (translate (rotate (scalematrix vtm.scale) atm.rotation) vtm.pos) 
		)
		viewport.activeViewport = av 
		windows.sendmessage ViewHwnd WM_SETREDRAW 1 1
	)
	LastView = av
	
	ViewTransform = atm
	DirtyViewport = off
	s.Start()
)
registerRedrawViewsCallback dirtyViewportDraw

try
(
	dotnet.removeAllEventHandlers tmr
	tmr.dispose()
) 
catch()

tmr = dotnetobject "Timer"
tmr.Stop()
tmr.Interval = 10
dotnet.addEventHandler tmr "Tick" syncViews
DirtyViewport = on
tmr.Start()
 
  06 June 2011
very nice!

You get extra points for not using a c# assembly
 
  06 June 2011
Originally Posted by lo: very nice!

You get extra points for not using a c# assembly


i wanted... but... put a lid on my desire...
 
  06 June 2011
Originally Posted by lo: Here is the working (and shorter) code if anyone is interested:

...


do you sync views scale on purpose?
 
  06 June 2011
Originally Posted by denisT: do you sync views scale on purpose?


Honsetly, I haven't given it much thought. Is there ever a naturally occurring case in which the perspective viewport TM scale part is not [1,1,1]?
 
  06 June 2011
Originally Posted by lo: Honsetly, I haven't given it much thought. Is there ever a naturally occurring case in which the perspective viewport TM scale part is not [1,1,1]?

scale might be any. matrix's [1] ([2],[3]) is direction*scale. in my sample i don't sync scale.
 
  06 June 2011
Yes, I noticed that. But I'm trying to understand the logic of why it's important. Can you give an example in which (getViewTM()).scale!=[1,1,1] (other that setting it so by script)?
 
  06 June 2011
Originally Posted by lo: Yes, I noticed that. But I'm trying to understand the logic of why it's important. Can you give an example in which (getViewTM()).scale!=[1,1,1] (other that setting it so by script)?

that's easy. just try to change a view by wheeling mouse middle button
 
  06 June 2011
Originally Posted by denisT: that's easy. just try to change a view by wheeling mouse middle button


That is what I tried but the scale stays [1,1,1] for me... only the position changes.
 
Thread Closed share thread



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
CGSociety
Society of Digital Artists
www.cgsociety.org

Powered by vBulletin
Copyright 2000 - 2006,
Jelsoft Enterprises Ltd.
Minimize Ads
Forum Jump
Miscellaneous

All times are GMT. The time now is 12:55 PM.


Powered by vBulletin
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.