Hard Light Productions Forums

Modding, Mission Design, and Coding => The Modding Workshop => Topic started by: zookeeper on January 04, 2011, 03:01:51 pm

Title: Useful scripts for 3ds Max
Post by: zookeeper on January 04, 2011, 03:01:51 pm
I've written a few scripts to help myself perform some of the most tedious routines involved in setting up and converting models in Max. No reason to not share them, so here you go:

1. reset_Xform_on_selected.ms - this is for reseting xform on everything right before exporting the ship. Because reseting xform on an object messes up the transforms of its children, you'd usually have to either unlink everything, reset xform and then re-link everything, which can get really tedious. This script does that for you, so all you have to do is select everything and run the script.

2. setup_turret_vecs.ms - this is for setting up fvecs/uvecs. It has a notable limitation of not being able to handle angled turrets for which xform has already been reseted, but that doesn't bother me since I always take care to not do that in a working file.

3. scale_orbs_for_export.ms - this is for allowing you to easily setup subsystem/thruster orbs in Max. Instead of placing those points as dummies, place them as spheres with a size of your choosing. Then, before exporting run the script and it should scale each point so that they'll end up being the right size in PCS2. Note: reseting XForms must be done before running this script, not after.

4. old_turrets_to_spicious_style.ms - do you have turrets setup the old way and they won't work when imported in new PCS2 builds? Running this script should convert the old-style turret hierarchy to the one expected by Spicious's newer builds.

All are written on Max 2009; I think they'll work on somewhat earlier versions too, but I don't know for sure. I'm sure there's bugs and some things I've missed, so feel free to point those out, although I won't promise to fix any issues I don't myself find important. Don't forget to read the comments inside the script for more details of use.

Note: all of the scripts tend to act based on object names: for example, setup_turret_vecs assumes that all objects whose name matches *turret* (except those which match *turret*-arm* or *turret*-destroyed*) are indeed turret bases, and strange things are bound to happen if any other objects have names which match that.

Finally, if you've written or know of any other maxscripts which are useful for modders, post/link them here!

EDIT: reset_Xform_on_selected had a bug which caused the objects to not get linked back together - fixed.
EDIT2: reset_Xform_on_selected now resets only objects which are selected, whereas the previous version(s) reseted selected objects + all their children. It also now automatically collapses the stack of every reseted object.
EDIT3: setup-turret-vecs wasn't correctly inverting the third fvec component, leading to turrets firing in reverse, and it also didn't overwrite already existing properties as advertised. Both fixed.
EDIT4: reverted the bogus fvec fix mentioned above.

reset_Xform_on_selected.ms
Code: [Select]
-- This script resets xform on all selected nodes. The built-in reset xform
-- function is inadequate because it cannot handle linked objects correctly.
-- This script unlinks each object from its children first before reseting
-- its xform and then linking the children back.
--
-- NOTE: this script seems to sometimes not work on objects which have
-- an active subobject selection.

undo "Reset XForm on selected" on (
for obj in selection do (
children = #()

for child in obj.children do (
append children child
)

for child in children do (
child.parent = undefined
)

collapseStack obj
ResetXForm obj
collapseStack obj

if children.count >= 1 then (
for child in children do (
child.parent = obj
)
)
)
)

setup-turret-vecs.ms
Code: [Select]
-- This script helps in setting up fvec/uvec values for turrets.
-- However, for angled turrets it'll only work as long as the turret
-- retains its rotation; if you've reseted xform on an angled turret
-- already, then its own rotation information has been lost and the
-- script no longer works right.
--
-- Instructions: select the turrets you wish to setup, run the script
-- and adjust the sliders until the fvec (green) points along the
-- direction of the barrels and uvec (blue) points directly upwards,
-- then hit "Apply".
--
-- IMPORTANT NOTE:
--
-- It's unlikely that you can setup all turrets at once; most likely
-- you'll have to do them in groups. The script uses the turret's
-- existing rotation as a starting point, and if the selection contains
-- turrets which have been rotated in different ways then the script
-- will "suggest" different kinds of fvecs/uvecs for them, in which
-- case you will simply have to setup them separately.

turrets = for turret in selection where ((matchPattern turret.name pattern:"*turret*") and (not matchPattern turret.name pattern:"*turret*-arm*") and (not matchPattern turret.name pattern:"*turret*-destroyed*")) collect turret

