Objects replace


#1

Hello,
I am trying to adapt a script found on this forum Replacing Objects
it works great when objects are in the “maxfilepath” but i try to add a custom folder path to it, it’s not working as i would like, it kind of work sometimes but after a 3dsmax restart it’s not working anymore, if some of you has any clue on what i am doing wrong here is on of my many attempt to make this work :

– Create the rollout for the interface
rollout replaceObjectsRollout “Replace Objects” width: 250 height: 200
(

group "Browse and Replace Objects" 
(   
    button replaceButton "Replace" width:200 height:30 pos:[13,145] 
	button btn_browse "Browse..." width:100 height:20 pos:[13,45] 
	editText edt_path "Export Location:" labelOnTop:true		
)

fn deselectDuplicates =
(
local allNames = #()
local duplicateObjs = #()
for obj in selection do
(
if findItem allNames obj.name == 0 then append allNames obj.name
else append duplicateObjs obj
)
deselect duplicateObjs
)

fn replaceSelectedObjects objNamePattern =
(
– get selected objects
selObjs = selection as array

-- loop through selected objects and replace them with corresponding name
for obj in selObjs do
(
    if findString obj.name objNamePattern != undefined  do
    (
        print ("Replacing object: " + obj.name)
        objectReplace obj.name
    )
)

)

fn objectReplace name all:on =
(
print ("Replacing object " + name)
undo “object replace” on

act = off
pattern = if all then (name + "*") else name

-- collect all nodes with the name or which name match the pattern <name>* (name_01, name 02, etc. )  
nodes = for obj in objects where matchpattern obj.name pattern:pattern collect obj


-- replace file has to be in the same directory and has the name of object to replace
file = edt_path.text + "\\" + name + ".max"
if nodes.count > 0 and doesfileexist file do
(
    clearSelection()
    -- merge only object with the name
    mergeMaxFile file #(name) #select #mergeDups #neverReparent #useMergedMtlDups  quiet:on
    if isvalidnode (node = selection[1]) do
    (
        -- replace all old instances with new one
        for obj in nodes do (
                                instancereplace obj node
                                obj.mat = node.mat
                            )
        
        delete node
        act = on
    )
)
print ("Object replace " + (if act then "succeeded" else "failed"))
act

)

on btn_browse pressed do
(
newpath = getSavePath()
edt_path.text = newpath
print newpath
)–end do

on replaceButton pressed do
(

	deselectDuplicates ()

	replaceSelectedObjects ""	
		
)

)

– Create the dialog for the interface
createDialog replaceObjectsRollout


#2

why is my post so messy, i don’t know how to post the code in a sub window as other post.


#3

You need to put the code between tags:
[code]
your code here…
[/code]


#4

thank you , i try to edit the post but i get “403 Forbidden” so i am reposting the code :

– Create the rollout for the interface
rollout replaceObjectsRollout “Replace Objects” width: 250 height: 200
(

group "Browse and Replace Objects" 
(   
    button replaceButton "Replace" width:200 height:30 pos:[13,145] 
	button btn_browse "Browse..." width:100 height:20 pos:[13,45] 
	editText edt_path "Export Location:" labelOnTop:true		
)

fn deselectDuplicates =
(
local allNames = #()
local duplicateObjs = #()
for obj in selection do
(
if findItem allNames obj.name == 0 then append allNames obj.name
else append duplicateObjs obj
)
deselect duplicateObjs
)

fn replaceSelectedObjects objNamePattern =
(
– get selected objects
selObjs = selection as array

-- loop through selected objects and replace them with corresponding name
for obj in selObjs do
(
    if findString obj.name objNamePattern != undefined  do
    (
        print ("Replacing object: " + obj.name)
        objectReplace obj.name
    )
)

)

fn objectReplace name all:on =
(
print ("Replacing object " + name)
undo “object replace” on

act = off
pattern = if all then (name + "*") else name

-- collect all nodes with the name or which name match the pattern <name>* (name_01, name 02, etc. )  
nodes = for obj in objects where matchpattern obj.name pattern:pattern collect obj


-- replace file has to be in the same directory and has the name of object to replace
file = edt_path.text + "\\" + name + ".max"
if nodes.count > 0 and doesfileexist file do
(
    clearSelection()
    -- merge only object with the name
    mergeMaxFile file #(name) #select #mergeDups #neverReparent #useMergedMtlDups  quiet:on
    if isvalidnode (node = selection[1]) do
    (
        -- replace all old instances with new one
        for obj in nodes do (
                                instancereplace obj node
                                obj.mat = node.mat
                            )
        
        delete node
        act = on
    )
)
print ("Object replace " + (if act then "succeeded" else "failed"))
act

)

on btn_browse pressed do
(
newpath = getSavePath()
edt_path.text = newpath
print newpath
)–end do

on replaceButton pressed do
(

	deselectDuplicates ()

	replaceSelectedObjects ""	
		
)

)

