You can register one or more functions to be called whenever the 3ds Max viewports are redrawn.
In addition, with the introduction of the Nitrous Graphics Manager in 3ds Max 2012, any MAXScript Graphics Window (gw.) Viewport Drawing Methods must be wrapped in a Viewport Redraw Callback function to produce any results due to the progressive refinement nature of
the drawing process.
The following methods let you register and unregister these callbacks:
registerRedrawViewsCallback <fn>
unRegisterRedrawViewsCallback <fn>
You can register as many functions as you like. Each one is individually called whenever the viewports are redrawn. The functions
you register must take no arguments.
EXAMPLE:
|
fn redrawviews_p = print "Viewports Redrawn"
registerRedrawViewsCallback redrawviews_p
|
In the above example, the registered function causes the string "Viewports Redrawn" to be printed in the Listener window whenever
the 3ds Max viewports are redrawn.
|
Special Considerations
-
If a runtime error occurs in a callback function while it is being executed, an error message is displayed and that callback
function is permanently disabled.
-
The registered function is executed in a special context and not in the context of the code that created it. This means that
the function cannot contain references to local variables in outer code nestings surrounding the function definition since
those variables are on an execution stack that does not exist at the time the function is called. An important exception to
this is utility and rollout panel locals, such as local functions, rollout variables and nested rollouts. You can refer to
them in change handler code inside rollout code as they are associated directly with their rollout or utility object.
-
Note that it is the function value that is being registered, not the function name or global variable. This means that redefining the same-named function after
registering it will not change the callback automatically, but register another function value to be called resulting in two
separate callbacks. You either need to unregister the old callback function, then redefine the function and register it again
(see further on this page), or make the registered function an intermediary which calls another function,
EXAMPLE:
|
fn redrawviews_cb = print currentTime
fn rvcb = redrawviews_cb()
registerRedrawViewsCallback rvcb
|
In this case, the registered callback function, rvcb , calls the real call back, redrawviews_cb , (by referencing the global variable it is defined in), meaning you can redefine redrawviews_cb() as often as you need and the callback will always invoke the latest definition. This is a useful technique to employ while
you are developing and debugging a callback.
Note that you may only redefine the redrawviews_cb() function but NOT evaluate the other two lines again, otherwise you would end up with multiple callbacks calling the same
function on each redraw.
|
-
Due to the fact that the function value is being registered, it is absolutely important to unregister any previous value using
the unregisterRedrawViewsCallback() method before redefining the function and registering a new callback.The method can be called with an undefined argument,
so it can be safely placed just before the function definition without the need to ensure the function already exists. This
will ensure that a previous definition of the function will not remain registered without any means to unregister it (short
of restarting 3ds Max).
EXAMPLE:
|
unregisterRedrawViewsCallback redrawviews_callback_function
fn redrawviews_callback_function = print "Viewport Redraw 1"
registerRedrawViewsCallbackredrawviews_callback_function
|
Evaluating the above will first attempt to unregister the callback function. If it was not registered, nothing will happen.
Then the function will be defined and its value will be registered as callback. Orbiting in the viewport for example will
cause the string to be printed to the listener.
If you would change the string to "Viewport Redraw 2" and evaluate the same code, the old function will be unregistered, the
new function will be defined and the new callback will be registered. Orbiting the viewport will print the new string only.
|
BAD EXAMPLE:
|
fn redrawviews_callback_function = print "Viewport Redraw2"
unregisterRedrawViewsCallback redrawviews_callback_function
registerRedrawViewsCallback redrawviews_callback_function
|
If the unregister function call were the second line and not the first, the new function would be defined first, then the
unregister attempt would fail silently because the new function would be used and the old one's value would be inaccessible.
This would result in both the old and the new functions being executed on viewport redraws, thus printing both "Viewport Redraw
1" and "Viewport Redraw 2" to the Listener.
|
LOCAL SCOPE EXAMPLE:
|
(--open a local scope
global redrawviews_cb_function --ensure variable visibility
unregisterRedrawViewsCallback redrawviews_cb_function
fn redrawviews_cb_function= print "Redraw Test1"
registerRedrawViewsCallback redrawviews_cb_function
)--end local scope
|
If the function definition and the callback unregistering and registering are performed in local scope, it is paramount to
ensure the callback function's visibility to the code in either global scope or a "private local" scope such as the top-level
local scope of a MacroScript that would protect the function value during the current session. Evaluating the above code without
a global declaration would create a function value inside the LOCAL scope defined by the outer-most brackets and the attempt
to call unregisterRedrawViewsCallback() in a second evaluation of the script would NOT see the original function value in the variable since the variable would have
ceased to exist after the first run. Thus, the variable storing the function value must be declared as global to allow a second
run of the script to successfully unregister the previous function value.
Changing the printed string to "Redraw Test 2" and evaluating the code again would correctly unregister the old value stored
in the global variable, then overwrite it with the new value and register it as a redraw views callback.
|