ScriptSpot

Forum topic · General Scripting

Select by name filter, update and Matte material global override

By Freak · 2016-10-16

Downloads
Description

Hello everyone :)
I'm relatively new to Max scripting so I have few problems to solve which I didn't found on help or other forums.

I want my script to, when selecting geometry, filter only geometry when selecting by name (multiple objects - only Geometry - no helpers, cameras, shapes or so), the same thing with cameras. Also, to check those, I want them to print in the text box under the buttons and connect selected camera to render button.
Then, when my objects are selected and printed, on "Update" button, next to render button, I want to apply a Global MATTE material override to selected geometry (mental ray) on chosen radio button "Front" - when "Front" is chosen - it should apply MATTE to previously selected geometry MESH objects and when Back is selected to do vice versa, to apply on objects that are not selected and printed in the box - I don't know how to do that so I'm asking someone to help and I would really appreciate it.

The script is in the attachment. Thanks in advance.

Comments (11)

select camera without target

Freak · 2016-10-31

hello :) I have just one more problem to solve but can't figure it out so If somebody can help me with just one more line of code.
I want script to collect Camera (without target) when opening. It can be selected by select by name that is filtered in class of cameras ("isKindOf") and it's working but I can't add that filter when opening and collecting camera automatically. In my every scene the camera has the same name "CAM_2_Front" and script, when is collecting, it grabs target object with camera too. I tried to filter it in some other way but it's not working, I got "call needs function or class, got: $Target_Camera:CAM_2_Front @ [-1.777199,-434.206512,187.059662]" problem. Few days of trying and still can't figure it out. Here's the code:

try destroydialog ::AutoCamera catch()

Rollout AutoCamera "AutoCamera" width:155
(

button bt_SelectCamera "Select Camera" align:#left height:30 width:130
listbox list_cameras "" align:#left height:5 width:130
button bt_render "Render" border:false align:#center width:206 height:256

local theCameras = #()

----FUNCTIONS----
fn FilterCameras obj = isKindOf obj camera

fn selectCameras =
(
selCam = selectByName title:"Pick Camera" filter:FilterCameras showHidden:true
if selCam != undefined then
(
theCameras = selCam
list_cameras.items = for c in theCameras collect c.name
)else(
list_cameras.items = #()
theCameras = #()
)
)--end fn selectCameras

----EVENTS----
on AutoCamera open do
(
camName = $CAM_2_Front
for i in camName do
(
if camName isKindOf i camera do
(
camName2
)
)
openSelCam = camName2
if openSelCam != undefined then
(
theCameras = openSelCam
list_cameras.items = for c in theCameras collect c.name
)else(
list_cameras.items = #()
theCameras = #()
)
)--end onOpen

on bt_SelectCamera pressed do
(
if theCameras != undefined then selectCameras ()

)--end bt_SelectCamera

on bt_Render pressed do
(
cam = theCameras[list_cameras.selection]
fnRender = render camera:cam vfb:false outputwidth:128 outputheight:256
bt_Render.images = #(fnRender, undefined, 1,1,1,1,1 )
fnRender
)

)--END ROLLOUT

createdialog AutoCamera

Attachments

.

miauu · 2016-10-31

The same as in CGTalk:


camsARr = for o in cameras where classof o != targetobject collect o

Thank you again :)

Freak · 2016-10-31

Kostadin, thank you, again, and here on scriptspot!

Cheers ;)

removed space on the end

Freak · 2016-10-21

Thank you, Pixamoon. Worked like a charm.
I just had to use rendName = substring rendName 1 (rendName.count - 1) to make it work. I know that this was very simple problem but, for a newbie like me it took few hours to figure out where to put this line of code X) I will study your method for materials, thank you for sharing.

Cheers

