I’ve added a few todo’s to the previously posted code.
Add a name maker function that will use keys array to check whether new name is already used by another texmap.
Use this function to make a new name for each duplicate and don’t forget to append this lowercased new name to the keys array
Yes, I first wrote a question, and then I saw that comments were added to the source code.
Thanks for the hint, I solved this problem!
Hello Serejan!
I am still trying to optimize the code for renaming maps that have the same name.
On small scenes, everything works fine, but if you come across a large scene in which there are a huge number of maps with the same names (sometimes there are up to 20), the code takes a very long time (sometimes more than a minute) and at the end I get an error
-- Call stack overflow
I think this is because of the extra check for the duplicate name in the keys array.
Is it possible to somehow optimize the code so that it runs faster and does not give such an error?
Thanks again!
PS: For testing, I took one of the evermotion scenes, which has a lot of maps with duplicate names. You can also take this file for testing here: https://we.tl/t-wMjeo8BAJa (without textures, since the file will have a very large size with them)
You’re lucky to wait just a minute
Simple code like this takes 600 sec on my laptop in your scene (yeah it is that slow)
for tex_class in textureMap.classes do
(
for tex in getClassInstances tex_class do ( /* do nothing */ )
)
Every time you call getClassInstances under the hood it iterates over whole scene hierarchy to test and collect instances of a particular class. That’s why it is so slow (If I’m not mistaken).
If you want to make it perform as fast as possible switch to c# or c++ and take a look at getClassInstances_cf source code. It is available in the SDK source files.
I doubt that there’s any better solution available in maxscript
Maybe you can use EnumerateAllAnimatables somehow, and filter only TextureMaps after that
Unfortunately, there are such problems due to the same map names that some people have, who often use all sorts of scene managers focused on the names of nodes in their work (for example, a script that performs the task of outputting the render pass depending on which map is actually loaded). There are many different cases where the same names in a scene can cause all sorts of problems. In general, having duplicate names for different independent nodes is a bad practice.
Perhaps you are right, and if a quick and correct solution is not found to rename only duplicate names, then most likely you will have to go this way. The only thing that can be annoying in this case is the large digital values in such maps names.
This is not a very convenient way, since renaming maps is just a part of one big maxscript script. Moreover, I am not very well versed in C# and C++, and I do not understand how to combine this with maxscript.
What I noticed is that the getClassInstances function works pretty quickly for me. Moreover, in the process of searching and dividing maps with the same names, I do not use getClassInstances, but use a ready-made array of all scene maps. My code looks like this:
(
mapsArray -- array of all texturemaps from scene
fn MakeUniqueTexmapName base_name keys cntNum format_string =
(
local tempName=base_name + (formattedPrint cntNum format:format_string)
if (finditem keys tempName)!=0 do (
MakeUniqueTexmapName base_name keys (cntNum += 1) format_string
)
tempName
)
fn renMapsName mapsArray =
(
keys = #()
textures = #()
duplicates = #{}
for tex in mapsArray do
(
key = toLower tex.Name
index = findItem keys key
if index > 0 then
(
append textures[ index ] tex
duplicates[ index ] = true
)
else
(
append keys key
textures[ keys.Count ] = #( tex )
)
)
for index in duplicates do
(
unique_duplicates = makeUniqueArray textures[ index ]
if unique_duplicates.count > 1 do
(
map_name = unique_duplicates[1].Name
base_name = trimRight map_name "0123456789"
num_digits = map_name.Count - base_name.Count
num = (substring map_name (1 + base_name.Count) num_digits) as integer
format_string = "0" + num_digits as string + "d"
i = num
for j = 2 to unique_duplicates.Count do
(
new_name = MakeUniqueTexmapName base_name keys (i += 1) format_string
unique_duplicates[j].Name = new_name
append keys (tolower new_name)
)
)
)
)
renMapsName mapsArray
)
It seems to me that the problem occurs in the last part of the code in the MakeUniqueTexmapName function, when it passes an additional check for existing names in the array. However, duplicate names are still sometimes skipped.
Perhaps I am making a mistake somewhere.
what if you replace custom map name maker function with assignNewName function as denisT suggested?
btw. there could be a recursion problem here. Imagine having two maps named Map#01 and the rest named Map#02 … till Map#666
how deep your recursion will be in this case? Map#667 is going be the first unused name. ( At this point you have 666 tempName string variables held on stack. Or maybe not strings but only pointers to string values stored on heap. Anyway no matter where exactly, all these 665 temp names are stored without any purpose, thus bloating the heap size and probably overflowing the stack? )
If you still want to use custom namemaker replace recursion with while loop
new_name = MakeNewName()
while (finditem keys new_name ) > 0 do new_name = MakeNewName()
tex.name = new_name
append keys new_name
I am trying to understand the logic to give some meaning to what we are doing …
What’s the point of keeping abstract names like: Map # 123, Map #1123125236, Map # 56, …? Even if they are unique?
I analyzed the names of the textures in your scene, so 99.9% of the names don’t make any sense. Wouldn’t it be easier to rename them all at once? At least we’ll track their count by increasing ID…
There is another idea …
(
dummy_anim = bezier_float()
max_handle = gethandlebyanim dummy_anim
texture_maps = for h = 1 to max_handle where iskindof (t = getanimbyhandle h) TextureMap collect t
)
… and all together is:
(
dummy_anim = bezier_float()
max_handle = gethandlebyanim dummy_anim
texs = for h = 1 to max_handle where iskindof (t = getanimbyhandle h) TextureMap collect t
names = #()
items = #()
for t in texs do
(
k = finditem names t.name
if k == 0 do
(
append names t.name
append items #()
k = names.count
)
append items[k] t
)
for item in items do for k=2 to item.count do assignnewname item[k]
)
It was the first thing that I tried to use as a replacement for getClassInstances, but unfortunately it crashes max on provided scene file.
Perhaps, EnumerateAllAnimatables is the only way to go