Guys here's the script idea:
I need a script that would move all the instances (of multiple definitions) from the current selection to the separate layers
For example in the current selection I have 10 instances called AAA_ , 10 instances called BBB_ , 10 instances called CCC_ etc..
The script would create the separate layers in accordance to instances names, but starting with prefix z_ :
z_AAA
z_BBB
z_CCC
and move the instances to related layers (all AAA_ instances will be moved to layer z_AAA, BBBs to layer z_BBB etc)
P.S. Instances have numbers in its names, typically AAA_001, AAA_002 etc - the script should ignore them
P.P.S. Ideally I'd like the this script to continue and create dedicated Forest Sets based on those new layers, but maybe I'm asking too much :)
The script is needed for creating reference forests based CAD blocks coming from Sketchup, Autocad, etc..
Thank you
Forum topic · Scripts Wanted
instances to separate layers
By 1rv · 2024-03-24
Description
Comments (16)
discussion went in slightly wrong direction
1rv · 2024-03-25
Hello Kostadin, Simon and others
please take a look at at 2 primitive max files called before and after which might explain my goal better. Also take a look at eponymous printscreens.
In the printscreen and max file called "after": I want the script to create AT LEAST properly named layers (1) and put eponymous instances in them, and maybe (as bonus) it'd create proerly named forest SETS (2) with inner option set to "from layers".
Thank you for attention!
Link:
https://drive.google.com/file/d/1Zn9c6VYeJeF77G1RihW70dS8OtYApTdw/view?u…
p.s. I added the 3rd file to show the forest created via forest set ("by layer")
p.p.s: How to populate Forest Sets from a layer:
https://www.youtube.com/watch?v=TSsa9mS5A9Q&t=760s
Hi
SimonBourgeois · 2024-03-25
I have tested the script with your provided scene and it creates three layers Z_blue,Z_green,Z_red and the forest sets called ForestSet001,ForestSet002 and ForestSet003, from layers is checked and the layer is loaded . if i compare with your scene "2_after" it is exaclty the same exept the name of the layers doesn't have the Z_.
I see that in your third scene you named the forestsets with red green and blue, i can add the renaming of the forestset obj, also for some reason when i tested yesterday the forest set appeared with a visible sized but when i tried in your scene they where created with size set to zero, currently their position are based on the first instance of a group of instance,in my test scene all object were aligned and created in the same order so the forestsets were well aligned with the objects, but in your scene, objects are randomly distributed, it would make more sense to position them evently spaced at the origin.here is a modified version of the code that remove the "Z_" and add a default size of 100 to the created ForestSet and set their positions and names,i 've also added the corresponding forest obj with "reference" on and with the corresponding layer loaded as in your third scene.
(
local imGetInstances = InstanceMgr.GetInstances
fn collectUniqueNodes nodes: =
(
local handles = #{}
for o in nodes where not handles[GetHandleByAnim o] collect
(
imGetInstances o &inst
for i in inst do handles[GetHandleByAnim i] = true
o
)
)
local uniquenodes = collectUniqueNodes nodes:selection as array
local forestsetArr=#()
local forestArr=#()
for obj in uniquenodes do
(
imGetInstances obj &instances
trueInstances = for n in instances where (areNodesInstances obj n) collect n
if trueInstances.count > 1 do
(
local layername = trimRight obj.name "0123456789_"
if (objLayer = layerManager.newLayerFromName layerName) != undefined then objLayer.addNodes trueInstances
else (layerManager.getLayerFromName layerName).addNodes trueInstances
if isForestPack != undefined do
(
ForestSetname = "ForestSet_"+layername
local ForestSetobj= ForestSet name:ForestSetname pos:[0,0,0] layerimport:on layernames:layername iconSize:100
append ForestSetArr ForestSetobj
local ForestObj
if Forest_Pro != undefined then
(
ForestObj= Forest_Pro pos:[0,-120,0] iconSize:100
)
else
(
ForestObj= Forest_Lite pos:[0,-120,0] iconSize:100
)
ForestObj.name = "Forest_"+layername
ForestObj.distmode = 2
ForestObj.distrefnodes[1] = ForestSetobj
append ForestArr ForestObj
)
)
)
if ForestSetArr.count > 1 do
(
for i = 0 to ForestSetArr.count-1 do (ForestSetArr[i+1].pos.x=i*100;ForestArr[i+1].pos.x=i*100)
)
)
if you select all objects in your first scene and execute the script you will get the same result as your third scene, let me know if i misunderstood your needs. :)
Already rock solid script
1rv · 2024-03-26
Works smoothly, HUGE thank you Simon! And wow! you went even further by creating matching forests! It even works without errors if eponymous sets\forests already exist.
Somehow I forgot to rename new layers with "z_" prefix in my final.max example, could you please update the script so it would do add this prefix in layer name?
The only problem is the typical situation when instances are named like Group#1071_001, Group#1071_002, Group#1071_003 where Group#1071 is the typical Sketchup naming and "_00x" is numbering added by importer.. Currently the script cuts off everything after # sign. Could it please preserve it (in given example the "Group#1071" should remain) everywhere? It should cut off starting with "_" and on.
Thank you Simon!
you're welcome
SimonBourgeois · 2024-03-26
Glad i could help :)
I reintroduce the "z_" in the layers names, and i changed the way it remove "_00x" but keep in mind that it will always remove 4 characters at the end of your instance objects names, if imported objects has a different naming convention, you can modify those lines:
"local layername = "z_"+ substring obj.name 1 (obj.name.count - 4)
local forestname = substring obj.name 1 (obj.name.count - 4)"
changing the "4" to the number of characters that needs to be removed.
i also introduced an additionnal check, as forest has 2 versions, the forest object is different between versions and in my previous code it was checking if the Pro version or Lite version exist but it wasn't checking if both versions were installed, now it creates a pro version object if both are installed.
(
local imGetInstances = InstanceMgr.GetInstances
fn collectUniqueNodes nodes: =
(
local handles = #{}
for o in nodes where not handles[GetHandleByAnim o] collect
(
imGetInstances o &inst
for i in inst do handles[GetHandleByAnim i] = true
o
)
)
local uniquenodes = collectUniqueNodes nodes:selection as array
local forestsetArr=#()
local forestArr=#()
for obj in uniquenodes do
(
imGetInstances obj &instances
trueInstances = for n in instances where (areNodesInstances obj n) collect n
if trueInstances.count > 1 do
(
local layername = "z_"+ substring obj.name 1 (obj.name.count - 4)
local forestname = substring obj.name 1 (obj.name.count - 4)
if (objLayer = layerManager.newLayerFromName layerName) != undefined then objLayer.addNodes trueInstances
else (layerManager.getLayerFromName layerName).addNodes trueInstances
if isForestPack != undefined do
(
ForestSetname = "ForestSet_"+forestname
local forestsetobj = ForestSet name:ForestSetname pos:[0,0,0] layerimport:on layernames:layername iconSize:100
append forestsetArr forestsetobj
local Forestobj
if Forest_Pro != undefined or (Forest_Pro != undefined and Forest_Lite != undefined) then
(
Forestobj = Forest_Pro pos:[0,-120,0] iconSize:100
)
else if Forest_Lite != undefined do
(
Forestobj = Forest_Lite pos:[0,-120,0] iconSize:100
)
Forestobj.name = "Forest_"+forestname
Forestobj.distmode = 2
Forestobj.distrefnodes[1] = forestsetobj
append forestArr Forestobj
)
)
)
if forestsetArr.count > 1 do
(
for i = 0 to forestsetArr.count-1 do (forestsetArr[i+1].pos.x=i*100;forestArr[i+1].pos.x=i*100)
)
)
1rv · 2024-03-26
So far so good! This is great boring dumb work eliminator, thank you so much! and the text after # is preserved nicely :)
Its a pity Itoo still didn't implement icon text feature for their objects and forest sets are not in their lister ..
P.S. I'd publish it so it wouldn't get lost
That is why i began scripting, no more repetitive task...
SimonBourgeois · 2024-03-26
I had the same idea about text icon, but forgot to add it, here is the modified version with a text for each forest objects, i added parenting links so that you can move the forest object with its text and its corresponding forest set,it return an error when trying to parent the forestset to the forest object, so i parented the forestset to the text and the text to the forest object :).
here is the modified script:
(
local imGetInstances = InstanceMgr.GetInstances
fn collectUniqueNodes nodes: =
(
local handles = #{}
for o in nodes where not handles[GetHandleByAnim o] collect
(
imGetInstances o &inst
for i in inst do handles[GetHandleByAnim i] = true
o
)
)
local uniquenodes = collectUniqueNodes nodes:selection as array
local forestsetArr=#()
local forestArr=#()
for obj in uniquenodes do
(
imGetInstances obj &instances
trueInstances = for n in instances where (areNodesInstances obj n) collect n
if trueInstances.count > 1 do
(
local layername = "Z_"+ substring obj.name 1 (obj.name.count - 4)
local forestname = substring obj.name 1 (obj.name.count - 4)
if (objLayer = layerManager.newLayerFromName layerName) != undefined then objLayer.addNodes trueInstances
else (layerManager.getLayerFromName layerName).addNodes trueInstances
if isForestPack != undefined do
(
ForestSetname = "ForestSet_"+forestname
local forestsetobj = ForestSet name:ForestSetname pos:[0,0,0] layerimport:on layernames:layername iconSize:100
append forestsetArr forestsetobj
local Forestobj
if Forest_Pro != undefined or (Forest_Pro != undefined and Forest_Lite != undefined) then
(
Forestobj = Forest_Pro pos:[0,-120,0] iconSize:100
)
else if Forest_Lite != undefined do
(
Forestobj = Forest_Lite pos:[0,-120,0] iconSize:100
)
Forestobj.name = "Forest_"+forestname
Forestobj.distmode = 2
Forestobj.distrefnodes[1] = forestsetobj
append forestArr Forestobj
)
local txticon = text text:forestname pos:[0,-210,0] size:20
txticon.wireColor = color 254 159 0
txticon.parent = Forestobj
forestsetobj.parent = txticon
)
)
if forestsetArr.count > 1 do
(
for i = 0 to forestsetArr.count-1 do (forestArr[i+1].pos.x=i*100)
)
)
Sure you can publish it, maybe it would be better if i make a macroscript, to have a button in the UI, let me know if you think it would be better or if it's ok as it is...
1rv · 2024-03-26
Linked text is an awesome improvement - thank you so much.
If you indeed consider an mcr with UI it might be a good idea creating a field for custom prefix in layer name, making forest set and FP creation optional.
Thanks!
Version with UI
SimonBourgeois · 2024-03-27
your welcome :)
I've added the checkboxes for ForestSet and ForestPack, i've also added the custom suffix.
you can download the file here:
https://www.mediafire.com/file/fmjw8fi9qher7oo/Instances_to_layers_For_F…
1rv · 2024-03-27
Fantastique! Merci beaucoup!
SimonBourgeois · 2024-03-27
De rien :)
1rv · 2024-03-31
This will launch this script to stratosphere:
Is it possible to add an optional button "add FP geo"?
(I'd create FP in the rRef mode with selected entities) :)
P.S. I PMed you (@free.fr)
updated script
SimonBourgeois · 2024-04-20
https://www.mediafire.com/file/y9izl2rf83sgzqb/Instances_to_layers_For_F…
Previous version has a bug when forest pro version was used.
there is still a limitation to this script, every sets of instances need to have a different base name or the script crashes because layers are created based on those instances names stripped of the "_00x"part.
for example: Object_001,Object_002,Object_003,etc... and Other-Object_001,Other-Object_002,Other-Object_003,etc...
miauu · 2024-03-24
(
local strZ_ = "Z_"
local str0123456789_ = "0123456789_"
local imGetInstances = InstanceMgr.GetInstances
function UniqueNodes nodes: =
(
local handles = #{}
for o in nodes where not handles[GetHandleByAnim o] collect
(
imGetInstances o &inst
for i in inst do handles[GetHandleByAnim i] = true
o
)
)
UniqueObjsArr = UniqueNodes nodes:(selection as array)
for o in UniqueObjsArr do
(
objsArr = #()
if (imGetInstances o &inst) > 1 do (if findItem objsArr o == 0 do join objsArr inst)
layerName = (strZ_ + (trimRight o.name str0123456789_))
if (objLayer = layerManager.newLayerFromName layerName) != undefined then
objLayer.addNodes objsArr
else
(layerManager.getLayerFromName layerName).addNodes objsArr
)
)
Hi miauu,Good idea for the
SimonBourgeois · 2024-03-24
Hi miauu,
Good idea for the (trimRight o.name str0123456789_), its better then my (substring obj.name 1 (obj.name.count - 4), also your function for collecting unique nodes looks better,and the check for already existing layer with the same name is also a good idea.;)
But there is something that doesn't work well in your script, if you have objects in selection that are referenced copys it will create a layer for them aswell, that is why i added this line: trueInstances = for n in instances where (areNodesInstances obj n) collect n
also i noticed that your script was creating a layer for objects that aren't instanced if selection contain non instanced object.
i've updated my earlier post to add the forestset creation, now that i see your post, maybe we should merge the two versions...:)
By the way thanks for all your post, you have been really helpfull, i am new to scripting and i've been learning a lot from you.
Also, i was wondering how to post code in a "window" like you, mine has been all messed up when i posted it (every line start at the begining of a line).
edit:
Here is a merged version of both script:
(
local imGetInstances = InstanceMgr.GetInstances
fn collectUniqueNodes nodes: =
(
local handles = #{}
for o in nodes where not handles[GetHandleByAnim o] collect
(
imGetInstances o &inst
for i in inst do handles[GetHandleByAnim i] = true
o
)
)
local uniquenodes = collectUniqueNodes nodes:selection as array
for obj in uniquenodes do
(
imGetInstances obj &instances
trueInstances = for n in instances where (areNodesInstances obj n) collect n
if trueInstances.count > 1 do
(
local layername = "Z_"+(trimRight obj.name "0123456789_")
if (objLayer = layerManager.newLayerFromName layerName) != undefined then objLayer.addNodes trueInstances
else (layerManager.getLayerFromName layerName).addNodes trueInstances
if isForestPack != undefined do ForestSet pos:obj.pos layerimport:on layernames:layername
)
)
)
miauu · 2024-03-24
Hi, Simon!
I don't know if my code will work properly or not, because I don't have a scene to test with.
If you want you can scalp what you need from my code.
To properly post your code in the forum use this(without the quotes):
["code"]
-- your code here
["/code"]
Hi
SimonBourgeois · 2024-03-25
thx Miauu,
For some reason i can't edit the earlier message but i can edit this one, so here is the updated code :
(
local imGetInstances = InstanceMgr.GetInstances
fn collectUniqueNodes nodes: =
(
local handles = #{}
for o in nodes where not handles[GetHandleByAnim o] collect
(
imGetInstances o &inst
for i in inst do handles[GetHandleByAnim i] = true
o
)
)
local uniquenodes = collectUniqueNodes nodes:selection as array
for obj in uniquenodes do
(
imGetInstances obj &instances
trueInstances = for n in instances where (areNodesInstances obj n) collect n
if trueInstances.count > 1 do
(
local layername = "Z_"+(trimRight obj.name "0123456789_")
if (objLayer = layerManager.newLayerFromName layerName) != undefined then objLayer.addNodes trueInstances
else (layerManager.getLayerFromName layerName).addNodes trueInstances
if isForestPack != undefined do ForestSet pos:obj.pos layerimport:on layernames:layername
)
)
)