`

pixamoon · 2016-10-22

cool, yes, just remove b.selected and previous line with b and u ready:


viewNode = sme.GetView (sme.activeView)
smeViewMats = #()
for n = 1 to trackViewNodes[#sme][(sme.activeView)].numSubs do (
m = trackViewNodes[#sme][(sme.activeView)][n].reference
if superclassof m == material do append smeViewMats m
)

print smeViewMats

Thank you! :D

Freak · 2016-10-23

You are genius and thank you for help :) Now I have 99% of script settled. Only I have one more issue to solve and still can't figure it out.. I want my sp_Material spinner to instantly change and assign those materials (which are collected from scene and in array and listed) to selected mesh or, if it is possible, let Max to found mesh in which has a name in it ("Head" in "MESH_Head_01" - that would be fantastic) and make that as a selection (just to avoid manually selecting in scene) I tried to access the material index via spinner.value but i got "--No ""get"" function for undefined" error. Here is the code and screenshot is in the attachment. try destroydialog ::CRM catch()

Rollout CRM "CRM" width:225
(
group "Character Def"
(
spinner sp_Pose "Pose" type:#integer range:[0,200,currentTime] align:#left across:2 width:100 height:20
spinner sp_Material "Material" type:#integer range:[0,20,1] align:#right width:100 height:20

edittext tx_Pose readOnly:true align:#left across:2 offset:[0,-5] width:100 height:18
edittext tx_Material readOnly:true align:#right offset:[0,-5] width:100 height:18
)
group "Select and Location"
(
button bt_selectFolder "..." tooltip:"Pick path for render" align:#left width:30 offset: [0,0] across:2 height:18
edittext tx_folder "" readonly:true align:#right height:18 width:165

button bt_SelectGeometry "Select Geometry" align:#right height:30 width:97 across:2
button bt_SelectCamera "Select Camera" align:#left height:30 width:97
listbox list_geometry "" align:#left height:5 width:97 across:2 readOnly:true enabled:true
listbox list_cameras "" align:#right height:5 width:97
button bt_SelectText "Select MaxText" align:#left height:30 width:97 across:2
button bt_SelectMaterials "Collect Materials" align:#right height:30 width:97
listbox list_text "" align:#left height:5 width:97 readOnly:true across:2 enabled:true
listbox list_materials "" align:#right height:5 width:97 readonly:true enabled:true

label rend_option "Render:" align:#left across:2
radiobuttons rad_options "" labels:#("Pose","FaceExpression") align:#left offsets:#([-50,0],[-40,0])
)
group "Update - Render"
(
--button bt_update "Update" align:#left across:2 width: 97 height:30
button bt_render "Render" border:false align:#center width:206 height:256
)

local matteMaterial
local theObjects = #()
local theCameras = #()
local outputFolder = undefined

local tpose = "t-pose"

on CRM open do
(
matteMaterial = Matte_Shadow_Reflection__mi catch_shadows:false ao_on:false catch_reflections:false

tx_Pose.text = tpose
tx_Material.text = "undefined"
)

fn FilterGeometry obj = isKindOf obj geometryClass and not isKindOf obj Targetobject --<< fn FilterCameras obj = isKindOf obj camera
fn FilterShapes obj = isKindOf obj shape

on sp_Pose changed ttime do
(

sliderTime = ttime

if ttime > 0 then
(
tx_Pose.text = if list_text.items[ ttime+1 ] != undefined then list_text.items[ttime+1] else ""
)
else if ttime == 0 then
(
tx_Pose.text = "t-pose"
)
)

on sp_Material changed FEx do
(
if sp_Material.value > 0 then (
tx_Material.text = if list_materials.items[sp_Material.value] != undefined then list_materials.items[sp_Material.value] else ""
) else (
tx_Material.text = ""
)

FEx = theMaterials[sp_Material.value]
face = $
face.material = FEx showInViewport:true

)

on bt_SelectGeometry pressed do
(
sel = selectByName title:"Pick" filter:FilterGeometry showHidden:true
if sel != undefined then
(
theObjects = sel
list_geometry.items = for j in theObjects collect j.name
)else(
list_geometry.items = #()
theObjects = #()
)
)

on bt_SelectCamera pressed do
(
sel = selectByName title:"Pick" filter:FilterCameras showHidden:true
if sel != undefined then
(
theCameras = sel
list_cameras.items = for c in theCameras collect c.name
)else(
list_cameras.items = #()
theCameras = #()
)
)

on bt_SelectText pressed do
(
sel = selectByName title:"Pick Text" filter:FilterShapes showHidden:true
if sel != undefined then
(
theObjects = sel

local arr = #()
for s in theObjects do (

for w in filterstring s.text "\n" do append arr (substring w 4 (w.count - 2))

)
list_text.items = arr

)
)

on bt_selectFolder pressed do
(
outputFolder = getSavePath()
if outputFolder != undefined then tx_folder.text = outputFolder else tx_folder.text = "undefined"
)

local theMaterials = #()
on bt_SelectMaterials pressed do
(
viewNode = sme.GetView (sme.activeView)
smeViewMats = #()
for n = 1 to trackViewNodes[#sme][(sme.activeView)].numSubs do
(
m = trackViewNodes[#sme][(sme.activeView)][n].reference
if superclassof m == material do append smeViewMats m
)

local selMats = smeViewMats
if selMats != undefined then
(
theMaterials = selMats
list_materials.items = for m in theMaterials collect m.name

)
else
(
list_materials.items = #()
theMaterials = #()
)

)

on bt_render pressed do
(
if outputFolder == undefined do return messagebox "No Output Folder Selected"
if theObjects.count == 0 do return messagebox "No Geometry Selected"
if theCameras.count == 0 do return messagebox "No Camera Selected"

setwaitcursor()

cam = theCameras[list_cameras.selection]

rendName = tx_Pose.text as string
local poseName = rendName = substring rendName 1 (rendName.count - 1)

case rad_options.state of
(
1:
(
matteObjects = theObjects
filename = outputFolder + "\\" + poseName + ".png"
)
2:
(
matteObjects = for j in geometry where finditem theObjects j == 0 collect j
filename = outputFolder + "\\" + poseName + "-" + "material_name" + ".png"
)
)

oldMaterials = for j in matteObjects collect #(j, j.material)

matteObjects.material = matteMaterial

fnRender = render camera:cam vfb:false outputwidth:128 outputheight:256 outputfile:filename
bt_Render.images = #(fnRender, undefined, 1,1,1,1,1 )
fnRender

for j in oldMaterials do j[1].material = j[2]

--messagebox ("Image Rendered to:\n" + filename)

setarrowcursor()
)
)

createdialog CRM

Attachments

`