– Create the dialog for the interface
createDialog replaceObjectsRollout

#5

to describe a bit more what the problem is, if i save the file in the same folder as the replacements objects are, then it’s working even if i have removed “maxfilepath” and replaced it with the browser, if i move the files and browse to this new location it works, but if i restart 3dsmax than it’s not working again, and have an error at the line " objectReplace obj.name", then if i save my file back to where the replacement object are its working again, i can even move the files and browse to the new location and it works, it is very confusing, i read somewhere i don’t remenber where…that “mergeMaxFile” by default search for files in “maxfilepath” so it might be a reason for the fact that it works even if i remove the “maxfilepath” and replace with the browser but i can’t understand why i can get the browser to work properly, thanks to anyone who have could provide some knowledge , i can’t understand why it works on severall occasion and stop working after a reboot…


#6

First of all, clearly describe what you want to accomplish. Write, so to speak, a Technical Specification where the conditions and objectives will be outlined point by point.

My objectReplace function was suggested for a specific case, and I’m not sure if it fits your needs well.


#7

Hello Denis, thank you very much for taking time to respond.
this script would be intended to replace object base on their name, i came across this thread Replacing Objects where you posted some code that does exactly what i need .
Thank you very much, it’s almost the perfect solution for what i have to do.
I have to replace lots of bad looking objects whith brand new good looking ones . since the ugly ones are not all the same kind its not easy to replace them, but they are instanced.
i build myself an instance renamer, and prepare the goodlooking ones with corresponding names and also did a little script to place them at the origin with their pivot at origin and “save select” them on individual files, then with the help of your script i scatter them across the scene and it works nicely.

For convinience purpose, i was trying to implement an option to browse to any folder where the reference meshes (the goodlooking one) would be located. at first it wasn’t very fast as it was merging the new meshes for every objects that has a corresponding name, in my case all object that has the same name are basicly the same objects so i added a little function to deselect object sharing the same name to have only one object of each “kind” in selection, it speeded up the replacement a lot cause it merges only one object of each kind and the instance replace by name scatter them on the position of the old ones and delete ugly ones, for the rest i kept most of your code and add another part for the browser.
i already used the browsing part on other script and managed to make it work but here it’s kind of driving me crazy, it works fine than i reboot max and nothing works anymore .
when it stop working, i often try to save my file to the place where reference objects are located, than 3dsmax find the files and it works back.
then i try to save to an other location and it works so i think that the few changes i made to the code has finaly paid of, but when i restart max , nothing works anymore, it’s been three days that i am in this nightmare loop… i think it 's apparently working but in fact it’s not it’s just that 3dsmax must have been keeping somewhere in its memory the location of those files and what ever folder i browse to doesn’t really matter,
when i reboot max is fresh and have no clue about the location of the files i try to reach , and the loop start again.

i think the path is not visible to the function, i manage to make a version of the script that work on the browing side, you can choose a location and 3dsmax merge the objects base on their name and on the location of my choose, but in this version it’s the instance replace that fail and every object end up at 0, 0, 0.
on the other hand i have a version of the script that works at replacing and instancing object across the scene by their name but without option to select a folder…i am quite new to scripting and have a huge lack of knowledge but i try to learn more every day by doing those kind of project, no better way to learn than to try and fail and retry…but here i’am stucked and need some kind of advice , help would be much appreciated.

ps: by reading the code i posted it seems that in this version the selection filter at the begining is not working but i have one that works fine i just remove the local array put them out of the rollout and remove the “local”, and it works just fine it is the browser that causes me headache…


#8

here is the code that works on the browser part, even on a fresh 3dsmax, but fail at instance replace


allNames = #()
duplicateObjs = #()

for obj in objects do
(
    if findItem allNames obj.name == 0 then append allNames obj.name 
    else append duplicateObjs obj
)

deselect duplicateObjs

oldobj = #{}
for obj in objects do append oldobj (getHandleByAnim obj)
print oldobj

