'Change Material/Map Type' in maxscript

'Change Material/Map Type' in maxscript

mxmiceliSGB2D
Explorer Explorer
6,690 Views
16 Replies
Message 1 of 17

'Change Material/Map Type' in maxscript

mxmiceliSGB2D
Explorer
Explorer

I've got what I'm hoping is a simple question that I cannot find the answer to online. I'm trying to do the following:

 

-Select a material (or multiple) in my scene by name with a wildcard name match

-Replace those materials with a material I specify from a material library on my computer

 

The manual equivalent would be:

-load the material library from the network

-select the material in your scene you want to replace

-right click that material

-click change material/map type

-select the new material in the material/map browser pop up

 

The goal is for revit or sketchup scenes I import into max I can run a script that finds all materials in the scene with 'chrome', or 'glass', or 'walnut wood' in the name and automatically swap them out for corresponding high quality vray materials I have already made and put in a material library.

 

I hope this is clear but if not I can try again to explain it differently.

0 Likes
Accepted solutions (2)
6,691 Views
16 Replies
Replies (16)
Message 2 of 17

denisT.MaxDoctor
Advisor
Advisor

the script itself is simple, but it needs more rules ...


for example,

# where to look for materials?
possible places: scene materials, material editor, everywhere ...

 

# should we also look for submaterials?

 

# do we need to search only specific classes?

 

etc.

 

 

So ... Make a clear specification, and you'll probably get the script 🙂

 

 

  

0 Likes
Message 3 of 17

mxmiceliSGB2D
Explorer
Explorer

Thanks for the feedback!

 

I suppose when looking for materials to replace I would only really need to look in scene materials but for the materials that are coming in to replace those, ideally I'd want them to be from a .mat file on my computer.

 

Yes I'd want to look for sub materials because I think any multi/sub-object material shouldn't need to change at all, since it retains the correct material->geometry mapping I'd want to leave that alone completely and only swap out the sub-materials.

 

Regarding class I'm not sure I know all the technical parts of that but would be looking for only materials, not textures or maps, and from revit I think it's almost always an 'autodesk material' and from sketchup it's a 'standard material'.

 

After thinking more about this though I'm not as sure the searching/wildcard aspect of this is as necessary. A simpler version (I think) would be to just use the full name of the material you want to replace and swap it for the material you want to substitute in. Is that possible/easier to facilitate? This would mean I couldn't make a generic script that work with a lot of scenes but instead would write up a script for each project that replaces its specific materials with ones from my library and when I need to re-import because something changed back in revit or sketchup I can run the script on it again.

 

This would make it a lot more of a 1 to 1 with my manual example in my former post  as I think you could do something along the lines of:

 

sceneMaterials["Multi/Sub _01"].material[2] = sceneMaterials["Material_A"]

 

Which I figured out using macro recorder, however, this only seems to work for sub-materials and the macro recorder provides no feedback when you're just replacing one scene material with another.

 

My big hurdle now is I can't figure out the syntax for just replacing a material with another, seems like a basic thing but I can't find it anywhere. Does anyone here know what that would be? Just looking for something that would be one or two lines of code like above but not operate on sub-materials.

0 Likes
Message 4 of 17

denisT.MaxDoctor
Advisor
Advisor
Accepted solution

here is all methods that you need:

 

 

 

fn getMaterialLibrary filename: = 
(
	if filename == unsupplied do filename = getOpenFilename caption:"Load Material Library" types:"Mat Lib (.mat)|*.mat"
				
	if filename != undefined do
	(
		loadTempMaterialLibrary filename
	)
)

mapped fn collectAllMaterials mat mats:#() = if mat == undefined then mats else  
(
	for k=1 to getNumSubMtls mat where (m = getSubMtl mat k) != undefined do
	(
		if (appendifunique mats m) do collectAllMaterials mat mats:mats
	)
	mats
)