pixamoon · 2016-10-23

:) you're welcome

didn't have time to check a code but u can simply add

if classof x == Array then ......

it will skip all non array values

or

if x != undefined then ......

but in your case it can't get indexed value in array, so first one is better

cheers,
Pixamoon

select camera without target

Freak · 2016-10-31

hello :)
Just so you know the script is working great and big thanks for your help. I've been learning MaxScript and modified my script just like I wanted and it's very addicting thing to learn in Max.
Only I have just one more problem to solve but can't figure it out so If you can help me with just one more line of code.
I want script to collect Camera (without target) when opening. It can be selected by select by name that is filtered in class of cameras ("isKindOf") and it's working but I can't add that filter when opening and collecting camera automatically. In my every scene the camera has the same name "CAM_2_Front" and script, when is collecting, it grabs target object with camera too. I tried to filter it in some other way but it's not working, I got "call needs function or class, got: $Target_Camera:CAM_2_Front @ [-1.777199,-434.206512,187.059662]" problem. Few days of trying and still can't figure it out. Here's the code:


try destroydialog ::AutoCamera catch()

Rollout AutoCamera "AutoCamera" width:155
(

button bt_SelectCamera "Select Camera" align:#left height:30 width:130
listbox list_cameras "" align:#left height:5 width:130
button bt_render "Render" border:false align:#center width:206 height:256

local theCameras = #()

----FUNCTIONS----
fn FilterCameras obj = isKindOf obj camera

fn selectCameras =
(
selCam = selectByName title:"Pick Camera" filter:FilterCameras showHidden:true
if selCam != undefined then
(
theCameras = selCam
list_cameras.items = for c in theCameras collect c.name
)else(
list_cameras.items = #()
theCameras = #()
)
)--end fn selectCameras

----EVENTS----
on AutoCamera open do
(
x = $CAM_2_Front
onlyCam = if classof x == Targetcamera and not classof x == TargetObject then x onlyCam
openSelCam = onlyCam
if openSelCam != undefined then
(
theCameras = openSelCam
list_cameras.items = for c in theCameras collect c.name
)else(
list_cameras.items = #()
theCameras = #()
)
)--end onOpen

on bt_SelectCamera pressed do
(
if theCameras != undefined then selectCameras ()

)--end bt_SelectCamera

on bt_Render pressed do
(
cam = theCameras[list_cameras.selection]
fnRender = render camera:cam vfb:false outputwidth:128 outputheight:256
bt_Render.images = #(fnRender, undefined, 1,1,1,1,1 )
fnRender
)

)--END ROLLOUT