rollout turretVecSetup "FS2O Turret fvec/uvec setup" (
slider scale_helpers "Scale helpers" orient:#vertical type:#float range:[0,5,1] height:100

slider rot_x "Rotate X" orient:#horizontal type:#integer ticks:2 range:[0,3,0] width:100
slider rot_y "Rotate Y" orient:#horizontal type:#integer ticks:2 range:[0,3,0] width:100
slider rot_z "Rotate Z" orient:#horizontal type:#integer ticks:2 range:[0,3,0] width:100

button apply_btn "Apply"
button exit_btn "Exit"

fn update_helpers = (
for turret in turrets do (
fvec_helper = getnodebyname(turret.name + "_fvec_helper")
uvec_helper = getnodebyname(turret.name + "_uvec_helper")

uvec_helper.rotation = turret.rotation

in coordsys turret (
rotate uvec_helper (eulerangles (rot_x.value * 90) (rot_y.value * 90) (rot_z.value * 90))
)

uvec_helper.pos = turret.pos

in coordsys uvec_helper (
fvec_helper.rotation = uvec_helper.rotation
rotate fvec_helper (eulerangles 0 -90 0)
)
)
)

fn clean_up = (
for turret in turrets do (
fvec_helper = getnodebyname(turret.name + "_fvec_helper")
uvec_helper = getnodebyname(turret.name + "_uvec_helper")

if fvec_helper != undefined then delete fvec_helper
if uvec_helper != undefined then delete uvec_helper
)
)

on turretVecSetup open do (
for turret in turrets do (
fvec_helper = cylinder name:(turret.name + "_fvec_helper") radius:1 height:20 wirecolor:(color 0 255 0)
uvec_helper = cone name:(turret.name + "_uvec_helper") radius1:3 radius2:0 height:10 wirecolor:(color 0  0 255)
uvec_helper.rotation = turret.rotation

in coordsys turret (
fvec_helper.pos = [0,0,0]
uvec_helper.pos = [0,0,0]
)
)

update_helpers()
)

on turretVecSetup close do (
clean_up()
)

on scale_helpers changed val do (
for turret in turrets do (
fvec_helper = getnodebyname(turret.name + "_fvec_helper")
uvec_helper = getnodebyname(turret.name + "_uvec_helper")

in coordsys local (
ResetScale fvec_helper
ResetScale uvec_helper
fvec_helper.objectoffsetscale = [scale_helpers.value,scale_helpers.value,scale_helpers.value]
uvec_helper.objectoffsetscale = [scale_helpers.value,scale_helpers.value,scale_helpers.value]
)
)
)

on rot_x changed val do (
update_helpers()
)
on rot_y changed val do (
update_helpers()
)
on rot_z changed val do (
update_helpers()
)

on apply_btn pressed do (
overwrote_props = false

for turret in turrets do (
props = getUserPropBuffer turret

fvec_helper = getnodebyname(turret.name + "_fvec_helper")
uvec_helper = getnodebyname(turret.name + "_uvec_helper")

in coordsys uvec_helper (
fvec_helper.pos = [-10,0,0]
uvec_helper.pos = [0,0,10]
)

fvec = normalize -(turret.pos - fvec_helper.pos)
uvec = normalize -(turret.pos - uvec_helper.pos)

fvec.x = (formattedPrint fvec.x format:"2.3f") as float
fvec.y = (formattedPrint fvec.y format:"2.3f") as float
fvec.z = (formattedPrint fvec.z format:"2.3f") as float
uvec.x = (formattedPrint uvec.x format:"2.3f") as float
uvec.y = (formattedPrint uvec.y format:"2.3f") as float
uvec.z = (formattedPrint uvec.z format:"2.3f") as float

fvec_prop = "$fvec: " + fvec.x as string + "," + fvec.z as string + "," + fvec.y as string
uvec_prop = "$uvec: " + uvec.x as string + "," + uvec.z as string + "," + uvec.y as string

if (matchPattern props pattern:"*?vec*") then (
props = ""
overwrote_props = true

print (turret.name + " already had an fvec, uvec or both set, all old subobject properties overwritten!")
)

props += "\r\n" + fvec_prop + "\r\n" + uvec_prop + "\r\n"

setUserPropBuffer turret props

delete fvec_helper
delete uvec_helper
)

if overwrote_props then (
rollout props_overwritten_note "Subobject properties overwritten" (
label note1_lbl "All previous subobject properties of turrets which already had fvec/uvec set were overwritten."
label note2_lbl "Look in the maxscript listener to see a list of those turrets."
label note3_lbl "Undo is available."
button ok_btn "Ok"

on ok_btn pressed do (
destroyDialog props_overwritten_note
)
)

createDialog props_overwritten_note width:600 height:100
)
)

on exit_btn pressed do (
clean_up()

destroyDialog turretVecSetup
)
)

createDialog turretVecSetup

scale_orbs_for_export.ms
Code: [Select]
orbs= #()

for b in $bay* where (matchpattern b.name pattern:"*-*") do (
append orbs b
)

for p in $path* where (matchpattern p.name pattern:"*-*") do (
append orbs p
)

for s in $subsystem* do (
append orbs s
)

for t in $thruster* where not (matchpattern t.name pattern:"thrusters*") do (
append orbs t
)

for o in orbs do (
upscale = (o.max.x - o.min.x) / 2
downscale = 1 / (o.max.x - o.min.x)

scale o [downscale,downscale,downscale]

ResetScale o

scale o [upscale,upscale,upscale]
)

old_turrets_to_spicious_style.ms
Code: [Select]
for t in $turret* where not (matchpattern t.name pattern:"*-destroyed") and not (matchpattern t.name pattern:"*-arm") do (
for h in t.children where (matchpattern h.name pattern:"*helper*") do (
for a in t.children where (matchpattern a.name pattern:"*-arm") do (
h.parent = a
)

for f in h.children where (matchpattern f.name pattern:"*firepoints*") do (
f.name = substituteString f.name "firepoints" "multifirepoints"
)
)
)
Title: Re: Useful scripts for 3ds Max
Post by: Scooby_Doo on January 09, 2011, 04:50:07 am
How far down the hierarchy does the reset xform script go? 

For turrets you need the parent (turret), it's child (helper), that childs child (firepoints) and that childs children (firepoint).  Same depth with the thrusters
Title: Re: Useful scripts for 3ds Max
Post by: zookeeper on January 09, 2011, 05:32:17 am
How far down the hierarchy does the reset xform script go? 

For turrets you need the parent (turret), it's child (helper), that childs child (firepoints) and that childs children (firepoint).  Same depth with the thrusters
It goes all the way: it applies to the selected objects and recursively to all their children, even to ones which aren't selected. Arguably it could be smarter than that and only reset the objects which are actually selected, but it's a bit more complicated to handle that way so I didn't do that (yet), as the intended use is a mass-reset of everything right before exporting the model.

EDIT: Looks like I was mistaken; it wasn't really any more complicated to do, so I've updated the first post with a version which only resets the selected objects and also collapses the stack so you don't have to do it manually.
Title: Re: Useful scripts for 3ds Max
Post by: The E on January 09, 2011, 06:17:11 am
Stickied due to usefulness
Title: Re: Useful scripts for 3ds Max
Post by: mjn.mixael on January 09, 2011, 08:55:23 am
yeah, these are pretty much awesome! Good work!  :yes:
Title: Re: Useful scripts for 3ds Max
Post by: Kusanagi on January 10, 2011, 05:07:42 am
Will these keep the orientation for firingpoints?
Title: Re: Useful scripts for 3ds Max
Post by: zookeeper on January 10, 2011, 06:34:49 am
Will these keep the orientation for firingpoints?

