Notifications
Clear all

[Closed] NodeEventCallback Failure

 JHN

Hi all!

Does anyone know why this maxscript fails when run for the first time, via startup scripts folder.
Running it manually doesn’t fail.


-- Clear Inits
global nmt_AddCamera_sceneNodeAdded = undefined
global nmt_cameraCallback = undefined

-- Define Callback FN
struct nmt_cameraCallback
(
	fn setCameraHandle ev handle =
	(
		if handle.count > 0 do
		(
			-- get all handles
			local all = for h in handle collect GetAnimByHandle h
			-- get the camera
			local cam = (for a in all where isKindOf a camera collect a)[1]
			-- find cam
			if cam != undefined then
			(
				-- find target
				local target = (for a in all where isKindOf a TargetObject and cam.target != undefined and cam.target == a collect a)[1]
				
				-- print results
				format "camera : %, target : %
" cam target
			)
		)
	)
)

-- Init
::nmt_AddCamera_sceneNodeAdded = NodeEventCallback mouseUp:true delay:500 added:nmt_cameraCallback.setCameraHandle

To test place file in startup folder and create any object. The test code in the help file also fails for me the first time a node is created.
Help File

Error code :

>> MAXScript NodeEventCallback Exception:
– Unknown system exception <<

Fails on both max 2014/2016 for me.

Any one has any pointers or ideas?

-Johan

30 Replies
 lo1

It works when the function is not inside a struct.

 JHN

That’s so weird to me, all my other regular callbacks (.addScript) work fine via structs…
Do you have an possible explanation for it?

But thanks for taking time and checking! At least there’s a workaround!

Cheers,
-Johan

 lo1

No idea why structs fail, but the code is available in the SDK if you want to investigate:

…\samples\maxscript\mxsagni\extclass.cpp

1 Reply
 JHN
(@jhn)
Joined: 10 months ago

Posts: 0

Absolutely, but first I need to get this project out of the way, maybe after that…

Thanks again!
-Johan

this snippet shows the way i usually do for a definition of a node callback monitor with global structure:

global NodeCallbackMonitor 
(
	struct NodeCallbackStruct
	(
		eventCallback,
		fn disposeCallback = if isstruct (d = NodeCallbackMonitor) do
		(
			d.eventCallback = undefined
			gc light:on
			ok
		),
		fn callbackMonitor event handles = 
		(
			format "event:% handles:%
" event handles
		),
		on create do
		(
			disposeCallback()
			eventCallback = NodeEventCallback all:callbackMonitor
			eventCallback.enabled = on
		)
	)
	NodeCallbackMonitor = NodeCallbackStruct()
	ok
)

Hey,

I am hitting the same unknown exception issues as well lately. But I am really not sure why.
I tried to copy the method here , but I still am hitting an exception randomly.

I thought maybe it could be from cloneOps that I am using, but i switched to Instance, and it didn’t matter.
But sometimes it doesn’t happen for a while, then other-times i hit it every time in a row, triggering the CB.

Before I was also trying to do it much simpler, without a Struct, by setting a Global Var equal to something like:


tT_AlignSOCallback = undefined
gc light:true
tT_AlignSOCallback = NodeEventCallback mouseUp:true delay:150 geometryChanged:tT_alignObjectToSO subobjectSelectionChanged:tT_alignObjectToSO selectionChanged:tT_alignObjectToSO

This is a test/might be a very bad way to do this, but is what I was trying as below, in a Struct. I am in Max 2016 64bit SP1.
This still hits the same issue as my simpler method.

Any ideas?

Thank you!


Global functions 

struct FunctionStruct
(
   newNodeEventCallback,

   fn alignObjectToSO ev nd = 
   (
      local moveObj
      local SOLevel = SubObjectLevel

      
      --Kill Callack, since Cloneops will trigger it again when making  a new node / selection 
      ::functions = undefined
      gc light:true

      with undo off
      (
         for o in tT_AlignObjPreviewNodes where isValidNode o do delete o
         tT_AlignObjPreviewNodes = #()
      )
      

      --Calls FN to Get SO selection/Build Transform Data,  and calls then another FN to Instance/Place objects via Cloneops
      
      if SubObjectLevel == 1 do tT_alignObjectToVert 1
      if SubObjectLevel == 2 do tT_alignObjectToEdge 2
      if SubObjectLevel == 3 do tT_alignObjectToEdge 2
      if SubObjectLevel == 4 do tT_alignObjectToFace 4
      if SubObjectLevel == 5 do tT_alignObjectToFace 5

         
      if (SOLevel != undefined and SOLevel != 0) do
      (
         setCommandPanelTaskMode #modify
         subObjectLevel = SOLevel
      )

      
      functions = FunctionStruct()

      redrawViews()
   ),
   fn init =
   (
      newNodeEventCallback = NodeEventCallback mouseUp:true delay:150 geometryChanged:alignObjectToSO subobjectSelectionChanged:alignObjectToSO selectionChanged:alignObjectToSO
   ),
   start = init()
)