fn replaceSelectedObjects objNamePattern =
(
    
    selObjs = selection as array

    
    for obj in selObjs do
    (
        if findString obj.name objNamePattern != undefined do
        (
            print ("Replacing object: " + obj.name)

            newPath = getSavePath()
			
            if newPath == undefined do exit


for o in (getfiles (newPath + "\\*.max")) collect
(
    execute ("mergeMaxFile \"" + o + "\" #mergeDups #neverReparent #useMergedMtlDups quiet:on")
    
)

  	newobj = for obj in objects where not oldobj[getHandleByAnim obj] collect obj	
		
print newobj


         
            for newObj in newObjs do
            (
              
                oldObjs = for o in objects where o.name == obj.name and o != obj collect o
                for oldObj in oldObjs do
                (
                    
                    instancereplace oldObj newObj
					
                    oldObj.mat = newObj.mat
                )
            )

            
            delete obj
        )
    )
)

replaceSelectedObjects ""

#9

try(destroydialog ReplaceObjectsRol) catch()
rollout ReplaceObjectsRol "RO:71E91E30" width:191
(
	local recent_targetfile = unsupplied 
	local target_nodename = ""
	
	group "Target: "
	(
		button browse_bt "Target File..." width:90 align:#left offset:[-4,0]
		edittext trgfile_tx width:172 align:#left offset:[-6,0]
		
		label pattern_lb "Target Node Name:" align:#left offset:[-4,4]
		edittext trgname_tx width:172 align:#left offset:[-6,0]
	)
	group "Source: "
	(
		radiobuttons source_nodes_rb "Replace Nodes:" labels:#("All By Name", "Selected") columns:1 align:#left offset:[0,0]
		button replace_bt "Replace Objects" width:172 align:#left offset:[-4,6] 
		button delmerged_bt "Delete Last Merged Nodes" width:172 align:#left offset:[-4,6] 
	)
	
	on browse_bt pressed do 
	(
		filename = getMAXOpenFileName filename:recent_targetfile
		--file = getOpenFileName caption:"Pick Target File" types:"MAX file(*.max)|*.max"
		
		if (filename != undefined) do
		(
			trgfile_tx.text = filenameFromPath filename
			recent_targetfile = trgfile_tx.tooltip = filename
		)
	)		
	
	on trgname_tx entered text do
	(
		target_nodename = text
	)
	
	fn replaceNodes nodes file: nodename: = 
	(
		act = off
		if nodes.count > 0 and iskindof file String and doesfileexist file do
		(
			mergeMaxFile file #(nodename as name) #mergeDups #neverReparent #useMergedMtlDups quiet:on
			merged = getLastMergedNodes()
			--format ">> merged >> %\n" merged
			
			if merged.count > 0 do undo "X-Replace" on
			(
				target = merged[1]
				--format ">> target >> %\n" target
				instancereplace nodes target
				delete target
				act = on
			)
		)
		act
	)
	
	on replace_bt pressed do 
	(
		source_nodes = if source_nodes_rb.state == 1 then
		(
			for node in objects where matchpattern node.name pattern:(target_nodename + "*") collect node
		)
		else
		(
			selection as array
		)
		-- format ">> %\n" source_nodes
		
		if source_nodes.count do
		(
			replaceNodes source_nodes file:recent_targetfile nodename:target_nodename 
		)
	)
	
	on delmerged_bt pressed do undo "Delete Last Merged"on 
	(
		try (delete (getLastMergedNodes())) catch()
	)
	on ReplaceObjectsRol open do
	(
	)
)
createdialog ReplaceObjectsRol

Try this and feel free to ask questions

  1. Specify target file (to merge from)

  2. Specify node name to be merged

  3. Two options for nodes being replaced: All - nodes are matching the pattern “name*”, or selected (ignoring name)

  4. Replace (because merge process deletes undo history only replace action itself is undoable)

  5. If it needs undo - Try to delete recently merged nodes.


#10