No, but it's worth noting that you don't need to set the firepoint normals in pcs2 if the turret has fvecs and uvecs set.
Title: Re: Useful scripts for 3ds Max
Post by: Spicious on January 10, 2011, 02:31:07 pm
The importer looks for a helper called vec* now. Try exporting something with uvec and fvec.
Title: Re: Useful scripts for 3ds Max
Post by: Kusanagi on January 10, 2011, 03:43:07 pm
So let me see if I have this straight.

I have a few turrets that I need to add to a ship both blob and multiparts which are in their own separate MAX scenes so I can merge them into the existing scene.

For a blob turret, I would just import it, group it (with its three helpers) and move them to where they need to go with the angles. Ungroup, run the fvec/uvec script, and then run the reset xform script?

For multiparts, the process is the same?
Title: Re: Useful scripts for 3ds Max
Post by: zookeeper on January 10, 2011, 03:48:18 pm
The importer looks for a helper called vec* now. Try exporting something with uvec and fvec.

So if a turret has a helper called vec* attached to it (does there need to be another helper in between?), the importer automatically creates fvecs and uvecs for the turret based on the rotation of the vec helper? That's a great feature.
Title: Re: Useful scripts for 3ds Max
Post by: zookeeper on January 10, 2011, 03:52:41 pm
So let me see if I have this straight.

I have a few turrets that I need to add to a ship both blob and multiparts which are in their own separate MAX scenes so I can merge them into the existing scene.

For a blob turret, I would just import it, group it (with its three helpers) and move them to where they need to go with the angles. Ungroup, run the fvec/uvec script, and then run the reset xform script?

For multiparts, the process is the same?