functions = FunctionStruct()


It doesn’t seem like it matters if I have a struct or not, unless I am doing it wrong.

Having all my Fns in the Struct or not, I still get the exception, when instancing/cloning objects.
Usually it seems to happen more when I clone multiple objects. The Callback should be undefined/disabled when I clone them all, then I turn on after the that.

Maybe I can’t be doing all this in 1 function, or rather enabling/disabling the Callback in the same FN I use to perform the Callback…

Edit: I will try this method again, the last post on this thread ( http://forums.cgsociety.org/showthread.php?f=98&t=949177&highlight=NodeEventCallback )

Maybe if I do the fake FN in the struct, it willl work…

I finally figured it out… !

I was going off of the documentation, saying to set the callback to undefined and run GC:lite.
But with my script I am writing, I had to basically stop it, do some operations/cloning as I said, then start it again.
Doing the Undefined method though, was causing those Unknown Exceptions.

So going off of what I saw Dennis mention, and doing some testing of my own, I figured out that I can just leave the callback alone.
Or at least, not set it Undefined, until I really do want to disable/clear it. Instead I use the .Enabled property, to turn it on/off temporarily.

But in my case, I had to do a little trick of using a counter, checking/setting 0/1, depending on my state I was in.
Otherwise it would get into a continuous loop when Enabling/Disabling inside the Callback FN.

Basically something like this, and I only increment the counter, when I actually perform the action in my other FNs.


--Not my exact FN, but same layout more or less
fn callbackFN ev nd = 
(
   if (counter == 1) then
   (
      counter = 0
   )
   else
   (
      if (counter == 0) do
      (
         local SOLevel = SubObjectLevel
         
         theCallback.enabled = off

         
         if SubObjectLevel == 1 do FN
         if SubObjectLevel == 2 do FN
         if SubObjectLevel == 3 do FN
         if SubObjectLevel == 4 do FN
         if SubObjectLevel == 5 do  FN

            
         if (SOLevel != undefined and SOLevel != 0) do
         (
            setCommandPanelTaskMode #modify
            subObjectLevel = SOLevel
         )
         redrawViews()


          --This  (.enabled) will trigger the callback again, right away, if called inside like I am here.  Well, mainly because if I had a face selection, it would detect that in my external function, and          process    some stuff, then re-enable the callback FN, which then would see the selection, and repeat forever.  
         So i used a counter to stop it, if I called my external Fns from the SubObject stuff above.   
         If those executed properly, it would set the counter to 1, which then would cause the instant callback trigger, to set it to 0.  So then the next selection/etc in the viewport would jump          back in to the count ==0 section again and work as expected. 

         theCallback.enabled = on 
   )
)


i have to show this again:


global EventCallbackWrapper
(
   if (globalvars.isglobal #EventCallbackWrapper) do
   (
      if isstruct (d = globalvars.get #EventCallbackWrapper) do d.destroyCallback owner:d
   )
   EventCallbackWrapper =
   (
      struct EventCallbackStruct
      (
         id,
         callbackContainer,
         fn doCallbacks event handles = 
         (
            owner_def = valOps.current_frame_owner()
            nodes = for handle in handles where isvalidnode (node = GetAnimByHandle handle) collect node
            
            format "> % %
" event nodes
            ------ format "> % %
" event nodes <this struct instance id>   ------> try to make it works
         ),
         fn destroyCallback owner:this =
         (
            owner.callbackContainer = undefined
            gc light:on
         ),
            
         on create do
         (
            id = (random -1e9 1e9)
            callbackContainer = NodeEventCallback all:doCallbacks
         )
      )
   )
   EventCallbackWrapper = EventCallbackWrapper()
   ok
)

but in this example i’m specially don’t show how to pass to the callback function pointer to the instance of the the structure.
This is a challenge for you … [b]check the commented piece of code and try to make it works!

[/b]when you get access to the struct instance ID you will get EVERYTHING!

you have to understand the difference on just a putting a callback in structure and getting access to the owner structure from the callback function. another way you have to define all methods inside the callback function. which is a big limitation

Page 1 / 3