try(destroydialog ReplaceObjectsRol) catch()
rollout ReplaceObjectsRol "Replace Objects" width:191
(
	local recent_targetfile = unsupplied 
	local target_nodename = ""
	
	group "Target: "
	(
		edittext trgfile_tx width:148 align:#left offset:[-6,0] readonly:on across:2
		button browse_bt "..." width:22 align:#right offset:[4,-2] \
			tooltip:"Pick Target File\n   RC\t- Load target names"
		dropdownlist trgname_dd width:172 align:#left offset:[-4,0]
	)
	group "Source: "
	(
		radiobuttons source_nodes_rb "Nodes:" labels:#("By Name", "Selected") columns:2 align:#left offset:[0,-2] offsets:#([38,-16],[4,-16])
		checkbutton replace_bt "Replace" width:172 align:#left offset:[-4,4] 
	)
	group ""
	(
		button delmerged_bt "Delete Last Merged" width:172 align:#left offset:[-4,-6] 
	)
	
	on browse_bt pressed do 
	(
		filename = getOpenFileName filename:recent_targetfile caption:"Pick Target File" types:"MAX file(*.max)|*.max"
		
		if (filename != undefined) do
		(
			trgfile_tx.text = filenameFromPath filename
			recent_targetfile = trgfile_tx.tooltip = filename
		)
	)
	
	on browse_bt rightclick do if iskindof (file = recent_targetfile) String do
	(
		if doesfileexist file do
		(
			names = getmaxfileobjectnames file quiet:on
			sort names
			trgname_dd.items = names
			if names.count do 
			(
				trgname_dd.selection = 1 
				target_nodename = trgname_dd.selected
			)
		)
	)
	
	on trgname_dd selected sel do
	(
		target_nodename = trgname_dd.selected
	)
	
	fn replaceNodes nodes file: nodename: = 
	(
		act = off
		if nodes.count > 0 and iskindof file String and doesfileexist file do
		(
			mergeMaxFile file #(nodename as name) #mergeDups #neverReparent #useMergedMtlDups quiet:on
			merged = getLastMergedNodes()
			--format ">> merged >> %\n" merged
			
			if merged.count > 0 do undo "X-Replace" on
			(
				target = merged[1]
				--format ">> target >> %\n" target
				InstanceMgr.MakeObjectsUnique nodes #individual
				instancereplace nodes target
				delete target
				act = on
			)
		)
		act
	)
	
	on replace_bt changed state do 
	(
		if state do with redraw off
		(
			source_nodes = if source_nodes_rb.state == 1 then
			(
				for node in objects where matchpattern node.name pattern:(target_nodename + "*") collect node
			)
			else
			(
				selection as array
			)
			-- format ">> %\n" source_nodes
			
			if source_nodes.count do
			(
				replaceNodes source_nodes file:recent_targetfile nodename:target_nodename 
			)
		)
		replace_bt.state = off
	)
	
	on delmerged_bt pressed do undo "Delete Last Merged"on 
	(
		try (delete (getLastMergedNodes())) catch()
	)
	
	on ReplaceObjectsRol open do
	(
	)
)
createdialog ReplaceObjectsRol

An advanced version (check the difference yourself).


#11

Thanks very much, it is working fine , but it’s targeting only one file and one node and the material from the old object is applied to the new object this should not be to much complicate to fix the material problem. if it could target multiple files or multiple nodes in one files it would be exactly what i need, but as it is, the other code you did was closer to my needs since i need to replace a lot of different objects of different kinds at the same time, i have a version of your code that i’ve modified just a tiny bit that works almost exactly as i need exept that it lacks the browser , here is the modified version of your code, it is very fast at replacing multiple kind of object present in multiple files in one go, it’s almost the perfect solution for my needs.


allNames = #()
duplicateObjs = #()

	
fn deselectDuplicates =
    (
        
        for obj in selection do
(
	if findItem allNames obj.name == 0 then append allNames obj.name 
	else append duplicateObjs obj
)

deselect duplicateObjs

    )

fn replaceSelectedObjects objNamePattern =
(
    -- get selected objects
    selObjs = selection as array

    -- loop through selected objects and replace them with corresponding name
    for obj in selObjs do
    (
        if findString obj.name objNamePattern != undefined  do
        (
            print ("Replacing object: " + obj.name)
            objectReplace obj.name
        )
    )
)


fn objectReplace name all:on  = 
( 
    print ("Replacing object " + name)
    undo "object replace" on
    deselectDuplicates ()
    act = off
    pattern = if all then (name + "*") else name
    
    -- collect all nodes with the name or which name match the pattern <name>* (name_01, name 02, etc. )  
    nodes = for obj in objects where matchpattern obj.name pattern:pattern collect obj
   
    
    -- replace file has to be in the same directory and has the name of object to replace
    file = maxfilepath + name + ".max"
    if nodes.count > 0 and doesfileexist file do
    (
        clearSelection()
        -- merge only object with the name
        mergeMaxFile file #(name) #select #mergeDups #neverReparent #useMergedMtlDups  quiet:on
        if isvalidnode (node = selection[1]) do
        (
            -- replace all old instances with new one
            for obj in nodes do (
                                    instancereplace obj node
                                    obj.mat = node.mat
                                )
            
            delete node
            act = on
        )
    )
    print ("Object replace " + (if act then "succeeded" else "failed"))
    act
)