Well, yes, that's how it works for multiparts. I'm not sure how it is for blob turrets, but I think at some point a change was made which makes blob turrets use the uvec (if provided) as the firepoint normal instead of the one you can set in pcs2. If that's the case then yeah, you the same process works for blob turrets as well, although the fvec doesn't need to point in the right direction (since there isn't a right direction).
Title: Re: Useful scripts for 3ds Max
Post by: Scooby_Doo on January 10, 2011, 03:57:37 pm
You should also collapse the stack BEFORE doing a reset Xform.  If the stack isn't collapsed some odd things can happen that's irreversible.
Title: Re: Useful scripts for 3ds Max
Post by: Kusanagi on January 10, 2011, 09:47:33 pm
There never was any stack on it, heh.

I think this goes back to you giving me a hand with the assault frigate and what a wonderful time we had with that, Scooby :P
Title: Re: Useful scripts for 3ds Max
Post by: Scooby_Doo on January 10, 2011, 10:11:20 pm
Trust me... if the objects modifier stack isn't collapsed, you can get some nasty results when you least expect it (like when you forgot to save before hand LOL)

Edit: This i think is all you need to change
Code: [Select]
--print ("Reseting XForm on " + node.name)
collapseStack obj
ResetXForm obj
collapseStack obj
Title: Re: Useful scripts for 3ds Max
Post by: Spicious on January 11, 2011, 01:49:42 am
So if a turret has a helper called vec* attached to it (does there need to be another helper in between?), the importer automatically creates fvecs and uvecs for the turret based on the rotation of the vec helper? That's a great feature.
All helpers should be a child of the "helper" node.
Title: Re: Useful scripts for 3ds Max
Post by: zookeeper on January 11, 2011, 05:44:19 am
Fixed two bugs in setup-turret-vecs.

EDIT: Sigh. The fvec bug wasn't a bug at all, I just thought so because the ship I was converting was facing backwards.
Title: Re: Useful scripts for 3ds Max
Post by: Reprobator on February 26, 2011, 08:32:38 am
Lincks are deleted, can you repost them please?
Title: Re: Useful scripts for 3ds Max
Post by: Scooby_Doo on March 13, 2011, 09:26:10 pm
Lincks are deleted, can you repost them please?

Agreed. I'm looking for the vector one
Title: Re: Useful scripts for 3ds Max
Post by: zookeeper on March 14, 2011, 02:01:32 am
Oops, sorry. :ick: They're back now. Feel free to host them permanently somewhere and post a link here.
Title: Re: Useful scripts for 3ds Max
Post by: Scooby_Doo on March 14, 2011, 02:35:04 am
How about this:

setup_turret_vecs.txt
Code: [Select]
-- This script helps in setting up fvec/uvec values for turrets.
-- However, for angled turrets it'll only work as long as the turret
-- retains its rotation; if you've reseted xform on an angled turret
-- already, then its own rotation information has been lost and the
-- script no longer works right.
--
-- Instructions: select the turrets you wish to setup, run the script
-- and adjust the sliders until the fvec (green) points along the
-- direction of the barrels and uvec (blue) points directly upwards,
-- then hit "Apply".
--
-- IMPORTANT NOTE:
--
-- It's unlikely that you can setup all turrets at once; most likely
-- you'll have to do them in groups. The script uses the turret's
-- existing rotation as a starting point, and if the selection contains
-- turrets which have been rotated in different ways then the script
-- will "suggest" different kinds of fvecs/uvecs for them, in which
-- case you will simply have to setup them separately.

turrets = for turret in selection where ((matchPattern turret.name pattern:"*turret*") and (not matchPattern turret.name pattern:"*turret*-arm*") and (not matchPattern turret.name pattern:"*turret*-destroyed*")) collect turret

rollout turretVecSetup "FS2O Turret fvec/uvec setup" (
slider scale_helpers "Scale helpers" orient:#vertical type:#float range:[0,5,1] height:100

slider rot_x "Rotate X" orient:#horizontal type:#integer ticks:2 range:[0,3,0] width:100
slider rot_y "Rotate Y" orient:#horizontal type:#integer ticks:2 range:[0,3,0] width:100
slider rot_z "Rotate Z" orient:#horizontal type:#integer ticks:2 range:[0,3,0] width:100

button apply_btn "Apply"
button exit_btn "Exit"

fn update_helpers = (
for turret in turrets do (
fvec_helper = getnodebyname(turret.name + "_fvec_helper")
uvec_helper = getnodebyname(turret.name + "_uvec_helper")

uvec_helper.rotation = turret.rotation

in coordsys turret (
rotate uvec_helper (eulerangles (rot_x.value * 90) (rot_y.value * 90) (rot_z.value * 90))
)

uvec_helper.pos = turret.pos

in coordsys uvec_helper (
fvec_helper.rotation = uvec_helper.rotation
rotate fvec_helper (eulerangles 0 -90 0)
)
)
)

fn clean_up = (
for turret in turrets do (
fvec_helper = getnodebyname(turret.name + "_fvec_helper")
uvec_helper = getnodebyname(turret.name + "_uvec_helper")

if fvec_helper != undefined then delete fvec_helper
if uvec_helper != undefined then delete uvec_helper
)
)

on turretVecSetup open do (
for turret in turrets do (
fvec_helper = cylinder name:(turret.name + "_fvec_helper") radius:1 height:20 wirecolor:(color 0 255 0)
uvec_helper = cone name:(turret.name + "_uvec_helper") radius1:3 radius2:0 height:10 wirecolor:(color 0  0 255)
uvec_helper.rotation = turret.rotation

in coordsys turret (
fvec_helper.pos = [0,0,0]
uvec_helper.pos = [0,0,0]
)
)

update_helpers()
)

on turretVecSetup close do (
clean_up()
)

on scale_helpers changed val do (
for turret in turrets do (
fvec_helper = getnodebyname(turret.name + "_fvec_helper")
uvec_helper = getnodebyname(turret.name + "_uvec_helper")

in coordsys local (
ResetScale fvec_helper
ResetScale uvec_helper
fvec_helper.objectoffsetscale = [scale_helpers.value,scale_helpers.value,scale_helpers.value]
uvec_helper.objectoffsetscale = [scale_helpers.value,scale_helpers.value,scale_helpers.value]
)
)
)

on rot_x changed val do (
update_helpers()
)
on rot_y changed val do (
update_helpers()
)
on rot_z changed val do (
update_helpers()
)

on apply_btn pressed do (
overwrote_props = false

for turret in turrets do (
props = getUserPropBuffer turret

fvec_helper = getnodebyname(turret.name + "_fvec_helper")
uvec_helper = getnodebyname(turret.name + "_uvec_helper")

in coordsys uvec_helper (
fvec_helper.pos = [-10,0,0]
uvec_helper.pos = [0,0,10]
)

fvec = normalize -(turret.pos - fvec_helper.pos)
uvec = normalize -(turret.pos - uvec_helper.pos)

fvec.x = (formattedPrint fvec.x format:"2.3f") as float
fvec.y = (formattedPrint fvec.y format:"2.3f") as float
fvec.z = (formattedPrint fvec.z format:"2.3f") as float
uvec.x = (formattedPrint uvec.x format:"2.3f") as float
uvec.y = (formattedPrint uvec.y format:"2.3f") as float
uvec.z = (formattedPrint uvec.z format:"2.3f") as float

fvec_prop = "$fvec: " + fvec.x as string + "," + fvec.z as string + "," + fvec.y as string
uvec_prop = "$uvec: " + uvec.x as string + "," + uvec.z as string + "," + uvec.y as string

if (matchPattern props pattern:"*?vec*") then (
props = ""
overwrote_props = true

print (turret.name + " already had an fvec, uvec or both set, all old subobject properties overwritten!")
)

props += "\r\n" + fvec_prop + "\r\n" + uvec_prop + "\r\n"

setUserPropBuffer turret props

delete fvec_helper
delete uvec_helper
)

if overwrote_props then (
rollout props_overwritten_note "Subobject properties overwritten" (
label note1_lbl "All previous subobject properties of turrets which already had fvec/uvec set were overwritten."
label note2_lbl "Look in the maxscript listener to see a list of those turrets."
label note3_lbl "Undo is available."
button ok_btn "Ok"

on ok_btn pressed do (
destroyDialog props_overwritten_note
)
)

createDialog props_overwritten_note width:600 height:100
)
)

on exit_btn pressed do (
clean_up()

destroyDialog turretVecSetup
)
)

createDialog turretVecSetup

Title: Re: Useful scripts for 3ds Max
Post by: zookeeper on March 14, 2011, 03:01:02 am
How about this:
Right. Kinda obvious now that I think of it. :banghead:
Title: Re: Useful scripts for 3ds Max
Post by: zookeeper on July 15, 2011, 04:49:04 pm
Added two more little scripts which might be of use, and also (finally) put them all into [ code ] blocks instead of attachments so they don't get lost.
Title: Re: Useful scripts for 3ds Max
Post by: Scooby_Doo on July 16, 2011, 12:44:00 am
Here's a few useful ones:


Used to create properties for subobjects (if it's a turret with uvec, fvec it'll copy the values to the destroyed version)
Code: [Select]
macroScript GiveThemProperties category:"Freespace"
(
changeCount = 0

uVecValue = ""
fVecValue = ""

isThisATurret = false
turretDestroyedLocation = -1

-- Go through all the selected objects
for i = 1 to selection.count do
(
test = ""
dummyCount = 0

-- Let's see what we're actually got
if (findString selection[i].name "helper" != undefined) then
continue -- helper object
else if (findString selection[i].name "thruster" != undefined) then
continue -- thruster dummy points
else if (findString selection[i].name "bank" != undefined) then
continue -- missile, gun and glow banks
else if (findString selection[i].name "eye" != undefined) then
continue -- eyepoint
else if (findString selection[i].name "destroyed" != undefined) then
(
-- Possibly a destroyed turret (could be another subsystem)
turretDestroyedLocation = i
continue ;
)
else if (findString selection[i].name "-" != undefined) then
continue -- turret arms, destroyed subsystems
else if (findString selection[i].name "firepoint" != undefined) then
continue -- firepoints
else if (findString selection[i].name "turret" != undefined) then
(
isThisATurret = true
)

-- Retrieve the existing properties, we need to save the uvec,fvec
objectProperties = getUserPropBuffer selection[i]
listofProp = filterString objectProperties "\n\r"

foundUVec = false

-- Save the uvec if theres any
for j = 1 to listofProp.count do
(
if (findstring listofProp[j] "$uvec" != undefined) then
(
foundUVec = true
uVecValue = listofProp[j]
break
)
)

-- Save the fvec if there's any (there should be)
for j = 1 to listofProp.count do
(
if (findstring listofProp[j] "$fvec" != undefined) then
(
fVecValue = listofProp[j]
break
)
)

-- Paste the new data back to the object's property
test = "$special=subsystem\r\n$fov=180\r\n$name=" + selection[i].name

if (foundUVec) then
(
test = test + "\r\n" + uVecValue + "\r\n" + fVecValue
)
changeCount = changeCount + 1

setUserPropBuffer selection[i] test
)

--If this was a turret and had a destroyed version we need to copy the uvec and fvec if they exist
if (isThisATurret) then
(
if (turretDestroyedLocation != -1) then
(
test = uVecValue + "\r\n" + fVecValue
setUserPropBuffer selection[turretDestroyedLocation] test
)
)

if (changeCount == 0) then
(
MessageBox "Nothing useable was selected"
)
)


Remove ALL properties from anything
Code: [Select]
macroScript RemoveProperties category:"Freespace"
(

for i = 1 to selection.count do
(
setUserPropBuffer selection[i] ""
)
)


Numbering turrets (this also renames the turret0X-arm).  Just group select the turet, arm (if any), firepoints, firepoint, helper and destroyed (if exists) click the Renumber Turret and repeat for each additional turret, it'll convert their names to a numbered "turret0X" correctly.  It does not create uvecs or fvecs, use the other scripts for that. 
Code: [Select]
macroScript TurretNumber category:"Freespace"
(
turretNumber = 1

rollout rename_rollout "Turret Renumbering"
(
button ResetCount "Reset Turret Count"
button RenameBase "Renumber Turret"

on ResetCount pressed do
(
turretNumber = 1
)

on RenameBase pressed do
(
print "hello"

changeCount = 0

if (turretNumber < 10) then
newName = "turret0" + turretNumber as String
else
newName = "turret" + turretNumber as String

for loop = 1 to selection.Count do
(
oldname = selection[loop].name
if (findString oldName "turret" != undefined) then
(
if (findString oldName "-arm" != undefined) then
(
selection[loop].Name = newName + "-arm"
changeCount = changeCount + 1
)
else if (findString oldName "-destroyed" != undefined) then
(
selection[loop].Name = newName + "-destroyed"
changeCount = ChangeCount + 1
)
else
(
selection[loop].Name = newName
changeCount = changeCount + 1
)
)
)

if (changeCount == 0) then
(
MessageBox "Nothing useable was selected"
)
else
(
turretNumber = turretNumber + 1
)
)

)--end rollout

createDialog rename_rollout

)


Used to renumber engine thrusters objects (thruster01, thruster02, thruster03.....)  Note this does not create the properties (use the first script above)
Code: [Select]
macroScript EngineNumber category:"Freespace"
(
engineNumber = 1

rollout rename_rollout "Engine Renumbering"
(
button ResetCount "Reset Engine Count"
button RenameBase "Renumber Engine"

on ResetCount pressed do
(
engineNumber = 1
)

on RenameBase pressed do
(
changeCount = 0

if (engineNumber < 10) then
newName = "engine0" + engineNumber as String
else
newName = "engine" + engineNumber as String

for loop = 1 to selection.Count do
(
oldname = selection[loop].name
if (findString oldName "engine" != undefined) then
(
if (findString oldName "-destroyed" == undefined) then
(
selection[loop].Name = newName
changeCount = changeCount + 1
)
else
(
selection[loop].Name = newName + "-destroyed"
changeCount = changeCount + 1
)
)
)

if (changeCount == 0) then
(
MessageBox "Nothing useable was selected"
)
else
(
engineNumber = engineNumber + 1
)
)

)--end rollout

createDialog rename_rollout

)


Same as thrusters but for intakes (if you do have them)
Code: [Select]
macroScript IntakeNumber category:"Freespace"
(
intakeNumber = 1

rollout rename_rollout "Intake Renumbering"
(
button ResetCount "Reset Intake Count"
button RenameBase "Renumber Intake"

on ResetCount pressed do
(
intakeNumber = 1
)

on RenameBase pressed do
(
changeCount = 0

if (intakeNumber < 10) then
newName = "intake0" + intakeNumber as String
else
newName = "intake" + intakeNumber as String

for loop = 1 to selection.Count do
(
oldname = selection[loop].name
if (findString oldName "intake" != undefined) then
(
if (findString oldName "-destroyed" == undefined) then
(
selection[loop].Name = newName
changeCount = changeCount + 1
)
else
(
selection[loop].Name = newName + "-destroyed"
changeCount = changeCount + 1
)
)
)

if (changeCount == 0) then
(
MessageBox "Nothing useable was selected"
)
else
(
intakeNumber = intakeNumber + 1
)
)

)--end rollout

createDialog rename_rollout

)

Title: Re: Useful scripts for 3ds Max
Post by: Scooby_Doo on March 28, 2015, 04:45:50 am
4 years later!!  :eek2:

Oh you've added a box, modified it and then copied it many times (be it copy or instances doesn't matter).  Then you realize you probably should have named them better, whoops!  Well this script asks for a name then applies it plus padded number to it... AKA Box becomes Grip01, Grip02, Grip03.... Grip10, Grip11...etc
Rename script:
Code: [Select]
macroScript RenameObjects category:"Fast Utils"
(
rollout renameObjs_rollout "Renaming Objects" width:162 height:72
(
label lbl_Name "New Name"
edittext edit_NewName ""
button btnStart "Start"

on btnStart pressed  do
(
if (edit_NewName.text == "") then
(
MessageBox "You need to give it a name first"
return 0
)

if (selection.Count == 0) then
(
MessageBox "You need to select at least one entry first"
return 0
)

for loop = 1 to selection.Count do
(
-- This keeps the number formatting nice and neat
prefex = ""

upper = ceil ((selection.Count as float)/ (10 as float)) * 10
lower = ceil ((loop as float)/ (10 as float)) * 10

while (lower < upper) do
(
prefex = prefex + "0"
lower = lower * 10
)

selection[loop].Name = edit_NewName.Text + prefex + loop as String
)

destroydialog renameObjs_rollout
)
)

createDialog renameObjs_rollout

)