fn getAllLeafMaterials lib: = 
(
	mats = #()
	if lib == unsupplied do lib = scenematerials
	collectAllMaterials (join #() lib) mats:mats
	mats
)

fn findMaterialByName mats name = 
(
	local mat = undefined
	for m in mats while mat == undefined where (stricmp mat.name name == 0) do (mat = n) 
	mat
)

fn replaceMaterialsByName sources targets srcname: trgname: = 
(
	if srcname == unsupplied do srcname = trgname
	if trgname == unsupplied do trgname = srcname
	
	result = false
	if iskindof source String and iskindof target String do
	(
		source = findMaterialByName sources srcname
		target = findMaterialByName targets trgname
		
		if iskindof source Material and iskindof target Material then
		(
			replaceinstances source target 
			result = true
		)
	)
	
	result
)	

 

 

 

 

here is an example of how to use:

 

 

 

temp_lib = getMaterialLibrary()
sources = getAllLeafMaterials()
targets = getAllLeafMaterials lib:temp_lib

replaceMaterialsByName sources targets srcname:<source name> trgname:<target name>

 

 

 

 

<source name> and <target name> are names which you want to use for search and replace. 

You can specify both names or only one of them. In the second case the same name will be used for both source and target

 

Message 5 of 17

denisT.MaxDoctor
Advisor
Advisor

some bugs in my code above were fixed...

0 Likes
Message 6 of 17

denisT.MaxDoctor
Advisor
Advisor
Accepted solution

another bug... it should be fixed as:

 

fn findMaterialByName mats name = 
(
	local mat = undefined
	for m in mats while mat == undefined where (stricmp m.name name == 0) do (mat = m) 
	mat
)

 

 

0 Likes
Message 7 of 17

mxmiceliSGB2D
Explorer
Explorer

Thanks for all your help on this! The script is great and very helpful.

0 Likes
Message 8 of 17

Anonymous
Not applicable

This looks exactly what im after @denisT.MaxDoctor  but I cant get it to work. Lister writes out false on (tried to swap scrname with trgname but still false) 
replaceMaterialsByName sources targets srcname:SteelBlackVray trgname:Steel_Black

 

SteelBlackVray is the material from the matlib and the Steel_Black is the materialname in the scene.

 

Another question does it work with wildcard or did you skip it out?

 

 

0 Likes
Message 9 of 17

denisT.MaxDoctor
Advisor
Advisor

@Anonymous wrote:

This looks exactly what im after @denisT.MaxDoctor  but I cant get it to work. Lister writes out false on (tried to swap scrname with trgname but still false) 
replaceMaterialsByName sources targets srcname:SteelBlackVray trgname:Steel_Black

 

SteelBlackVray is the material from the matlib and the Steel_Black is the materialname in the scene.

 

Another question does it work with wildcard or did you skip it out?

 

 


as I understand your case is opposite. You want to replace a lib's material with a scene material.. right?

 

so it must be :

temp_lib = getMaterialLibrary()
targets = getAllLeafMaterials()
sources = getAllLeafMaterials lib:temp_lib

replaceMaterialsByName sources targets srcname:<source name> trgname:<target name>

 

where <source name> and <target name> are strings and must be "SteelBlackVray" and "Steel_Black" in your case

 

 

 

it is easy to find material from a wildcard, but using a wildcard involves finding several possible matches. How to be in this case? How do I replace multiple materials? This requires additional specification...

 

fn findMaterialsByPattern mats wildcard first:off = 
(
	local mm = #()
	local out = off
	for m in mats where (matchpattern m.name pattern:wildcard) do 
	(
		if first do return m
		append mm m
	)
	if not first do mm
)

-- findMaterialsByPattern scenematerials "Steel_Black*" first:off

 

0 Likes
Message 10 of 17

Anonymous
Not applicable

Thank you for a quick reply @denisT.MaxDoctor , still cant get it to work no matter how I change the targets or sources.

Maybe this will be more clear I have a scene with the standard material called Steel_Black and I want to replace the material in the scene with a material called SteelBlackVray from the replace.mat so the new material in the scene will be the SteelBlackVray (vraymtl).

I tried to put the standard material outside a multi/sub material but still couldnt get it to work. I tried both with "" Around the materialnames and without the ""

So I guess the previous code Is what I still want to achieve.

 

fn getMaterialLibrary filename: = 
(
	if filename == unsupplied do filename = getOpenFilename caption:"Load Material Library" types:"Mat Lib (.mat)|*.mat"
				
	if filename != undefined do
	(
		loadTempMaterialLibrary filename
	)
)

mapped fn collectAllMaterials mat mats:#() = if mat == undefined then mats else  
(
	for k=1 to getNumSubMtls mat where (m = getSubMtl mat k) != undefined do
	(
		if (appendifunique mats m) do collectAllMaterials mat mats:mats
	)
	mats
)

fn getAllLeafMaterials lib: = 
(
	mats = #()
	if lib == unsupplied do lib = scenematerials
	collectAllMaterials (join #() lib) mats:mats
	mats
)

fn findMaterialByName mats name = 
(
	local mat = undefined
	for m in mats while mat == undefined where (stricmp m.name name == 0) do (mat = m) 
	mat
)

fn replaceMaterialsByName sources targets srcname: trgname: = 
(
	if srcname == unsupplied do srcname = trgname
	if trgname == unsupplied do trgname = srcname
	
	result = false
	if iskindof source String and iskindof target String do
	(
		source = findMaterialByName sources srcname
		target = findMaterialByName targets trgname
		
		if iskindof source Material and iskindof target Material then
		(
			replaceinstances source target 
			result = true
		)
	)
	
	result
)	

temp_lib = getMaterialLibrary()
sources = getAllLeafMaterials()
targets = getAllLeafMaterials lib:temp_lib

replaceMaterialsByName sources targets srcname:"Steel_Black" trgname:"SteelBlackVray"

This is what the listener prints

getMaterialLibrary()
collectAllMaterials()
getAllLeafMaterials()
findMaterialByName()
replaceMaterialsByName()
#materialLibrary()
#(Steel_Black:Standard)
#()
false
OK

  Just curious, how should I use the findMaterialsByPattern in replaceMaterialsByName sources targets srcname:<source name> trgname:<target name>

0 Likes
Message 11 of 17

denisT.MaxDoctor
Advisor
Advisor

it's a first time you told that your source material is MultiMaterial. it changes everything and makes everything simple:

 

sources = scenematerials
targets = getMaterialLibrary()

replaceMaterialsByName sources targets srcname:"Steel_Black" trgname:"SteelBlackVray"

 

before, we only looked for leaf materials, skipping any materials with sub-materials.

 

 

0 Likes
Message 12 of 17

Anonymous
Not applicable

I see, as long as it works on both MultiMaterial and alone then its fine. Tried to replace the code with the one you posted but still not working, not even if I place the VrayMaterial in the scene.

ReplaceMat.JPG

0 Likes
Message 13 of 17

denisT.MaxDoctor
Advisor
Advisor

Oh! you've already confused yourself and confused me! 

 

looking on the picture you have a LEAF material "Steel_Black" (Standard) as a sub material which is applyed to a node(s). And you have another material "SteelBlackVray" (vraymtl)  which is in the library.

 

ok... lets do it step-by-step:

targets = getMaterialLibrary()
target_mats_array = join #() targets
--> here you have to see you "SteelBlackVray" !!!

source_mats_array = join #() scenematerials
--> here you have to see some MultiMaterial(s) which include "Steel_Black" !!!
  

 

if so, we can move on ...

 

0 Likes
Message 14 of 17

Anonymous
Not applicable

Its super confusing but now it feels like we are on the right path. 

Yes Its working, it prints both the material library and the scenematerials

 

getMaterialLibrary()
collectAllMaterials()
getAllLeafMaterials()
findMaterialByName()
replaceMaterialsByName()
#materialLibrary()
#(SteelBlackVray:VRayMtl, PlasticGreyVray:VRayMtl)
#(#Multi/Sub-Object:Multi/Sub(Standard:Steel_Black), Plastic_Grey:Standard)
OK

 

0 Likes
Message 15 of 17

denisT.MaxDoctor
Advisor
Advisor

it was a bug in the replaceMaterialsByName function

(the line must be : if iskindof srcname String and iskindof trgname String do ...)

 

the fixed and complete script is:

fn replaceMaterialsByName sources targets srcname: trgname: = 
(
	if srcname == unsupplied do srcname = trgname
	if trgname == unsupplied do trgname = srcname
	
	result = false
	if iskindof srcname String and iskindof trgname String do
	(
		source = findMaterialByName sources srcname
		target = findMaterialByName targets trgname
		
		if iskindof source Material and iskindof target Material then
		(
			replaceinstances source target 
			result = true
		)
	)
	
	result
)

sources = getAllLeafMaterials()
targets = getMaterialLibrary()

replaceMaterialsByName sources targets srcname:"Steel_Black" trgname:"SteelBlackVray"

 

Message 16 of 17

Anonymous
Not applicable

YES! Its finally working, many thanks!

Just curious, how do I apply the findMaterialsByPattern function and replace with it?

0 Likes
Message 17 of 17

denisT.MaxDoctor
Advisor
Advisor

== Just curious, how do I apply the findMaterialsByPattern function and replace with it?

 

have a question - open a new thread

 

this is a similar but new topic and probably needs another discussion

0 Likes