-- Create the rollout for the interface
rollout replaceObjectsRollout "Replace Objects" width: 310 height: 80
(
    
    group "Replace Objects" 
    (
        
        
        button replaceButton "Replace" width:200 height:30 align:#center
        
    )
    
    -- Define the button actions
   
    
   on replaceButton pressed do
(
	deselectDuplicates ()
	replaceSelectedObjects ""
    
)
)
-- Create the dialog for the interface
createDialog replaceObjectsRollout

how would you implement the browser in this code? i tried with getSavePath but as i mention earlier i fail at making it work…
or how would make the script you’ve just send me to target multiple files or multiple nodes in one files? i’ll try the last one you’ve send it might be doing so :slight_smile: thanks for your help it’s much appreciated


#12

i noticed “if nodes.count > 0” in your code, how do you specify multiple nodes?, the first code you sent worked well exept for the material, but the second ones destroyed all objects in the scene :slight_smile: it created a sort of modern art sculpture it’s really cool looking, it has replace all objects by a multitude of frame like objects, very creative script :wink:


#13

i fixed the material from the first script you’ve send, i did the same as you did back in 2009 here Replacing Objects
here is the modified code :


try(destroydialog ReplaceObjectsRol) catch()
rollout ReplaceObjectsRol "RO:71E91E30" width:191
(
	local recent_targetfile = unsupplied 
	local target_nodename = ""
	
	group "Target: "
	(
		button browse_bt "Target File..." width:90 align:#left offset:[-4,0]
		edittext trgfile_tx width:172 align:#left offset:[-6,0]
		
		label pattern_lb "Target Node Name:" align:#left offset:[-4,4]
		edittext trgname_tx width:172 align:#left offset:[-6,0]
	)
	group "Source: "
	(
		radiobuttons source_nodes_rb "Replace Nodes:" labels:#("All By Name", "Selected") columns:1 align:#left offset:[0,0]
		button replace_bt "Replace Objects" width:172 align:#left offset:[-4,6] 
		button delmerged_bt "Delete Last Merged Nodes" width:172 align:#left offset:[-4,6] 
	)
	
	on browse_bt pressed do 
	(
		filename = getMAXOpenFileName filename:recent_targetfile
		--file = getOpenFileName caption:"Pick Target File" types:"MAX file(*.max)|*.max"
		
		if (filename != undefined) do
		(
			trgfile_tx.text = filenameFromPath filename
			recent_targetfile = trgfile_tx.tooltip = filename
		)
	)		
	
	on trgname_tx entered text do
	(
		target_nodename = text
	)
	
	fn replaceNodes nodes file: nodename: = 
	(
		act = off
		if nodes.count > 0 and iskindof file String and doesfileexist file do
		(
			mergeMaxFile file #(nodename as name) #mergeDups #neverReparent #useMergedMtlDups quiet:on
			merged = getLastMergedNodes()
			--format ">> merged >> %\n" merged
			
			if merged.count > 0 do undo "X-Replace" on
			(
				target = merged[1]
				--format ">> target >> %\n" target
				instancereplace nodes target
				nodes.mat = target.mat
				delete target
				act = on
			)
		)
		act
	)
	
	on replace_bt pressed do 
	(
		source_nodes = if source_nodes_rb.state == 1 then
		(
			for node in objects where matchpattern node.name pattern:(target_nodename + "*") collect node
		)
		else
		(
			selection as array
		)
		-- format ">> %\n" source_nodes
		
		if source_nodes.count do
		(
			replaceNodes source_nodes file:recent_targetfile nodename:target_nodename 
		)
	)
	
	on delmerged_bt pressed do undo "Delete Last Merged"on 
	(
		try (delete (getLastMergedNodes())) catch()
	)
	on ReplaceObjectsRol open do
	(
	)
)
createdialog ReplaceObjectsRol

but for what i am working on, there is a bit to much human intervention, as i need to select file by file and typing the name of the target node, the other script take every thing at once and does it very fast, the browser was like the cherry on top of the cake, i still keep hope of getting it to work, but it is already quite good…


#14

