I’m not sure what’s it all about, but is it correct?
global EventCallbackWrapper
(
if (globalvars.isglobal #EventCallbackWrapper) do
(
if isstruct (d = globalvars.get #EventCallbackWrapper) do d.destroyCallback owner:d
)
EventCallbackWrapper =
(
local _this
struct EventCallbackStruct
(
id,
callbackContainer,
fn doCallbacks event handles =
(
nodes = for handle in handles where isvalidnode (node = GetAnimByHandle handle) collect node
format "> % % %
" event nodes _this.id
),
fn destroyCallback owner:this =
(
owner.callbackContainer = undefined
gc light:on
),
on create do
(
_this = this
id = (random -1e9 1e9) as integer
callbackContainer = NodeEventCallback all:doCallbacks
)
)
)
EventCallbackWrapper = EventCallbackWrapper()
ok
)
super!
absolutely right!!!
5++
my original version:
global EventCallbackWrapper
(
if (globalvars.isglobal #EventCallbackWrapper) do
(
if isstruct (d = globalvars.get #EventCallbackWrapper) do d.destroyCallback owner:d
)
EventCallbackWrapper =
(
owner
struct EventCallbackStruct
(
private
id = 0x196700,
public
fn who = (id),
callbackContainer,
fn doCallbacks event handles =
(
nodes = for handle in handles where isvalidnode (node = GetAnimByHandle handle) collect node
format "% > % %
" (owner.who()) event nodes
),
fn destroyCallback owner:this =
(
owner.callbackContainer = undefined
gc light:on
),
on create do
(
owner = this
callbackContainer = NodeEventCallback all:doCallbacks
)
)
)
EventCallbackWrapper = EventCallbackWrapper()
ok
)
using this technique you can put all heavy methods into the struct’s body… which can be used not just by the callback function only
someone might ask me why i don’t declare ‘owner’ variable as local? yes, it overrides any global ‘owner’ variable. because I want to punish unskillful and not far-sighted developers. [b]DON’T USE A COMMON NAMES FOR YOUR GLOBALS
[/b]but if you want to be safe, sure you have to declare it as local
Thank you!
I’ll have to look at that more, but what you guys are talking about might be over my head… 😮
I am now running into the same issue as I think Joker Martini saw before possibly.
I disable the callback, or seems like I do, but then when I enable again, it sometimes will trigger 2-3 times.
2 times if I have a modifier above my EditablePoly.
“geometryChanged”
“subobjectSelectionChanged”
Does the callback still record/know about these changes, even with off?? I am doing everything while it should be OFF, then turn it back on at the end, which is what’s confusing me…
Something like:
if (tT_AlignSOCallback != undefined) do tT_AlignSOCallback.enabled = off
if (tT_AlignSOCallback.enabled == off) do --just was testing if this matter for any reason
(
if SOLevel == 1 do tT_alignObjectToVert()
if SOLevel == 2 or SOLevel == 3 do tT_alignObjectToEdge()
if SOLevel == 4 or SOLevel == 5 do tT_alignObjectToFace()
if (SOLevel != undefined and SOLevel != 0) do
(
setCommandPanelTaskMode #modify
modPanel.setCurrentObject theSelection.baseObject
subObjectLevel = SOLevel
)
redrawViews()
tT_AlignSOCallback.enabled = on
)
His post, talking about a similar issue… ( http://forums.cgsociety.org/showthread.php?f=98&t=1054513&highlight=NodeEventCallback )
I want the user to be able to select Sub-Object elements, and do some processing with other nodes, then come back to the same SO level.
But it seems like, more so with any Modifiers, when trying to go back down to the BaseObject, it causes these Callbacks to freak out/get in an infinite loop, even if I enable/Disable.
This is probably NOT the way to do this, but if you run this code, with a Teapot in the scene, and if you select an EditablePoly Mesh, it will probably give an Unknown Exception at first.
Then after that it might work, but is still a good chance it will give an exception again.
global EventCallbackWrapper
(
if (globalvars.isglobal #EventCallbackWrapper) do
(
if isstruct (d = globalvars.get #EventCallbackWrapper) do d.destroyCallback owner:d
)
EventCallbackWrapper =
(
local _this
struct EventCallbackStruct
(
id,
callbackContainer,
fn doCallbacks event handles =
(
--nodes = for handle in handles where isvalidnode (node = GetAnimByHandle handle) collect node
format "> % % %
" event nodes _this.id
if (event == #callbackEnd) do
(
EventCallbackWrapper.destroyCallback()
local theObject = $Teapot001
local i = 0
while i < 10 do
(
i += 1
maxops.clonenodes theObject expandHierarchy:true cloneType:#instance actualNodeList:&c newNodes:&d
d[1].isFrozen = true
d[1].name = "TempObject"
)
if (selection[1] != undefined and classof selection[1].baseObject == Editable_Poly) do
(
setCommandPanelTaskMode #modify
modPanel.setCurrentObject selection[1].baseObject
SubObjectLevel = 4
EventCallbackWrapper.callbackContainer = NodeEventCallback callbackEnd:EventCallbackWrapper.doCallbacks
)
)
),
fn destroyCallback owner:this =
(
owner.callbackContainer = undefined
gc light:on
),
on create do
(
_this = this
id = (random -1e9 1e9) as integer
callbackContainer = NodeEventCallback callbackEnd:doCallbacks
)
)
)
EventCallbackWrapper = EventCallbackWrapper()
ok
)
I’ve been working on other things, but still want to figure this out.
Id like to support modifiers. But some cause multi callbacks to trigger from subobject and geo changed.
Really I think the issue is from clonenodes exiting my SO level.
But I’m not sure I can stop that. But I use it since it can dupe a heirarchy.
I’m very busy right now but if you want to help yourself, please put a code that you want to be fixed and simple explanation what’s wrong and how it has to work… i will take a look
Thank you
I am hitting an issue with how the NodeEventCallback is functioning. I initially was setting my callback variable to undefined , and doing a GC, to stop the events from firing when I didn’t want them to.
But after spending too much time trying to figure out why I was hitting random Fails/Exception Errors, I saw on here, you could use On/Off.
So I am using On/Off now, which partially works. But the issues I hit yet is with Events getting triggered multiple times in a row/when I don’t expect it.
If i turn the callback OFF, then do some stuff, then turn it back ON, it can still sometimes trigger 2-3 events in a row, and mess up my code/flow.
Like I mentioned, I would like to ideally stay in the SubObject Mode the user is in, and not exit. But CloneNodes seems to exit, so I have to go back in. I clone nodes to Align to the SO Selections.
But if I go back in, I hit the SubObjectLevel Changed Event. Which I can ignore, using a variable, that only is set if my other functions actually run.
So it runs my initial functions, tries to run again, but it hits a block that causes the variable to reset, and doesn’t let my other code execute again, until I re-select/change my SO Level/etc.
I posted a chunk of my code before, but I am basically calling this, which starts up my other callback function.
tT_AlignSOCallback = NodeEventCallback mouseUp:true delay:150 geometryChanged:tT_alignObjectToSO subobjectSelectionChanged:tT_alignObjectToSO selectionChanged:tT_alignObjectToSO
Then my CB FN
fn tT_alignObjectToSO event nd =
(
if (tT_AlignObjCBCounter != 0) then
(
tT_AlignObjCBCounter = 0
tT_AlignObjCounter = 0
)
else
(
local SOLevel = SubObjectLevel
local theSelection = selection[1]
tT_AlignObjSOLevel = SOLevel
--Turn OFF Callback, so it's not being triggered when copying/instancing objects to place them. Then Turn back on after.
if (tT_AlignSOCallback != undefined) do tT_AlignSOCallback.enabled = off
with undo off
(
for o in tT_AlignObjPreviewNodes where isValidNode o do delete o
tT_AlignObjPreviewNodes = #()
tT_AlignObjPreviewNodeTransforms = #()
)
if SOLevel == 1 do tT_alignObjectToVert()
if SOLevel == 2 or SOLevel == 3 do tT_alignObjectToEdge()
if SOLevel == 4 or SOLevel == 5 do tT_alignObjectToFace()
if (SOLevel != undefined and SOLevel != 0) do
(
setCommandPanelTaskMode #modify
--modPanel.setCurrentObject theSelection.baseObject --If I try this, it can really make it freak out/trigger many times/get stuck in a loop
subObjectLevel = SOLevel
)
format "Done Placing Object(s): Placement Count:%
" tT_AlignObjCounter
redrawViews()
tT_AlignSOCallback.enabled = on
)
)
the way you doing is complicated by structure… here is an example how i would do (by idea and structure)
global PolyFaceExtruder
(
if (globalvars.isglobal #EventCallbackWrapper) do
(
if isstruct (d = globalvars.get #PolyFaceExtruder) do d.EventCallbackWrapper.destroy()
)
PolyFaceExtruderStruct =
(
struct PolyFaceExtruderStruct
(
public
EventCallbackStruct =
(
struct EventCallbackStruct
(
public
callbackContainer,
fn doCallbacks event handles =
(
--owner_def = valOps.current_frame_owner()
nodes = for handle in handles where isvalidnode (node = GetAnimByHandle handle) collect node
format "callbacks > % %
" event nodes
),
fn destroy =
(
callbackContainer = undefined
gc light:on
),
fn create enabled:on =
(
callbackContainer = NodeEventCallback all:doCallbacks enabled:enabled mouseUp:on
),
on create do
(
create()
)
)
),
EventCallbackWrapper = EventCallbackStruct(),
extrude_amount = 10,
fn extrudePolyFaces node: faces:#selected quite:on =
(
callbacks_enabled = EventCallbackWrapper.callbackContainer.enabled
if quite do
(
EventCallbackWrapper.destroy()
)
if node == unsupplied do node = selection[1]
format ">> % %
" node faces
if iskindof node Editable_Poly do
(
if faces == #selected do faces = polyop.getfaceselection node
result = polyop.extrudeFaces node faces this.extrude_amount
redrawviews()
)
if quite and callbacks_enabled do
(
EventCallbackWrapper.create()
)
result
),
on create do
(
)
)
)
PolyFaceExtruder = PolyFaceExtruderStruct()
ok
)
(
delete objects
p = plane width:108 length:64 widthsegs:19 lengthsegs:11 wirecolor:blue
converttopoly p
polyop.setfaceselection p \
#{
21..24, 29, 34..36, 40, 44, 48, 52, 56, 59, 63,
67, 75, 78, 82, 86, 94, 97, 101, 105, 110..112,
116, 120, 124, 128, 135, 139, 143, 147, 154, 158,
162, 166, 170, 173..176, 179..183, 186..188 \
}
update p
)
/*
-- #1: THERE IS NO CALLBACKS
undo "Extrude" on PolyFaceExtruder.extrudePolyFaces()
-- #2: THERE ARE CALLBACKS
(
polyop.extrudeFaces $ (polyop.getfaceselection $) 10
redrawviews()
)
*/
but i have to disappoint you – you will get callbacks on undo/redo anyway. You can check if the undo/redo scene general events are fired before node event, you have to chance to disable node events using #sceneUndo and #sceneRedo callbacks
Thank you!!
This is working great with modifiers it looks like :D. Not freaking out at least, like it was before for me, and only seems to trigger once.
I am noticing however, the same issue I was before, when instancing/copying multiple objects to my SO selection (Verts in this case).
If you run this code, you should get a System Exception sometimes, as you change your vert selection… :banghead:
I am not sure why this happens yet, or more so, why happens when cloning more objects. Maybe I am doing this wrong/shouldn’t set it up this way, for my FN.
callbacks > #callbackBegin #()
callbacks > #subobjectSelectionChanged #($Editable_Poly:Plane001 @ [0.000000,0.000000,0.000000])
callbacks > #callbackEnd #()
Done Placing Object(s): Placement Count:36
>> MAXScript NodeEventCallback Exception:
-- Unknown system exception <<
global PolyFaceExtruder
(
if (globalvars.isglobal #EventCallbackWrapper) do
(
if isstruct (d = globalvars.get #PolyFaceExtruder) do d.EventCallbackWrapper.destroy()
)
PolyFaceExtruderStruct =
(
struct PolyFaceExtruderStruct
(
public
EventCallbackStruct =
(
struct EventCallbackStruct
(
public
callbackContainer,
fn doCallbacks event handles =
(
--owner_def = valOps.current_frame_owner()
nodes = for handle in handles where isvalidnode (node = GetAnimByHandle handle) collect node
format "callbacks > % %
" event nodes
case event of
(
#callbackEnd:
(
PolyFaceExtruder.alignObjectToSO (PolyFaceExtruder.cloneObj) true
)
)
),
fn destroy =
(
callbackContainer = undefined
gc light:on
),
fn create enabled:on =
(
callbackContainer = NodeEventCallback all:doCallbacks enabled:enabled mouseUp:on
),
on create do
(
create()
)
)
),
EventCallbackWrapper = EventCallbackStruct(),
extrude_amount = 10,
fn extrudePolyFaces node: faces:#selected quite:on =
(
callbacks_enabled = EventCallbackWrapper.callbackContainer.enabled
if quite do
(
EventCallbackWrapper.destroy()
)
if node == unsupplied do node = selection[1]
format ">> % %
" node faces
if iskindof node Editable_Poly do
(
if faces == #selected do faces = polyop.getfaceselection node
result = polyop.extrudeFaces node faces this.extrude_amount
redrawviews()
)
if quite and callbacks_enabled do
(
EventCallbackWrapper.create()
)
result
),
theNodes = #(),
cloneObj = undefined,
fn cloneTransformObj theSelection cloneObj =
(
local cloneOps = maxops.clonenodes
if (classof theSelection.baseObject == Editable_Poly) do
(
setCommandPanelTaskMode #create
local theBaseObj = (theSelection.baseObject)
local vertSel = polyop.getVertSelection theBaseObj
local getVertPos = polyop.getVert
if ((vertSel as array).count != 0) do
(
for vert in vertSel do
(
cloneOps cloneObj expandHierarchy:true cloneType:#instance actualNodeList:&c newNodes:&d
cloneObj = d[1]
cloneObj.name = uniqueName "Test_Clone" numDigits:3
cloneObj.isFrozen = true
cloneObj.pos = getVertPos theBaseObj vert node:theSelection
append theNodes cloneObj
)
)
)
),
fn alignObjectToSO node quiet =
(
callbacks_enabled = EventCallbackWrapper.callbackContainer.enabled
if quiet do
(
EventCallbackWrapper.destroy()
)
local SOLevel = SubObjectLevel
local theSelection = selection[1]
with undo off
(
for o in theNodes where isValidNode o do delete o
theNodes = #()
)
if SOLevel == 1 do cloneTransformObj theSelection node
if (SOLevel != undefined and SOLevel != 0) do
(
setCommandPanelTaskMode #modify
modPanel.setCurrentObject theSelection.baseObject
subObjectLevel = SOLevel
)
format "Done Placing Object(s): Placement Count:%
" (theNodes.count)
redrawViews()
if quiet and callbacks_enabled do
(
EventCallbackWrapper.create()
)
result
),
on create do
(
)
)
)
PolyFaceExtruder = PolyFaceExtruderStruct()
ok
)
delete objects
p = plane width:15 length:15 widthsegs:5 lengthsegs:5 wirecolor:blue
convertToPoly p
select p
polyop.setVertSelection $.baseObject #{1..($.baseObject.mesh.verts.count)}
update p
t = Teapot()
t.radius = 1.0
setCommandPanelTaskMode #modify
SubObjectLevel = 1
PolyFaceExtruder.cloneObj = t
PolyFaceExtruder.alignObjectToSO (PolyFaceExtruder.cloneObj) true
This is very very weird…
It seems like, if you run my last code I posted, over time, as you select/de-select verts, to CloneNodes, it will have a random System Exception, earlier and earlier.
At first I was only getting it after selecting 20+ verts. Then soon it was after doing 10+ verts, then 7,6,5,4,3,2,1. Not hitting it EVERY time, but pretty often.
I’m not sure if it’s because of deleting/re-creating the Callback so often, if that matters. Or what the deal is…
The callback is only being triggered once, at least to re-create it, each time. My Object Cloning is also only being triggered once.
callbacks > #callbackBegin #()
callbacks > #subobjectSelectionChanged #($Editable_Poly:Plane001 @ [0.000000,0.000000,0.000000])
callbacks > #callbackEnd #()
Done Placing Object(s): Placement Count:5
"Create"
>> MAXScript NodeEventCallback Exception:
-- Unknown system exception <<