i did a lot of work for ‘fool proofing’… but i really like an idea where any Biped node being deleted deletes whole Biped skeleton (rig).
previously i’ve showed a warnings kinda - Hey! you’ve deleted a ‘key’ node! are you stupid?
now i’m deleting whole skeleton with no giving any warning message. i give a chance to an user to feel himself stupid.
When object deleted, delete children. Huh?
That’s very much along the lines of what I was going for. Glad you got it working properly, because if it got to the point of requiring C++ I clearly never would have.
I ended up going with my “storing the array of related nodes on a separate control object” approach. I also eventually figured out that if
a) deleting any of the stored nodes will delete the master, and
b) deleting the master will delete all the stored nodes,
then deleting /any/ of the stored nodes will end up indirectly deleting /all/ of them, without having to loop through them in the parameters!
And with only about 50 lines of code, so I feel pretty good about that.
Now, the REST of what I’m going to do with this…
i’ve found the BIG bug in the max node deleting mechanism… any node is becoming deleted gets a status of a ‘being deleted node’. but if you ask the system about its state, the system answered that the node is not deleted and it can be deleted. this is wrong! the deleting of a node that is in a process of deleting causes the max crash on REDO
Luckily my method does not cause any crashing on undo or redo, but that does sound like a significant problem that should be addressed to Autodesk…
Okay, very weird. Apparently it is not crashing on undo or redo, but it DOES crash if I load another file.
I’ve tried setting it up as a function, a when construct, etc. but it has the same problem every time. The simplest version is simply putting the following into my parameters:
on nodes tabChanged change do
(
if change == #refDeleted do
(
owner = (refs.dependentNodes (custAttributes.getOwner this))[1]
if (isValidNode owner) and (not isDeleted owner) do delete owner
)
)
As I said, it’s not crashing on undo or redo, but only when I try to open a different file, or create a new one. Any ideas on how to get around this?
On a possibly related note, something I tried had the deleted/to-be-deleted objects showing up as $<unknown>:nodename @ [x,y,z]. Unfortunately, the program crashed and I don’t remember what I did to get to that point.
Also, can someone please tell me why
refs.dependentNodes (custAttributes.getOwner this)
returns the node in question like I want it to, while
custAttributes.getOwner this
returns “Object”?
I can’t figure out what I’m supposed to do with “Object”. It returns the same class as the node, but I can’t delete it, and can’t get a name, id, properties, or anything else off of it.
when you add a CA to an object you have an option - add it to the node or to node’s baseobject.
owner is the thing where the CA was added.
b = box()
a = attributes test (;)
custattributes.add b a baseobject:on
b.custattributes
b.baseobject.custattributes
custattributes.add b a baseobject:off
b.custattributes
so if you add ca to a baseobject, the owner is baseobject, to get a node you have to ask its first dependent node.
I didn’t think that would actually solve my problem, but figured it was worth a shot.
Anyway, putting what I’ve got from my script back into the context of the code we’ve been using, this is the simplest version of the solution I’ve found. It works until you try to open another scene file, and then it crashes.
2 questions (again in the simplest form possible):
Why does it happen? How do I fix it?
global ContainerAttrib = attributes ContainerAttrib attribID:#(0x1f05aab6, 0x7a59c3a6)
(
local handler
fn getNode = custattributes.getowner this
fn constructNode node: = if not (theHold.Redoing() or theHold.Holding()) do
(
if node == unsupplied do node = getNode()
if isvalidnode node and not isdeleted node and handler == undefined do
(
handler = when node deleted node do if (attr = node.custattributes[#ContainerAttrib]) != undefined do
(
if attr.handler != undefined do deleteChangeHandler attr.handler
attr.handler = undefined
delete (for n in attr.container where isvalidnode n and not isdeleted n collect n)
attr.container = #()
)
)
)
parameters params
(
container type:#nodeTab tabsizevariable:on
on container set val do constructNode()
on container tabChanged change do
(
if change == #refDeleted do
(
owner = custAttributes.getOwner this
if handler != undefined do
(
if (isValidNode owner) and (not isDeleted owner) do
(
delete owner
)
)
)
)
)
on create do constructNode()
on update do constructNode()
on load do constructNode()
)
(
num = [1,2,4]
for i=1 to num[1] do
(
gc()
delete objects
for k=1 to num[2] do
(
y = (k-1)*20
global b = dummy name:(uniquename "master") pos:[0,y,0]
custattributes.add b ContainerAttrib baseObject:false
b.container = for k = 1 to num[3] collect (point pos:[k*20,y,0] wirecolor:orange)
)
)
print "DONE!"
ok
)
So okay, I wanted to test out getting rid of the custom attributes before closing the scene to see if that would solve the problem. But… I’m a bit confused about how that is even done. I’d been under the impression that was what custAttributes.deleteDef was for.
First, I found that even after deleting all scene nodes
custAttributes.deleteDef ContainerAttrib
returns
Runtime error: custAttributes.deleteDef - Cannot delete, objects still using this definition: <AttributeDef:ContainerAttrib>
Which doesn’t particularly make sense.
So, I checked out another thread about deleting custom attributes to see if I could find a solution (Denis, you may recognize this one) at http://forums.cgsociety.org/showthread.php?t=1014476
I tried testing out the final script that was posted there, but the attribute definition is still there after it’s deleted. Huh??? How is that even a thing? D:
Apparently, for whatever reason, this is normal behavior for Max, so it looks like I won’t be ableto use this as a solution.
So I’ve stripped it down as far as I can and the problem remains. Why does this work until I try to load a new scene, and then crash?
global ContainerAttrib = attributes ContainerAttrib attribID:#(0x1f05aab6, 0x7a59c3a6)
(
parameters params
(
cont type:#nodeTab tabsizevariable:on
on cont tabChanged change do
(
if change == #refDeleted do
(
owner = custAttributes.getOwner this()
if owner != undefined do
(
if (isValidNode owner) and (not isDeleted owner) do
(
delete owner
)
)
)
)
)
)
gc()
delete objects
for k = 1 to 2 do
(
y = (k-1)*20
global b = dummy name:(uniquename "master") pos:[0,y,0]
custattributes.add b ContainerAttrib baseObject:false
b.cont = for k = 1 to 4 collect (point pos:[k*20,y,0] wirecolor:orange)
)
you code tries to delete some nodes which are is about to be deleted by system… it happens on some scene events… brief look at the problem gave this list of events and i disable “auto deletion feature of the CA at the moment of PRE action”
global PreSystem = off
global ContainerAttrib = attributes ContainerAttrib attribID:#(0x1f05aab6, 0x7a59c3a6)
(
parameters params
(
cont type:#nodeTab tabsizevariable:on
on cont tabChanged change do if not PreSystem do
(
if change == #refDeleted do
(
owner = custAttributes.getOwner this
if (isValidNode owner) and (not isDeleted owner) do
(
delete owner
)
)
)
)
)
fn PreSystemCallback event:#pre =
(
PreSystem = (event == #pre)
)
callbacks.removescripts id:#pre_system_flag
callbacks.addscript #filePreOpen "PreSystemCallback event:#pre" id:#pre_system_flag
callbacks.addscript #systemPreNew "PreSystemCallback event:#pre" id:#pre_system_flag
callbacks.addscript #systemPreReset "PreSystemCallback event:#pre" id:#pre_system_flag
callbacks.addscript #preSystemShutdown "PreSystemCallback event:#pre" id:#pre_system_flag
callbacks.addscript #filePostOpen "PreSystemCallback event:#post" id:#pre_system_flag
callbacks.addscript #systemPostNew "PreSystemCallback event:#post" id:#pre_system_flag
callbacks.addscript #systemPostReset "PreSystemCallback event:#post" id:#pre_system_flag
callbacks.addscript #postSystemShutdown "PreSystemCallback event:#post" id:#pre_system_flag
gc()
delete objects
for k = 1 to 2 do
(
y = (k-1)*20
global b = dummy name:(uniquename "master") pos:[0,y,0]
custattributes.add b ContainerAttrib baseObject:false
b.cont = for k = 1 to 4 collect (point pos:[k*20,y,0] wirecolor:orange)
)
I… actually understand what you did there, which kind of blows my mind. But I definitely would never have figured that out on my own.
So it was either the same problem as with “crash on redo” or else very closely related?
that’s a little different. in both cases some nodes are in “predeleted” stage, and cannot be simple delete with “delete” function. both are “predeleted” but are “predeleted” differently.
i don’t catch the state in you case yet.
Just want to point some things out really quick, some obvious and some… very weird.
If you save the scene this script creates, restart max, and load the scene up again, deleting any of the point objects throws
-- Error occurred in cont.tabChanged()
-- Frame:
-- PreSystem: undefined
-- change: #refDeleted
-- owner: undefined
>> MAXScript Scripted Plugin Handler Exception:
-- No ""not"" function for undefined <<
Trying to run the script again once this error has been thrown causes the same error to be thrown repeatedly. This persists even after creating a new scene.
Running this portion of the code prior to loading the scene returns functionality:
global PreSystem = off
fn PreSystemCallback event:#pre =
(
PreSystem = (event == #pre)
)
callbacks.removescripts id:#pre_system_flag
callbacks.addscript #filePreOpen "PreSystemCallback event:#pre" id:#pre_system_flag
callbacks.addscript #systemPreNew "PreSystemCallback event:#pre" id:#pre_system_flag
callbacks.addscript #systemPreReset "PreSystemCallback event:#pre" id:#pre_system_flag
callbacks.addscript #preSystemShutdown "PreSystemCallback event:#pre" id:#pre_system_flag
callbacks.addscript #filePostOpen "PreSystemCallback event:#post" id:#pre_system_flag
callbacks.addscript #systemPostNew "PreSystemCallback event:#post" id:#pre_system_flag
callbacks.addscript #systemPostReset "PreSystemCallback event:#post" id:#pre_system_flag
callbacks.addscript #postSystemShutdown "PreSystemCallback event:#post" id:#pre_system_flag
Now, going back for a second… I understand why the error occurs initially, that much makes total sense. But I don’t get why the script can no longer be run after that has happened.
To make things even more bizarre, if you run the script one block at a time, no error will be thrown, and the scene will (appear to) work properly. BUT, the scene events in question will once again crash Max!!!
Could you double check if this also happens on your system??
you have the REDO issue. try to select and delete both master and its point, and do undo - redo… max crashes
that is one of the worst situations in the tool development… the bug depends on an order of selection.
try to select a point first and its master after. and delete, undo, redo…
So is that something that can be fixed with another callback?
EDIT:
Actually, I am guessing it has something to do with the fact that the parameter handler does not appear to actually be called on the redo action, at least as evidenced by the following:
on cont tabChanged change do if not PreSystem do
(
format "tabChanged
"
if change == #refDeleted do
(
owner = custAttributes.getOwner this
if (isValidNode owner) and (not isDeleted owner) do
(
delete owner
)
)
)
there are probably two separate problems:
#1 is trying to delete that is already deleted or marked by system for deletion
#2 is doing some fatal operations (including delete) after scene pre-reset (pre-reset, pre-new, pre-load, etc.)
i’m sure that there are another actions (maybe renaming…) that cause max crash the same way as deleting…
so it could be nice to know when scene is in the stage of “resetting” (after PRE and before POST).
Would setting it up as another handler get around the problem?
Or since it only happens when the items are selected in a particular order, have some way to make it reorder the selection, maybe?