try(destroydialog ReplaceObjectsRol) catch()
rollout ReplaceObjectsRol "Replace Objects" width:191
(
	local recent_targetfile = unsupplied 
	local target_nodename = ""
	
	group "Target: "
	(
		edittext trgfile_tx width:148 align:#left offset:[-6,0] readonly:on across:2
		button browse_bt "..." width:22 align:#right offset:[4,-2] \
			tooltip:"Pick Target File\n   RC\t- Load target names"
		dropdownlist trgname_dd width:172 align:#left offset:[-4,0]
	)
	group "Source: "
	(
		radiobuttons source_nodes_rb "Nodes:" labels:#("By Name", "Selected") columns:1 align:#left offset:[40,-2] offsets:#([38,-16],[38,-16])
		radiobuttons replace_mode_rb "Method:" labels:#("Copy", "Instance", "Reference") columns:1 align:#left offset:[34,0] offsets:#([44,-16],[44,-16],[44,-16])
		checkbutton replace_bt "Replace" width:172 align:#left offset:[-4,4] 
	)
	group ""
	(
		button delmerged_bt "Delete Last Merged" width:172 align:#left offset:[-4,-6] 
	)
	
	on browse_bt pressed do 
	(
		filename = getOpenFileName filename:recent_targetfile caption:"Pick Target File" types:"MAX file(*.max)|*.max"
		
		if (filename != undefined) do
		(
			trgfile_tx.text = filenameFromPath filename
			recent_targetfile = trgfile_tx.tooltip = filename
		)
	)
	
	on browse_bt rightclick do if iskindof (file = recent_targetfile) String do
	(
		if doesfileexist file do
		(
			names = getmaxfileobjectnames file quiet:on
			sort names
			trgname_dd.items = names
			if names.count do 
			(
				trgname_dd.selection = 1 
				target_nodename = trgname_dd.selected
			)
		)
	)
	
	on trgname_dd selected sel do
	(
		target_nodename = trgname_dd.selected
	)
	
	fn replaceNodes nodes file: nodename: method:1 = 
	(
		act = off
		if nodes.count > 0 and iskindof file String and doesfileexist file do
		(
			mergeMaxFile file #(nodename as name) #mergeDups #neverReparent #useMergedMtlDups quiet:on
			merged = getLastMergedNodes()
			--format ">> merged >> %\n" merged
			
			if merged.count > 0 do undo "X-Replace" on
			(
				target = merged[1]
				--format ">> target >> % method:%\n" target method
				InstanceMgr.MakeObjectsUnique nodes #individual
				
				case method of
				(
					1:
					(
						instancereplace nodes target
						InstanceMgr.MakeObjectsUnique nodes #individual
					)
					2:  
					(
						instancereplace nodes target
					)
					3:
					(
						referencereplace nodes target
					)
				)
				nodes.mat = target.mat
				delete target
				act = on
			)
		)
		act
	)
	
	on replace_bt changed state do 
	(
		if state do with redraw off
		(
			source_nodes = if source_nodes_rb.state == 1 then
			(
				for node in objects where matchpattern node.name pattern:(target_nodename + "*") collect node
			)
			else
			(
				selection as array
			)
			-- format ">> %\n" source_nodes
			
			if source_nodes.count do
			(
				replaceNodes source_nodes file:recent_targetfile nodename:target_nodename method:replace_mode_rb.state
			)
		)
		replace_bt.state = off
	)
	
	on delmerged_bt pressed do undo "Delete Last Merged"on 
	(
		try (delete (getLastMergedNodes())) catch()
	)
	
	on ReplaceObjectsRol open do
	(
	)
)
createdialog ReplaceObjectsRol

#15

maybe just use XRef Objects?