Next script, let's say you have all those boxes now named properly, but let's say you want to change something on them, thats when you realize you made a copy and not a reference/instance, oh  :mad:. Well this fixes that (with some caveats).  Simply select the parent object, then select all of the children and it will replace each of the children with a copy/instance/reference of the parent.  The selected object is then deleted. It will also orientate and scale them correctly, HOWEVER, if you have rotated and/or scaled the object to be replaced then did a xreset, the scale/angle info was lost and it'll simply put a new object in the (0,0,0) orientation and 100% scale.
Code: [Select]
macroScript ReplaceWithClones category:"Fast Utils"
(
rollout ReplaceObjs_rollout "Replace objects with clones" width:162 height:227
(
 
button btnSelectParent "Select as Parent" pos:[34,7] width:93 height:21
 
button btnProcessInstances "Replace Selected" pos:[49,191] width:56 height:21
edittext edtParentName "" pos:[4,36] width:149 height:25 enabled:false
radiobuttons rdo1 "Replace By..." pos:[10,69] width:73 height:62 labels:#("Copy", "Instance", "Reference") default:2
checkbox chk1 "Delete original" pos:[10,150] width:96 height:20 checked:true


on btnSelectParent pressed do
(
if (selection.count != 1) then
(
MessageBox "Missing object for parent"
return 0
)

parent = selection[1]
edtParentName.text = selection[1].Name
)
on btnProcessInstances pressed do
(
if (edtParentName.Text == "") then
(
MessageBox "First need to select a parent object to copy"
return 0
)

for loop = 1 to selection.Count do
(
case rdo1.state of
(
1:
child = copy parent
2:
child = instance parent
3:
child = reference parent
)

child.pos = selection[loop].pos
child.rotation = selection[loop].rotation
child.scale = selection[loop].scale

)

if (chk1.Checked) then
(
for loop = 1 to selection.Count do
(
delete selection[1]
)
)
)
)
createDialog ReplaceObjs_rollout

)

