[Closed] Help request: altering Max5+ script to make it Max3 compatible version
Hello gentlemen,
I’m here to ask for a little help as I’m not any professional or even amateur MaxScripter. A while ago I’ve been helped by Galagast who was kind enough to write me a script I needed. There’s no problems with it, it works like a charm, except that it won’t work for 3dsmax R3 / 3.1 . I’m part of the group of enthusiasts who like to make 3D racing tracks for racing simulations and we’re obliged to use R3 because there are certain plugins made for it which are not available for higher versions of 3dsmax.
Below is the script which works fine in all max versions starting from 5. The error I get when I run the script in Max R3 is “– Unable to convert: 120 to type: String”. This message is in a small popup window. I close it and nothing happens. And ideally, the menu/rollout should apper with a couple of options. Anybody of you guys can figure out what language structures are used in this script which are not understood by Max R3? Is it possible to make some small changes to make it compatible with R3? Or at least make it a R3-only script?
I would really appreciate the help. I’ve posted a few times here, either via my brother or myself, and was always helped, which is why I keep coming back
And just a quick description of the script: it splits the objects by their material IDs and hence creates smaller objects, naming them by the texture file name (without extension) they have assigned. I know there is a “MaterialID Object Splitter v0.2” plugin for 3dsmax R3, but it doesn’t name the objects after their texture file name and we need this option a lot.
Best Regards,
Oleg
rollout detachByID "Detach by ID" width:120 height:135
(
local name_type = true
local ran_color = true
local the_name = "Element_"
local the_mats = #()
local id_list = #()
radiobuttons name_rd "" pos:[10,15] width:60 height:32 labels:#("Material", "Prefix") default:1 columns:1
edittext name_edt "" pos:[10,50] width:100 height:15 visible:false text:the_name
checkbox del_bot "Delete Original" pos:[5,75] width:110 height:15 checked:true
checkbox rancolor_chk "RandomWireColors" pos:[5,90] width:110 height:15 checked:true
button detach_bot "DETACH" pos:[5,110] width:110 height:21
GroupBox name_grp "Naming: " pos:[5,0] width:110 height:70
label name_lbl "- Texture Name -" pos:[10,50] width:100 height:15 visible:true
on detach_bot pressed do
undo "Detach by ID" on
(
obj = undefined
the_sel = selection as array
if the_sel != undefined do
(
for obj in the_sel where superclassof obj == GeometryClass do
(
id_list = #() -- empty the id_list array
the_mats = #() -- empty the the_mats array
try(convertToPoly obj)catch() -- convert the object to polygon
the_orig = obj --store the current object here
the_polyobj = copy the_orig --copy the original object
the_facecount = getnumfaces the_polyobj --get the face count
-- the ff will collect matrial ids
for i in 1 to the_facecount do
(
the_faceid = the_polyobj.getfacematerial i
if not (finditem id_list the_faceid) != 0 then append id_list the_faceid
) --thus store them in an array [id_list]
-- the ff will collect material names
try (
if obj.material != undefined then -- if the object has a material...
(
if classof obj.material == multimaterial then -- check if it is a multimaterial
(
for i in id_list do -- loop through the collected id list
(
the_objmat = obj.material[i] -- objects material is stored in here
try
(
the_mapfile = the_objmat.diffusemap.bitmap.filename --get the diffuse bitmap filename
the_filename = getfilenamefile the_mapfile + "-" -- get the filename
append the_mats (uniquename the_filename ) --collect the names
)
catch (append the_mats obj.name; format "%'s multimaterial[%] has no diffuse bitmap
" obj.name i)
)
)
else
(
if classof obj.material == standard do
(
the_objmat = obj.material
try
(
the_mapfile = the_objmat.diffusemap.bitmap.filename
the_filename = getfilenamefile the_mapfile + "-"
append the_mats (uniquename the_filename)
)
catch (append the_mats obj.name; format "%'s has no diffuse bitmap
" obj.name)
)
)
)
else (append the_mats obj.name; format "% has no material
" obj.name)
)
catch (append the_mats obj.name; format "unknown error occured on %
" obj.name)
-- thus store them in [the_mats]
-- the ff will loop through the collected ids, select faces using the face ids...
-- then detach those faces, and also use the collected names by index to name it
the_index = 0
for i in id_list do
(
the_index += 1 --increment the index
the_polyobj.selectbymaterial i --select the faces using the material ids collected
the_faces = getfaceselection the_polyobj
-- this will set what naming convention is used
if name_type then (the_name = uniquename the_mats[the_index])
else (the_name = (uniquename the_name))
polyOp.detachFaces the_polyobj the_faces asnode:true name:the_name --detach
if ran_color then (getnodebyname the_name).wirecolor = random black white
)
the_index = 0
if del_bot.checked do delete the_orig
delete the_polyobj
)
)
)
on name_edt entered txt do (the_name = txt)
on rancolor_chk changed val do (ran_color = val)
on name_rd changed val do
(
case val of
(
1:
(
name_edt.visible = false
name_lbl.visible = true
name_type = true
)
2:
(
name_edt.visible = true
name_lbl.visible = false
name_type = false
)
)
)
)
createdialog detachByID
-- script request by Eye of Hawk (CGTalk)
-- author: Galagast 05/05/24
This is a bit tricky as the code operates on Editable Poly objects which don’t exist as class in Max 3. The script would have to operate on Editable Mesh instead. Unfortunately, I don’t have a copy of the R3 MAXScript Help and don’t remember whether it featured the meshOp.detachFaces method. It would be great if you could open the Help and do a search for “meshop.detachfaces” and see if you will get anything back. There ARE workarounds for R3 if it does not exist, but the code would become much more complex.
It would be even better if you could put the R3 MAXScript help online somewhere for me to download.
Hi Bobo,
First of all, thank you for the quick reply!
I now start to remember why it wasn’t working for R3. Yes, indeed, MaxR3 doesn’t have the editable poly class. I guess it then won’t be as easy as I thought yesterday.
I didn’t know if I could just post the help file here, so I sent the link to MaxScript R3 help file to you via PM.
Thanks again for the reply and hope it’s still possible. I’ll keep my fingers crossed
Kind Regards,
Oleg
Got it, thanks.
Looking at the help, R3 does not support the meshOp. struct directly (although I am sure it was available in a DLX extension, Avguard or MaxAgni, might still be there on Scriptspot.com…)
I will try to get the same result using a different approach and will let you know whether it works or not.
Here is an attempt for a R3 version:
(
global detachByID_Floater --define the dialog rollout
rollout detachByID_Rollout "Detach by ID"
(
radiobuttons name_rd "" pos:[10,15] width:60 height:32 labels:#("Material", "Prefix") default:1 columns:1
edittext name_edt "" pos:[10,50] width:100 height:15 visible:false text:"Element"
checkbox del_bot "Delete Original" pos:[5,75] width:110 height:15 checked:true
checkbox rancolor_chk "RandomWireColors" pos:[5,90] width:110 height:15 checked:true
button detach_bot "DETACH" pos:[5,110] width:110 height:21
GroupBox name_grp "Naming: " pos:[5,0] width:110 height:70
label name_lbl "- Texture Name -" pos:[10,50] width:100 height:15 visible:true
on detach_bot pressed do
(
undo "Detach by ID" on
(
the_sel = for o in selection where superclassof o == GeometryClass and classof o != TargetObject collect o
for obj in the_sel do
(
local theClone = convertToMesh (copy obj) --create temp. EMesh copy
local id_list = #{} --init. ID list as bitarry and collect all IDs used in the mesh:
for i in 1 to theClone.numfaces do id_list[getfacematID theClone i] = true
local the_mats = #() --init. an array to collect material names
for i in id_list do --for each ID found
(
if name_rd.state == 1 then --if use texture names
(
case classof obj.material of --depending on the material class
(
multimaterial: (
try
the_mats[i] = obj.name + "_MatID" + i as string + "_" + (getfilenamefile obj.material[i].diffusemap.bitmap.filename)
catch
the_mats[i] = obj.name + "_MatID" + i as string
)
standard: (
try
the_mats[i] = obj.name + "_MatID" + i as string + "_" + (getfilenamefile obj.material.diffusemap.bitmap.filename)
catch
the_mats[i] = obj.name + "_MatID" + i as string
)
default: the_mats[i] = obj.name + "_MatID" + i as string --for all other classes, just use object and ID
)--end case
)
else --if prefix requested, use whatever is in the text field and add MatID to it.
the_mats[i] = name_edt.text + "_MatID" +i as string
)--end i loop
for i in id_list do --for each ID found
(
local newObject = copy theClone --create a new copy of the temp. object
newObject.name = uniquename (the_mats[i] + "_") --name it using the name collected above
--go through all faces backwards and delete any faces with ID not equal to the current one
for f = newObject.numfaces to 1 by -1 where getFaceMatID newObject f != i do deleteFace newObject f
local usedVerts = #{1..newObject.numverts} --init. a bitarray with all vertices set to true
for f = 1 to newObject.numfaces do --go through all faces
(
theFaceDef = getFace newObject f --get the face definition
usedVerts[theFaceDef.x] = false --and set its vertices to false
usedVerts[theFaceDef.y] = false
usedVerts[theFaceDef.z] = false
)
usedVerts = usedVerts as array --now we have an array of all isolated vertices not used by faces
--delete them:
for v = usedVerts.count to 1 by -1 do deleteVert newObject usedVerts[v]
--assign wirecolor depending on the option
if rancolor_chk.state then
newObject.wirecolor = random black white
else
newObject.wirecolor = obj.wirecolor
)
if del_bot.checked do delete obj --if requested, delete the original
delete theClone --delete the temp. clone
)--end obj loop
)--end undo
)--end on
on name_rd changed val do --enable/disable controls depending on radio buttons
(
name_edt.visible = val == 2
name_lbl.visible = val == 1
)
)
if detachByID_Floater != undefined do closeRolloutFloater detachByID_Floater
detachByID_Floater = newRolloutFloater "Detacher" 130 170
addRollout detachByID_Rollout detachByID_Floater
-- script request by Eye of Hawk (CGTalk)
-- author: Galagast 05/05/24
-- Max R3 version: Bobo 06/12/16
)
I had to use a Floater since R3 had no dialogs yet and work around mesh detaching using cloning and deleting.
It works in R9, but somebody should test it in R3 and see if everything still works – there might be some method not available there or a different behaviour.
The script is not 100% the same as the original – at least the material naming is slightly different as I used the object name, a suffix “MatID” with the actual face Material ID and then the diffuse map name if found. But this part of the script is easy to change if necessary.
I simplified large parts of the original script, got rid of most temp. variables etc.
Give it a try.
Hey Bobo,
no problems with the delay at all Thank you for working on it very much!
Now onto the new script. There seems to be some problems with it, first task is to make it run and do some job, correct or wrong. When I first launched it, I got this error:
So I tried and removed the whole line which starts with the word ‘GroupBox’ … as far as I understand it’s not crucial to have it there, just a groupbox control. Once removed, the rollout menu appeared and I could press the DETACH button. After clicking the DETACH button, I got this error:
well, I tried and removed the block which starts with ‘undo “Detach by ID” on’ as I thought that was not vital as well, at least for testing purposes, but then I got this:
And that’s where I stopped any more removal tricks. Is this fixable?
Oh my, I never use groupBox unless necessary, I forgot that it was not in R3.
Also, the .visible property was added in Max 5 or 6 or so if I am not mistaken.
I left the whole UI as is, will redo it completely, let’s see if it solves your problems.
This this one:
(
global detachByID_Floater --define the dialog rollout
rollout detachByID_Rollout "Detach by ID"
(
radiobuttons name_rd "" labels:#("Material", "Prefix") default:1 columns:2 offset:[0,-3]
edittext name_edt "" text:"Element" offset:[-11,0]
checkbox del_bot "Delete Original" checked:true offset:[-7,0]
checkbox rancolor_chk "RandomWireColors" checked:true offset:[-7,0]
button detach_bot "DETACH" width:110 height:21
on detach_bot pressed do
(
undo on
(
the_sel = for o in selection where superclassof o == GeometryClass and classof o != TargetObject collect o
for obj in the_sel do
(
local theClone = convertToMesh (copy obj) --create temp. EMesh copy
local id_list = #{} --init. ID list as bitarry and collect all IDs used in the mesh:
for i in 1 to theClone.numfaces do id_list[getfacematID theClone i] = true
local the_mats = #() --init. an array to collect material names
for i in id_list do --for each ID found
(
if name_rd.state == 1 then --if use texture names
(
case classof obj.material of --depending on the material class
(
multimaterial: (
try
the_mats[i] = obj.name + "_MatID" + i as string + "_" + (getfilenamefile obj.material[i].diffusemap.bitmap.filename)
catch
the_mats[i] = obj.name + "_MatID" + i as string
)
standard: (
try
the_mats[i] = obj.name + "_MatID" + i as string + "_" + (getfilenamefile obj.material.diffusemap.bitmap.filename)
catch
the_mats[i] = obj.name + "_MatID" + i as string
)
default: the_mats[i] = obj.name + "_MatID" + i as string --for all other classes, just use object and ID
)--end case
)
else --if prefix requested, use whatever is in the text field and add MatID to it.
the_mats[i] = name_edt.text + "_MatID" +i as string
)--end i loop
for i in id_list do --for each ID found
(
local newObject = copy theClone --create a new copy of the temp. object
newObject.name = uniquename (the_mats[i] + "_") --name it using the name collected above
--go through all faces backwards and delete any faces with ID not equal to the current one
for f = newObject.numfaces to 1 by -1 where getFaceMatID newObject f != i do deleteFace newObject f
local usedVerts = #{1..newObject.numverts} --init. a bitarray with all vertices set to true
for f = 1 to newObject.numfaces do --go through all faces
(
theFaceDef = getFace newObject f --get the face definition
usedVerts[theFaceDef.x] = false --and set its vertices to false
usedVerts[theFaceDef.y] = false
usedVerts[theFaceDef.z] = false
)
usedVerts = for i in usedVerts collect i --now we have an array of all isolated vertices not used by faces
--delete them:
for v = usedVerts.count to 1 by -1 do deleteVert newObject usedVerts[v]
--assign wirecolor depending on the option
newObject.wirecolor = if rancolor_chk.state then random black white else obj.wirecolor
)
if del_bot.checked do delete obj --if requested, delete the original
delete theClone --delete the temp. clone
)--end obj loop
)--end undo
)--end on
on name_rd changed val do --enable/disable controls depending on radio buttons
name_edt.enabled = val == 2
on detachByID_Rollout open do name_rd.changed 1
)
if detachByID_Floater != undefined do closeRolloutFloater detachByID_Floater
detachByID_Floater = newRolloutFloater "Detacher" 140 140
addRollout detachByID_Rollout detachByID_Floater
-- script request by Eye of Hawk (CGTalk)
-- author: Galagast 05/05/24
-- Max R3 version: Bobo 06/12/16
)
😮
Hey Bobo, you did it again! It works like charm! Exactly what’s been needed!:bounce:
And “again” was because you were the one who originally did the “OffsetVertsByEdge” script for me:
http://forums.cgsociety.org/showthread.php?t=278342
Just so you know, these things you did (do) for me/us are still being heavily used For us – modders – these are holy tools
Thank you again, Bobo! :bowdown:
Take care! :wavey:
Cheers,
Oleg