try(destroydialog ReplaceObjectsRol) catch()
rollout ReplaceObjectsRol "Replace Objects" width:191
(
	local recent_targetfile = unsupplied 
	local target_nodename = ""
	
	group "Target: "
	(
		edittext trgfile_tx width:148 align:#left offset:[-6,0] readonly:on across:2
		button browse_bt "..." width:22 align:#right offset:[4,-2] \
			tooltip:"Pick Target File\n   RC\t- Load target names"
		dropdownlist trgname_dd width:172 align:#left offset:[-4,0]
	)
	group "Source: "
	(
		radiobuttons source_nodes_rb "Nodes:" labels:#("By Name", "Selected") columns:1 align:#left offset:[40,-2] offsets:#([38,-16],[38,-16])
		radiobuttons replace_mode_rb "Method:" labels:#("Copy", "Instance", "Reference", "XRef Object") columns:1 align:#left offset:[34,0] \
			offsets:#([44,-16],[44,-16],[44,-16],[44,-16])
		checkbutton replace_bt "Replace" width:172 align:#left offset:[-4,4] 
	)
	group ""
	(
		button delmerged_bt "Delete Last Merged" width:172 align:#left offset:[-4,-6] 
	)
	
	on browse_bt pressed do 
	(
		filename = getOpenFileName filename:recent_targetfile caption:"Pick Target File" types:"MAX file(*.max)|*.max"
		
		if (filename != undefined) do
		(
			trgfile_tx.text = filenameFromPath filename
			recent_targetfile = trgfile_tx.tooltip = filename
		)
	)
	
	on browse_bt rightclick do if iskindof (file = recent_targetfile) String do
	(
		if doesfileexist file do
		(
			names = getmaxfileobjectnames file quiet:on
			sort names
			trgname_dd.items = names
			if names.count do 
			(
				trgname_dd.selection = 1 
				target_nodename = trgname_dd.selected
			)
		)
	)
	
	on trgname_dd selected sel do
	(
		target_nodename = trgname_dd.selected
	)
	
	fn replaceNodes nodes file: nodename: method:1 = 
	(
		act = off
		if nodes.count > 0 and iskindof file String and doesfileexist file do
		(
			target = undefined
			if method == 4 then
			(
				target = xrefs.addnewxrefobject file nodename dupMtlNameAction:#useXRefed reparentAction:#neverReparent
				--objxrefmgr.addxrefitemsfromfile file objnames:#(name)
			)
			else
			(
				mergeMaxFile file #(nodename as name) #mergeDups #neverReparent #useMergedMtlDups quiet:on
				merged = getLastMergedNodes()
				target = merged[1]
				--format ">> merged >> %\n" merged
			)
			if target != undefined do undo "X-Replace" on
			(
				--format ">> target >> % method:%\n" target method
				InstanceMgr.MakeObjectsUnique nodes #individual
				
				case method of
				(
					1:
					(
						instancereplace nodes target
						InstanceMgr.MakeObjectsUnique nodes #individual
					)
					2:  
					(
						instancereplace nodes target
					)
					3:
					(
						referencereplace nodes target
					)
					4:  
					(
						instancereplace nodes target
					)
				)
				nodes.mat = target.mat
				delete target
				act = on
			)
		)
		act
	)
	
	on replace_bt changed state do 
	(
		if state do with redraw off
		(
			source_nodes = if source_nodes_rb.state == 1 then
			(
				for node in objects where matchpattern node.name pattern:(target_nodename + "*") collect node
			)
			else
			(
				selection as array
			)
			-- format ">> %\n" source_nodes
			
			if source_nodes.count do
			(
				replaceNodes source_nodes file:recent_targetfile nodename:target_nodename method:replace_mode_rb.state
			)
		)
		replace_bt.state = off
	)
	
	on delmerged_bt pressed do undo "Delete Last Merged"on 
	(
		try (delete (getLastMergedNodes())) catch()
	)
	
	on ReplaceObjectsRol open do
	(
	)
)
createdialog ReplaceObjectsRol

#16

I have mixed feeling about xrefobjects, it was kind of buggy in the past but i’ll give it a try, your script is working very nicely. at first, i didn’t get the right click to refresh the targets node list, maybe a refresh button instead would be a nice addition.
for what i need it’s a little bit to much work since i have to manualy select from the scroll menu a name than click replace but for 100’s different objects it’s a bit like working in a factory… for my use it needs to be less fancy, but it could be very helpfull in other case scenario, i’ll use it on other projects, but here i just need to take all objects from a file and merge and “batch” replace only those that share the same name with objects already in the scenes, it needs to be automated, like the script from 2009 did, i’ll try to modify your script to do that, the browser works well and maybe i can skip the scrolling menu and take every objects in the scenes and filter by names the ones i need depending on the matching names already present in the scene, or maybe keep the script as it is but add an option “target all nodes” anyway thanks again for your help :slight_smile:


#17