And last....
This isn't really useful for POF as exporting to DAE and PCS can handle it well, but it seems to clear up problems exporting to Unreal 4.  What this script does is save your selected objects first, make copies of those objects, collapse just those, stuff those into a layer "ExportLayer", select all those and let's you export however necessary.  Finally there is a cleanup Finish, which deletes those objects, removes the layer and then reselects the objects you had originally selected, as if nothing had happened.  It's not foolproof and might be buggy, thats why I had the collapsed meshes put into a unique layer, so if things go bad you can simply delete everything from that layer.  Oh and ya don't have a "ExportLayer" in your project.  It can take sometime to actually process, depending on how complicated your model is.  It takes about a half minute to export a 300K model with some 130+ objects.  Couple other notes: if you see a bug in your model after prepping, don't fix it there, it'll just get deleted, nuke that export (hit "finish") fix the problem and try again.  Two: Groups seem to cause some problems, they will appear as big boxes that are empty, just delete them for now, the actual meshes will still be there (they're no longer part of the group upon resetting).
Code: [Select]
macroScript PrepForExport category:"Fast Utils"
(
mySavedArray = #()
rollout ExportPrep_rollout "Prep for Export" width:162 height:68
(
 
button btnSelect "Select object(s)" pos:[34,7] width:93 height:21
 
button btnFinish "Finished exporting" pos:[34,40] width:93 height:21

on btnSelect pressed do
(
if (selection.Count == 0) then
(
MessageBox "You need to select at least one entry first"
return 0
)


myNewArray = #()

layer = LayerManager.newLayer ()
layer.setName "ExportLayer"

for loop = 1 to selection.count do
(
entry = copy selection[loop]
print entry.Name

collapseStack entry
ResetXForm entry
collapseStack entry
layer.addnode entry

append myNewArray entry

windows.processpostedmessages ()

)

for obj in selection do
(
append mySavedArray obj
windows.processpostedmessages ()
)
max select none
select myNewArray

result = IsolateSelection.EnterIsolateSelectionMode()

MessageBox "You may now export your model"
)
on btnFinish pressed do
(
max select none

layer = LayerManager.GetLayerFromName "ExportLayer"

if (layer == undefined) then
(
return 0
)

layer.nodes &temp

select temp
delete selection

LayerManager.DeleteLayerByName "ExportLayer"

result = IsolateSelection.ExitIsolateSelectionMode ()
select mySavedArray
)
)
createDialog ExportPrep_rollout

)

Whew!
Title: Re: Useful scripts for 3ds Max
Post by: Scooby_Doo on January 01, 2018, 10:13:10 pm
Two years latter I've got some more for you...

If you have more than one object you want to replace with something else this script makes it easy.  Let's say you have a bunch of the same object but decide you want to replace or modify it. You realize you forget to make references or instances of it. Opps...

First select the parent object, hit "select as parent" then select all the objects you want replaced, then hit "Replace Selected".  You can choose to copy, instance or replace and you can choose to ignore the scale and/or rotation.  And finally you don't have to actually delete the original one.  Couple of caveats: The new objects are stuck in whichever layer is currently active, it may not be the one that the original object is located in (that should be fixable), and if the object(s) to be replaced have been xform resetted, you probably won't get the correct results.
Code: [Select]
macroScript ReplaceWithClones category:"Fast Utils"
(
rollout ReplaceObjs_rollout "Replace objects with clones" width:162 height:227
(
 
button btnSelectParent "Select as Parent" pos:[34,7] width:93 height:21
 
 
button btnProcessInstances "Replace Selected" pos:[3,191] width:145 height:21
edittext edtParentName "" pos:[4,36] width:149 height:25 enabled:false
radiobuttons rdoReplaceBy "Replace By..." pos:[10,69] width:73 height:62 labels:#("Copy", "Instance", "Reference") default:2
checkbox chkDeleteOrg "Delete original" pos:[10,166] width:96 height:20 checked:true
checkbox chkIgnScale "Ignore scale" pos:[10,150] width:103 height:17
checkbox chkIgnRot "Ignore rotation" pos:[9,132] width:103 height:15

global parent

on btnSelectParent pressed do
(
if (selection.count != 1) then
(
MessageBox "Misisng object for parent"
return 0
)

if (selection.Count == 0) then
(
MessageBox "You need to select at least one entry first"
return 0
)

parent = selection[1]
edtParentName.text = selection[1].Name
)
on btnProcessInstances pressed do
(
if (edtParentName.Text == "") then
(
MessageBox "First need to select a parent object to copy"
return 0
)

props = getUserpropBuffer parent

for loop = 1 to selection.Count do
(
case rdoReplaceBy.state of
(
1:
child = copy parent
2:
child = instance parent
3:
child = reference parent
)

if (chkIgnScale.checked == false) then
(
child.scale = selection[loop].scale
)


if (chkIgnRot.checked == false) then
(
child.rotation = selection[loop].rotation
)


child.pos = selection[loop].pos
setuserPropBuffer child props

)

if (chkDeleteOrg.Checked) then
(
for loop = 1 to selection.Count do
(
delete selection[1]
)
)
)
)
createDialog ReplaceObjs_rollout

)
Title: Re: Useful scripts for 3ds Max
Post by: Scooby_Doo on January 01, 2018, 10:17:11 pm
LODDING

This script let's you hide/show lod levels quickly and easily.  You have 4 lod levels to use, simply select the object(s) you want on a specific level and hit the "+" next to the LOD level.  "-" will remove it from that level.  You can then use the show/hide buttons for those objects.  Hint: Objects can be on more than one lod level.

Code: [Select]
macroScript LodDisplay category:"Fast Utils"
(
rollout renameObjs_rollout "LODS" width:344 height:240
(
 

button btnShow0 "Show" pos:[48,66] width:107 height:34
button btnHide0 "Hide" pos:[160,66] width:104 height:32
label lbl4 "LOD 0" pos:[8,80] width:32 height:16
button btnAdd0 "+" pos:[272,66] width:32 height:32
button btnRemove0 "-" pos:[304,66] width:32 height:32
button btnShow1 "Show" pos:[48,104] width:107 height:34
button btnHide1 "Hide" pos:[160,104] width:104 height:32
label lbl7 "LOD 1" pos:[8,118] width:32 height:16
button btnAdd1 "+" pos:[272,104] width:32 height:32
button btnRemove1 "-" pos:[304,104] width:32 height:32
button btnShow2 "Show" pos:[48,144] width:107 height:34
button btnHide2 "Hide" pos:[160,144] width:104 height:32
label lbl8 "LOD 2" pos:[8,158] width:32 height:16
button btnAdd2 "+" pos:[272,144] width:32 height:32
button btnRemove2 "-" pos:[304,144] width:32 height:32
button btnShow3 "Show" pos:[48,184] width:107 height:34
button btnHide3 "Hide" pos:[160,184] width:104 height:32
label lbl9 "LOD 3" pos:[8,198] width:32 height:16
button btnAdd3 "+" pos:[272,184] width:32 height:32
button btnRemove3 "-" pos:[304,184] width:32 height:32

fn ShowChildren parent levelText =
(
for obj in parent do
(
test = getUserPropBuffer obj

if matchpattern test pattern:levelText then
unhide obj
)

for children in parent.children do
(
ShowChildren children levelText
)
)

fn HideChildren parent levelText =
(
for obj in parent do
(
test = getUserPropBuffer obj

if matchpattern test pattern:levelText then
hide obj
)

for children in parent.children do
(
HideChildren children levelText
)
)

on btnShow0 pressed do
(
for obj in rootscene.world.children do
(
ShowChildren obj "*lod=0*"
)

)

on btnHide0 pressed do
(
for obj in rootscene.world.children do
(
HideChildren obj "*lod=0*"
)
)

on btnAdd0 pressed do
(
for loop = 1 to selection.Count do
(
test = getUserPropBuffer selection[loop]
if not(matchpattern test pattern:"*lod=0*") then
(
test = test + "lod=0\r\n"
setUserPropBuffer selection[loop] test
)
)

)
on btnRemove0 pressed do
(
for loop = 1 to selection.Count do
(
buff = (getUserPropBuffer selection[loop]) as stringStream
newb = stringStream ""
while not eof buff do
(
str = readLine buff
if str != "" and not matchpattern str pattern:"*lod=0*" do format "%\r\n" str to:newb
)
setUserPropBuffer selection[loop] (replace_LF_with_CRLF (newb as string))

)
)
on btnShow1 pressed do
(
for obj in rootscene.world.children do
(
ShowChildren obj "*lod=1*"
)
)
on btnHide1 pressed do
(
for obj in rootscene.world.children do
(
HideChildren obj "*lod=1*"
)
)
on btnAdd1 pressed do
(
for loop = 1 to selection.Count do
(
test = getUserPropBuffer selection[loop]
if not(matchpattern test pattern:"*lod=1*") then
(
test = test + "lod=1\r\n"
setUserPropBuffer selection[loop] test
)
)

)
on btnRemove1 pressed do
(
for loop = 1 to selection.Count do
(
buff = (getUserPropBuffer selection[loop]) as stringStream
newb = stringStream ""
while not eof buff do
(
str = readLine buff
if str != "" and not matchpattern str pattern:"*lod=1*" do format "%\r\n" str to:newb
)
setUserPropBuffer selection[loop] (replace_LF_with_CRLF (newb as string))

)
)
on btnShow2 pressed do
(
for obj in rootscene.world.children do
(
ShowChildren obj "*lod=2*"
)
)
on btnHide2 pressed do
(
for obj in rootscene.world.children do
(
HideChildren obj "*lod=2*"
)
)
on btnAdd2 pressed do
(
for loop = 1 to selection.Count do
(
test = getUserPropBuffer selection[loop]
if not(matchpattern test pattern:"*lod=2*") then
(
test = test + "lod=2\r\n"
setUserPropBuffer selection[loop] test
)
)

)
on btnRemove2 pressed do
(
for loop = 1 to selection.Count do
(
buff = (getUserPropBuffer selection[loop]) as stringStream
newb = stringStream ""
while not eof buff do
(
str = readLine buff
if str != "" and not matchpattern str pattern:"*lod=2*" do format "%\r\n" str to:newb
)
setUserPropBuffer selection[loop] (replace_LF_with_CRLF (newb as string))

)
)
on btnShow3 pressed do
(
for obj in rootscene.world.children do
(
ShowChildren obj "*lod=3*"
)
)
on btnHide3 pressed do
(
for obj in rootscene.world.children do
(
HideChildren obj "*lod=3*"
)
)
on btnAdd3 pressed do
(
for loop = 1 to selection.Count do
(
test = getUserPropBuffer selection[loop]
if not(matchpattern test pattern:"*lod=3*") then
(
test = test + "lod=3\r\n"
setUserPropBuffer selection[loop] test
)
)

)
on btnRemove3 pressed do
(
for loop = 1 to selection.Count do
(
buff = (getUserPropBuffer selection[loop]) as stringStream
newb = stringStream ""
while not eof buff do
(
str = readLine buff
if str != "" and not matchpattern str pattern:"*lod=3*" do format "%\r\n" str to:newb
)
setUserPropBuffer selection[loop] (replace_LF_with_CRLF (newb as string))

)
)
)

createDialog renameObjs_rollout

)

How it works: It simply adds a property "lod" to the objects properties.
Title: Re: Useful scripts for 3ds Max
Post by: Scooby_Doo on January 01, 2018, 10:21:24 pm
Collapse Clones

Ever get the message in PCS2 that something couldn't be imported because it wasn't xform resetted?  But you don't want to do an xform reset because you'll lose inheritance data.  This comes to the rescue...  Simply select the object(s) and run script.  The original object will be cloned, that clone will be xform reset and the original object(s) will be hidden.  If you make changes to the original object latter, just rerun the script, it'll delete the original clone and make a new one.

Code: [Select]
macroScript CollapseClones category:"Fast Utils"
(
fn AddKey theObject key=
(
buff = getUserPropBuffer theObject
buff = buff + key

setUserPropBuffer theObject buff
)

fn RetrieveID theObj theKey =
(

buff = (getUserPropBuffer theObj) as stringStream
newb = stringStream ""

scankey = "*" + theKey + "*"
while not eof buff do
(
str = readLine buff
if str != "" and matchpattern str pattern:scankey do 
(
filtered = filterString str "="
return filtered[2]
)
)

return ""
)
fn RemoveKey object key =
(
buff = (getUserPropBuffer object) as stringStream
newb = stringStream ""

while not eof buff do
(
str = readLine buff
if str != "" and not matchpattern str pattern:("*" + key + "*") do format "%\r\n" str to:newb
)
setUserPropBuffer object (replace_LF_with_CRLF (newb as string))

)

fn DeleteChildren obj =
(
for loop = 1 to obj.Children.count do
(
DeleteChildren obj.Children[loop]
)

delete obj
)

fn RemoveExistingClones parentID =
(

for child in rootscene.world.children do
(

id = RetrieveID child "slave"

if matchpattern id pattern:parentID then
(
DeleteChildren child
)
)

)

fn CreateClone object uniqueID =
(
--Get the node and all it's children
maxOps.CloneNodes

-- Now create the clone
maxOps.CloneNodes object clonetype:#copy newnodes:&cloneNodes expandHierarchy:true

for loop = 1 to cloneNodes.count do
(
AddKey cloneNodes[loop] ("slave=" +  uniqueID + "\r\n")
--cloneNodes[loop].Name = cloneNodes[loop].Name + "-Cloned"
cloneNodes[loop].Name = (uniquename "cloned")


--First unlink children
tempChildren = #()
for child in cloneNodes[loop].Children do
(
append tempChildren child
child.parent = undefine

)

--Then collapse
collapseStack cloneNodes[loop]
ResetXForm cloneNodes[loop]
collapseStack cloneNodes[loop]

--Then relink
for child in tempChildren do
append cloneNodes[loop].Children child

)

)

fn HideOriginal obj =
(
for loop = 1 to obj.Children.Count do
(
HideOriginal obj.Children[loop]
)

hide obj
)

fn ResetXFormClone object =
(
for obj in object do
objectChildren = #()

for child in obj.children do
append objectChildren child


for child in objectChildren do
child.parent = undefined


--print ("Reseting XForm on " + node.name)
collapseStack obj
ResetXForm obj
collapseStack obj

if objectChildren.count >= 1 then
(
for child in objectChildren do
child.parent = obj
)
)


for loop = 1 to selection.count do
(
checkForSlave = RetrieveId selection[loop] "*slave=*"
if (checkForSlave != "") then
continue

-- Retrieve the existing unique ID, if any
uniqueID = RetrieveID selection[loop] "master"

if (uniqueID != "") then
(
RemoveExistingClones uniqueID
RemoveKey selection[loop] "master"
)

--Create a random parent ID value
randomValue = random 1000 9999
uniqueID = uniquename selection[loop].Name  + randomValue as string


CreateClone selection[loop] uniqueID

--Now add that name to the parents properties
AddKey selection[loop] ("master=" + uniqueID + "\r\n")

HideOriginal selection[loop]
)
)

How it works: it simply creates a random number name that it attaches to the master/slave.