createdialog AutoCamera

Attachments

thank you for response, pixamoon

Freak · 2016-10-21

I figured it out some other similar way. I've already settled script 90% but still have few problems to solve.
First problem looks very simple but still can't figure it out. When rendering to a image it appears to be a "space" between pose name and extension ".png" I've tried trimRight, replace, and token commands on it but none of that is working... The screenshot is below in the attachment.
Second problem is that I want to connect material spinner to cycle through materials somehow. Problem is that Compact Material Editor has 24 free slots to fill with materials but I need 40+. I was wondering is there a way to connect Materials in Slate Editor to a spinner in script to cycle through materials.

Here I will post whole script and describe problem as close as I can.

I'm doing still poses (with biped) for a character with switchable face expressions and render them as separate: Head and body - on the same pose i can switch and render facial expressions defined by a diffuse map in separate material (number of facial expressions = standard materials in scene). This is for a game and those rendered images (body and few different expressions) are merged to a complete character. Later on, this will be animated with switchable faces with dialog text above and so on..
Scene is made from biped (and mesh skinned to it) MaxText with predefined poses for character formatted as ("pose number", "(space)", "pose name") for example "04 welcoming" - pose number is also timeslider number in Max just to found specified poses more easily (it can be to 200 poses). Materials are node-based in SME (because compact ME contains only 24 free slots to stock materials and I need 40+) - standard, selfillumination:100, diffuse map (face expression). It can be more than 40 different expressions to switch on a face mesh so I want that to be automated with spinner somehow too. Temporarily I have setup for expressions using state sets (each set is different material) and it can be easily changed in between.

I've written a script with big help from Jorge Rodríguez on forum "CGsociety" Everything works perfectly, just as I wanted. Only thing that I want to do to finish the script is to add a function to this spinner on top right of the script - material change. It should be a switchable materials in array with named materials in edittext - the same thing with rendering - only this time, in name of .png of expressions, it is named "pose name", "-" "facial expression(material) name" for example "welcoming-happy.png" Also, the diffuse map name is the same name as it is material for example: happy.jpg as a diffuse map to a happy.material.

All that I want is to connect pose spinner to a variable strings by changing time/poses and material spinner to expressions/materials. Sry for bad English, I'm trying to explain this as clear as possible. The code for whole script is below. Thank you in advance and I hope that there must be some kind of solution for that situation.

try destroydialog ::CRM catch()