try(destroydialog ReplaceObjectsRol) catch()
rollout ReplaceObjectsRol "Replace Objects" width:191
(
	local recent_targetfile = unsupplied 
	local target_nodename = ""
	local auto_collect = true
	local multi_target = false
	
	local src_selected = false 
	
	group "Target: "
	(
		edittext trgfile_tx width:148 align:#left offset:[-6,0] readonly:on across:2
		button browse_bt "..." width:22 align:#right offset:[4,-2] \
			tooltip:"Pick Target File\n   RC\t- Load target names"
		multiListBox trgname_ms width:168 height:20 align:#left offset:[-2,0]
	)
	group "Source: "
	(
		checkbox use_selected_ch "Use Node Selection" align:#left offset:[0,0] tooltip:"Only for single target"

		checkbox xrefobjects_ch "Make XRef Objects" align:#left offset:[20,4]
		checkbox make_unique_ch "Make Unique" checked:on align:#left offset:[20,0]
		
		checkbutton replace_bt "Replace" width:172 align:#left offset:[-4,4] 
	)
	group ""
	(
		button delmerged_bt "Delete Last Merged" width:172 align:#left offset:[-4,-6] 
	)

	on trgname_ms selectionEnd do
	(
		sel = trgname_ms.selection
		use_selected_ch.enabled = (sel.numberset == 1)
		multi_target = (sel.numberset > 1)
	)
	on trgname_ms rightclick do trgname_ms.selection = if trgname_ms.items.count then #{1..trgname_ms.items.count} else #{}
	
	fn collectTargetList = if iskindof (file = recent_targetfile) String do
	(
		if doesfileexist file do
		(
			names = getmaxfileobjectnames file quiet:on
			sort names
			trgname_ms.items = names
			if names.count do 
			(
				trgname_ms.selection = 1 
				target_nodename = trgname_ms.selected
			)
		)
	)
	
	on use_selected_ch changed state do src_selected = state
	
	on browse_bt pressed do 
	(
		filename = getOpenFileName filename:recent_targetfile caption:"Pick Target File" types:"MAX file(*.max)|*.max"
		
		if (filename != undefined) do
		(
			trgfile_tx.text = filenameFromPath filename
			recent_targetfile = trgfile_tx.tooltip = filename
			
			if auto_collect do collectTargetList()
		)
	)
	
	on browse_bt rightclick do collectTargetList()
	
	local targets = #()
	
	fn replaceMultipleNodes src_nodes file: names: method:1 makeXRef:false unique:off = 
	(
		num = 0
		if src_nodes.count > 0 and iskindof file String and doesfileexist file do
		(
			target_names = for n in names collect (n as name)
			makeuniquearray target_names

			targets = #()
			
			if makeXRef then
			(
				targets = xrefs.addnewxrefobject file target_names dupMtlNameAction:#useXRefed reparentAction:#neverReparent
			)
			else
			(
				mergeMaxFile file target_names #mergeDups #neverReparent #useMergedMtlDups quiet:on
				targets = getLastMergedNodes()

				--format ">> merged >> %\n" merged
			)
			if targets.count > 0 do undo "X-Replace" on
			(
				--format ">> target >> % method:%\n" target method
				for target in targets do
				(
					k = finditem target_names (target.name as name) 
					if k != 0 do 
					(
						nodes = src_nodes[k]
						InstanceMgr.MakeObjectsUnique nodes #individual
						
						instancereplace nodes target
						if unique do InstanceMgr.MakeObjectsUnique nodes #individual
						nodes.mat = target.mat
						
						delete target
						
						num += 1
					)
				)
			)
		)
		num
	)
	
	on replace_bt changed state do 
	(
		if state do with redraw off
		(
			source_nodes = #()
			target_names = for k in trgname_ms.selection collect (trgname_ms.items[k] as name)
			makeuniquearray target_names
			
			if src_selected and not multi_target then
			(
				append source_nodes (selection as array)
			)
			else
			(
				source_nodes = for trg_name in target_names collect
				(
					for node in objects where matchpattern node.name pattern:((trg_name as string) + "*") collect node
				)
			)
			-- format ">> %\n" source_nodes

			if source_nodes.count do
			(
				replaceMultipleNodes source_nodes file:recent_targetfile names:target_names makexref:xrefobjects_ch.state unique:make_unique_ch.state
			)
		)
		replace_bt.state = off
	)
	
	on delmerged_bt pressed do undo "Delete Last Merged"on 
	(
		try (delete (getLastMergedNodes())) catch()
		--for target in targets where isvalidnode target do delete target
	)
	
	on ReplaceObjectsRol open do
	(
	)
)
createdialog ReplaceObjectsRol

here is another version with #multitargets
the last one… from here on out, you go on your own… :sunglasses:


#18

thank you !!!, it’s exactly what i needed, it 's funny because i just finally got the other script to work with the browser like 10 min ago i was super happy, but there was still an issue because it was prompting me to choose the folder one time per objects, it was kind a half victory… you did exactly the script that i needed so thank you again. i will try to understand what you did to learn a bit more :wink:


#19

I don’t how you manage to do that ,but it’s super fast i’ve tried on 30 objects just to test and it was almost
instantaneous. i couldn’t believe my eyes, with the older script for the same test it was around 40 sec here it’s maybe 3 sec, i am speechless…well done :slight_smile: thx