Viewport Redraw Callback Mechanism
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.
|