Notifications
Clear all

[Closed] Expand Material Library – More Efficient?

Can anyone suggest some ways to make this more efficient…

fn getSubMats themat theLibrary = 
(
	if finditem (for o in thelibrary collect o.name) themat.name == 0 do append thelibrary themat
	
	for i = 1 to getNumSubMtls themat do
	(
		if (getSubMtl themat i) != undefined do thelibrary = getSubMats (getSubMtl themat i) theLibrary
	)
	theLibrary
)


fn expandMaterialLibrary thelib =
(
	aMaterialLibrary = #()
	
	for o in thelib do
	(
		getSubMats o aMaterialLibrary
	)
	aMaterialLibrary
)

testLib = (expandMaterialLibrary scenematerials)
10 Replies
 lo1
  • Don’t extract names on every iteration, store names in a separate array
  • SceneMaterials may contain maps, which will throw an exception if you call GetNumSubMtls on them. I added superclass check against this.
  • you can call GetSubMtl once instead of twice
fn getSubMats themat theLibrary libNames = 
(
	if finditem libNames themat.Name == 0 do
	(
		append thelibrary themat
		append libNames theMat.Name
	)
	
	if superClassof theMat == material do
	(
		for i = 1 to getNumSubMtls themat do
		(
			local subMat = getsubMtl theMat i
			if subMat != undefined do
			(
				thelibrary = getSubMats subMat theLibrary libNames
			)
		)
	)
	theLibrary
)

fn expandMaterialLibrary thelib =
(
	aMaterialLibrary = #()
	libNames = #()
	
	for o in thelib do
	(
		getSubMats o aMaterialLibrary libNames
	)
	aMaterialLibrary
)

ts = timestamp()
expandMaterialLibrary sceneMaterials
print (timestamp()-ts)
mapped fn expandMaterials source target = try  
(
	for mat in source where iskindof mat material do
	(
		if (appendifunique target mat) do expandMaterials mat target
	)
)
catch()
expandMaterials (join #() scenematerials) scenematerials

expand scenematerials into scenematerials is probably not a good idea. all ‘not scene materials’ will be wiped on next scene save action.
so the better way is to expand it to a temp library…

check by name is not right too. max allows to make materials with duplicated names… look at some architectural scenes… there are tons of multi-materials but all sub-materials are probably named: “window”, “wall”, “roof”, etc. (or “glass”, “bricks”, “concrete”, “grass”, etc.)

I am not sure which functions is correct, as I am getting different results with them. I get the same results from the functions from David and Rotem. For some reason they both seems to miss some materials in multimaterials.

Also the function from Denis as it is, seems to duplicate materials in the library.

Here is another function that runs faster for me and appears to collect all the materials in multimaterials, but I can’t tell if this one does what you need.

fn ExpandMaterialLibrary mLib =
  (
  	materials = #()
  	for m in mLib where iskindof m material do
  	(
  		append materials m
  		for s = 1 to (getnumsubmtls m) where m[s] != undefined do append materials m[s]
  	)
  	materials
  )

my function doesn’t duplicate materials… at least for my tests
can we make it faster? probably yes, but not dramatically. try/catch in this case shouldn’t take a time. all other things (append, search, etc.) we have to do anyway.

1 Reply
(@polytools3d)
Joined: 11 months ago

Posts: 0

This is what I get:

(
 mapped fn expandMaterials source target = try
 (
 	for mat in source where iskindof mat material do
 	(
 		if (appendifunique target mat) do expandMaterials mat target
 	)
 )
 catch()
 
 resetmaxfile #noprompt
 for j = 1 to 10 do sphere material:(multimaterial numsubs:25)
 
 format "scene materials:% 
" scenematerials.count
 
 expandMaterials (join #() scenematerials) scenematerials
 
 format "scene materials:% 
" scenematerials.count
 )

Before runing your function:
scene materials:10
After runing your function
scene materials:260

well… try takes a time… so modified looks this way

mapped fn expandMaterials source target = 
(
	for k=1 to getnumsubmtls source where iskindof (mat = getsubmtl source k) material do
	(
		if (appendifunique target mat) do expandMaterials mat target
	)
)
expandMaterials (join #() scenematerials) scenematerials
aa = makeuniquearray (join #() scenematerials)
format "% %
" scenematerials.count aa.count

Here is a simple test case and the results I am getting:

(
   	fn ExpandMaterialLibrary mLib =
   	(
   		materials = #()
   		for m in mLib where iskindof m material do
   		(
   			append materials m
   			for s = 1 to (getnumsubmtls m) where m[s] != undefined do append materials m[s]
   		)
   		materials
   	)
   	
   	resetmaxfile #noprompt
   	for j = 1 to 100 do sphere material:(multimaterial numsubs:25)
   	
   	gc()
   	st = timestamp(); sh = heapfree
   	
   	lib = ExpandMaterialLibrary scenematerials
   	
   	format "time:% ram:% mats:%
" (timestamp()-st) (sh-heapfree) lib.count
   )

time:4 ram:14024L mats:2600

These are the results from the other functions:
David
time:1611 ram:88103480L mats:2501
Rotem
time:94 ram:367240L mats:2501

Notice the difference in the materials gathered.

Thanks Jorge, sorry for delayed response I finally got around to actually using this!

Found a bug in your code where material[i] would retrieve subanims for undefined submaps. This works instead…

	fn ExpandMaterialLibrary mLib =
   	(
   		materials = #()
   		for m in mLib where iskindof m material do
   		(
   			append materials m
   			for s = 1 to (getnumsubmtls m) where (getsubmtl m s) != undefined do append materials (getsubmtl m s)
   		)
   		materials
   	)

And yes Denis I didn’t really want to expand the Scene Materials into itself, only into a temporary library so Jorge’s solution works great!