Rollout CRM "CRM" width:225
(
	group "Character Def"
		(
		spinner sp_Pose "Pose" type:#integer range:[0,200,currentTime] align:#left across:2 width:100 height:20
		spinner sp_Material "Material" type:#integer range:[0,20,1] align:#right width:100 height:20
		
		edittext tx_Pose readOnly:true align:#left across:2 offset:[0,-5] width:100 height:18
		edittext tx_Material readOnly:true align:#right offset:[0,-5] width:100 height:18
		)
	group "Select and Location"
		(
		button bt_selectFolder "..." tooltip:"Pick path for render" align:#left width:30 offset: [0,0] across:2 height:18
		edittext tx_folder "" readonly:true align:#right height:18 width:165
		
		button bt_SelectGeometry "Select Geometry" align:#right height:30 width:97 across:2
		button bt_SelectCamera "Select Camera"   align:#left  height:30 width:97
		listbox list_geometry "" align:#left  height:5 width:97 across:2 readOnly:true enabled:true
		listbox list_cameras "" align:#right height:5 width:97
		button bt_SelectText "Select MaxText" align:#left height:30 width:97
		listbox list_text "" align:#left  height:5 width:97 readOnly:true enabled:true	
			
		label rend_option "Render:" align:#left across:2
		radiobuttons rad_options "" labels:#("Pose","FaceExpression") align:#left offsets:#([-50,0],[-40,0])
		)
	group "Update - Render"
		(
		button bt_update "Update" align:#left across:2 width: 97 height:30
		button bt_render "Render" align:#right width:97 height:30
		)
	
	local matteMaterial
	local theObjects = #()
	local theCameras = #()
	local outputFolder = undefined
	
	local tpose = "t-pose"
	
	on CRM open do
	(
		matteMaterial = Matte_Shadow_Reflection__mi catch_shadows:false ao_on:false catch_reflections:false
		
		tx_Pose.text = tpose
		tx_Material.text = "undefined"
	)
	
	fn FilterGeometry obj = isKindOf obj geometryClass and not isKindOf obj Targetobject --<<<it should list only geometry objects but it also lists bones in selectByName window for some reason
	fn FilterCameras  obj = isKindOf obj camera
	fn FilterShapes obj = isKindOf obj shape	
	
	on sp_Pose changed ttime do
(
	sliderTime = ttime
	
		if ttime > 0 then (
			tx_Pose.text = if list_text.items[ ttime+1 ] != undefined then list_text.items[ttime+1] else ""
		) else (
			tx_Pose.text = ""		
		)

)

	
	on bt_SelectGeometry pressed do
	(
		sel = selectByName title:"Pick" filter:FilterGeometry showHidden:true
		if sel != undefined then
		(
			theObjects = sel
			list_geometry.items = for j in theObjects collect j.name
		)else(
			list_geometry.items = #()
			theObjects = #()
		)
	)

	on bt_SelectCamera pressed do
	(
		sel = selectByName title:"Pick" filter:FilterCameras showHidden:true
		if sel != undefined then
		(
			theCameras = sel
			list_cameras.items = for j in theCameras collect j.name
		)else(
			list_cameras.items = #()
			theCameras = #()
		)
	)

	
	
	on bt_SelectText pressed do
	(
		sel = selectByName title:"Pick Text" filter:FilterShapes showHidden:true
		if sel != undefined then
		(
			theObjects = sel
			
			local arr = #()
			for s in theObjects do (
				
				for w in filterstring s.text "\n" do append arr (substring w 4 (w.count - 2))
				
			)
			list_text.items = arr
			
		)
	)		
	
	
	
	on bt_selectFolder pressed do
	(
		outputFolder = getSavePath()
		if outputFolder != undefined then tx_folder.text = outputFolder else tx_folder.text = "undefined"
	)
	
	on bt_render pressed do 
	(
		if outputFolder == undefined do return messagebox "No Output Folder Selected"
		if theObjects.count == 0 do return messagebox "No Geometry Selected"
		if theCameras.count == 0 do return messagebox "No Camera Selected"
		
		setwaitcursor()
		
	
		cam = theCameras[list_cameras.selection]
		rendName = tx_Pose.text
		
		case rad_options.state of
		(
			1:
			(
				matteObjects = theObjects
				filename = outputFolder + "\\" + rendName + ".png"
			)
			2:
			(
				matteObjects = for j in geometry where finditem theObjects j == 0 collect j
				filename = outputFolder + "\\" + rendName + "-" + "material_name" + ".png"
			)
		)
		

		oldMaterials = for j in matteObjects collect #(j, j.material)
		
		matteObjects.material = matteMaterial
		
		render camera:cam vfb:false outputwidth:128 outputheight:256 outputfile:filename
		
		for j in oldMaterials do j[1].material = j[2]
		
		--messagebox ("Image Rendered to:\n" + filename)
		
		setarrowcursor()
	)
)

createdialog CRM
Attachments

`

pixamoon · 2016-10-21

bit busy today, but to get materials from SME take a look on my method here:
http://www.scriptspot.com/3ds-max/scripts/sme-get-selected-materials

it's getting only selected but it is easy to create new view in SME and just get all materials from there

to remove space on the end:


if rendName[rendName.count] == " " then rendName = substring rendName 1 (rendName.count - 1)

cheers,
pixamoon

`

pixamoon · 2016-10-20

hi, here is a small info how Select button should look like:


on bnSelGeom pressed do
(
	fn selmesh obj = (isKindOf obj geometryClass and not isKindof obj TargetObject)
	chosenMesh = selectByName title:"Pick" buttontext:"Pick" filter:selmesh showHidden:true
	nameMesh.text = ""
	for o in chosenMesh do nameMesh.text += o.name + "\n"    
)

but can u explain more how Front and Back should work ?

Cheers,
Pixamoon