CgTalk Maxscript Challenge 021: "More Information?"


#1

CgTalk Maxscript Challenge 021: “More Information?”

DESCRIPTION: Create a tool that gives you more information about the scene than would normally be available. This can involve use of viewport drawing, a floating dialog, in-scene geometry, automated camera renders, or even an external file (HTML/XML/TXT etc).
The aim here is to flex your data-mining and information presentation skills (something every good TA should have!).
Try to think of information that would be useful to have on hand, either dynamically or for archive purposes.

DEADLINE: 30/10/2008

RULES:
[ul]
[li]Do NOT post your code until the deadline![/li][li]Code from scratch. Try not to use pre-exisitng functions or plugins.[/li][li]Show your script references, if any (eg. Looking at another script to assist you).[/li][li]You are encouraged to ask for help where needed, but try to do it on your own. The maxscript reference is an invaluable resource.[/li][li]Post your final script inside [/li]```
tags (located on your posting toolbar).
[li]Post all code into THIS thread.[/li][li]Post the max version you coded in, plus any maxscript extensions you used. (Thanks galagast!)[/li][li]Try to finish the challenge in the proscribed time. There is no definite time limit.[/li][/ul]NOTES: Try to be creative with this one. Just exporting a text file of the summary info is not going to cut it!


#2

erilaz,

I wanted to participate in the challenge, and started a small material-info tool. However, in the past week, the tool has grown a lot and proven to be really useful. Therefor, we’ve decided to keep it in-house for now and develop it further.

Thanks for the inspiration though, and maybe I’ll be able to publish it some day.


#3

Excellent! The challenges are here for that purpose. Please share it if/when you can. The sharing of information is very important in the TD community.


#4

sharing the outline of what a script does is sometimes as useful as sharing the whole thing :wink: now i’m curious! lol


#5

LAME!

Just kidding of course Martijn, keep up the good work! Your scripting skills have improved a lot over the past year.

Cheers,
the other Martijn


#6

That shouldn’t be a problem :slight_smile: The main idea of the script is to check the map slots for all materials and collect these into an array. You can then filter this array in several ways and display them in a listView.

This way, you can easily find all materials that have a material in their reflection slot (for example), and disable/edit/delete them without ever opening the material editor. You can do this for multiple materials at the same time. You can also search materials/maps by name.

I’ve added a lot of options since the first version: for example, a filter to show all maps in the scene that have an output gamma that differs from your scene’s gamma setting (and have buttons to change it).

It might be useless for a lot of people, but I found it especially useful when cleaning up messy scenes.

magicm: Thanks, someone has to do it while you’re gone :wink:


#7

Have you ever wondered what the “average” color in your scene is? NO!?..well… neither have I. BUT NOW YOU CAN KNOW!

Didn’t have a lot of free time, so instead of coding something useful I decided to code something fun…


 (
 	fn averageColors colorArray = --returns the average color of an array of colors
 	(
 		local averageColor = color 0 0 0
 		FOR color in colorArray DO
 		(
 			averageColor = averageColor + color
 		)
 		averageColor = averageColor/colorArray.count
 		averageColor
 	)
 	
 	local sceneArea = 0 --a variable to hold the sum of the area of every object in the scene
 	local objectAreaArray = #() --an array to hold of each individual object
 	FOR o in geometry DO
 	(
 		local numFaces = meshop.getNumFaces o.mesh --get the number of faces
 		local objectArea = 0
 		FOR faceIndex = 1 to numFaces DO
 		(
 			objectArea = objectArea + (meshop.getFaceArea o.mesh faceIndex) --get the object's full area by adding the faces
 		)
 		append objectAreaArray objectArea
 		sceneArea = sceneArea + objectArea --add all the objects to get the full scene area
 	)
 	
 	local sceneColorsArray = #() --an array to hold the colors to be averaged
 	global objectColorsArray = #() --an array to hold the object's individual color to be used by the select button
 	FOR objectIndex = 1 to geometry.count DO --collect all colors 
 	(
 		IF geometry[objectIndex].material == undefined THEN --use the wirecolor for objects without a material
 		(
 			local percentArea = (objectAreaArray[objectIndex]/sceneArea as integer)*100 --find which percentage of the total scene area this object holds
 			FOR x = 1 to percentArea DO --add one color item to sceneColorsArray per percentage point
 			(
 				append sceneColorsArray geometry[objectIndex].wirecolor 
 			)
 			append objectColorsArray geometry[objectIndex].wirecolor 
 		)
 		ELSE
 		(
 			IF (classOf geometry[objectIndex].material) == Standardmaterial DO --only consider objects with standard materials (too lazy to code special cases, sorry)
 			(
 				IF geometry[objectIndex].material.diffusemap != undefined THEN --use the texture map in the diffuse channel if it exists
 				(
 					local tempMap = renderMap geometry[objectIndex].material.diffuseMap size:[20,20] scale:200 --render a sample of the texture map
 					local pixelArray = #()
 					FOR bitmapRow = 0 to 19 DO --collect the pixels from the rendered sample
 					(
 						local rowArray = getPixels tempMap [0,bitmapRow] 20 
 						FOR pixel in rowArray DO append pixelArray pixel
 					)
 					local averageColor =averageColors pixelArray --average the colected pixels
 					local percentArea = (objectAreaArray[objectIndex]/sceneArea as integer)*100 --find which percentage of the total scene area this object holds
 					FOR x = 1 to percentArea DO --add one color item to sceneColorsArray per percentage point
 					(
 						append sceneColorsArray averageColor
 					)
 					append objectColorsArray averageColor
 				)
 				ELSE --if no texture map exists in the difuse channel use the diffuse color
 				(
 					local percentArea = (objectAreaArray[objectIndex]/sceneArea as integer)*100 --find which percentage of the total scene area this object holds
 					FOR x = 1 to percentArea DO --add one color item to sceneColorsArray per percentage point
 					(
 						append sceneColorsArray geometry[objectIndex].material.diffuse
 					)
 					append objectColorsArray geometry[objectIndex].material.diffuse
 				)
 			)
 		)
 	)
 	
 	local averageColor
 	IF sceneColorsArray.count > 0 DO --find the average color from all the colors collected
 	(
 		averageColor = averageColors sceneColorsArray
 	)
 		
 	rollout ColorFinder "Color Finder"
 	(
 		label label01 "This scene's average color is:"
 		bitmap theColorBitmap align:#left width:80 
 		label label02 "R :" pos:(theColorBitmap.pos+[90,10])  align:#left
 		label label03 "G :" pos:(label02.pos+[0,30]) align:#left
 		label label04 "B :" pos:(label03.pos+[0,30]) align:#left
 		label label05 "H :" pos:(label02.pos+[65,0]) align:#left
 		label label06 "S :" pos:(label05.pos+[0,30]) align:#left
 		label label07 "V :" pos:(label06.pos+[0,30]) align:#left
 
 		group "Select by Color" 
 		(
 			colorpicker theColorPicker width:50 height:50
 			spinner theSpinner "Threshold :   " pos:(theColorPicker.pos+[65,5]) fieldWidth:50 range:[0,255,75] type:#integer
 			button isolateButton "Select" width:125 height:20 pos:(theColorPicker.pos+[65,30])
 		)
 		
 		ON isolateButton pressed DO
 		(
 			clearSelection()
 			targetColor = theColorPicker.color
 			threshold = theSpinner.value 
 			FOR colorIndex = 1 to objectColorsArray.count DO
 			(
 				IF objectColorsArray[colorIndex].r <= (targetColor.r+threshold) AND objectColorsArray[colorIndex].r >= (targetColor.r-threshold) AND
 					objectColorsArray[colorIndex].g <= (targetColor.g+threshold) AND objectColorsArray[colorIndex].g >= (targetColor.g-threshold) AND
 					objectColorsArray[colorIndex].b <= (targetColor.b+threshold) AND objectColorsArray[colorIndex].b >= (targetColor.b-threshold) 
 				DO selectmore geometry[colorIndex]
 			)
 		)
 	)
 	createDialog ColorFinder 240 190
 	colorFinder.theColorBitmap.bitmap = (bitmap 1 1 color:averageColor)
 	colorFinder.label02.text = "R :"+(averageColor.r as integer) as string
 	colorFinder.label03.text = "G :"+(averageColor.g as integer) as string
 	colorFinder.label04.text = "B :"+(averageColor.b as integer) as string
 	colorFinder.label05.text = "H :"+(averageColor.h as integer) as string
 	colorFinder.label06.text = "S :"+(averageColor.s as integer) as string
 	colorFinder.label07.text = "V :"+(averageColor.v as integer) as string
 )
 

The script will check all the objects in the scene and sample their average color (if it has a material applied it will render a small sample of the diffuse channel and average the pixels). Then the colors will be added to an array and averaged once again to get the overall scene color…pretty cool eh? (NOT!)
The script takes into account the surface area of the surface objects, that means that BIGGER objects will have more influence in the end result. This in turn means that the script has to calculate the area for every object in the scene which normally takes a prohibitive amount of time. So it’s useless AND slow!!

Finally I also added a few controls to allow the user to select items on the scene by their average color. Just set the color you wish to select and set the threshold according to your liking.

ENJOY!


#8

hahaha, useless AND slow :slight_smile:
actually i can imagine the above feature to be useful in some special situations. does this also consider maps like the rest of the script?


#9

Might be nice to create an “Start” button in your interface, instead of running the tool on evaluation.

I evaluated the script in a medium sized scene and it immediately crashed max with a memory error :wink:


#10

@pokoy:

Yes it does, however the script only works with standard materials. It could easily expanded to support other shaders… but what’s the point right?

@diffx:

It’s not a bug, it’s a feature…:blush:. I find it works best on scenes with less than 3 objects…


#11

well, then it’s 100% useless for me… in other words: this script totally fits my needs :smiley:


#12

this script is useful for preview scene and when you want obtain Focus plane in Depth of Field

http://vahid.3dmax.01.googlepages.com/FastRender.rar


#13

hey guys i read this thread and it gave me the idea to make a lil properties lister… nothing fancy but imo usefull for scripters.


try(closeRolloutFloater InfoRO)catch()
sleep 0.1

CurSelection=""

fn UpdateInfo = ()


rollout Stats "Statistics" (
	label TriCountlbl "Triangle Count . . . . ." width:90 pos:[180,5]
	label Tricount "NIL" width:90 pos:(TriCountlbl.pos+[80,0])

	label VertCountlbl "Vertex Count . . . . ." width:90 pos:(TriCountlbl.pos+[0,17])
	label Vertcount "NIL" width:90 pos:(VertCountlbl.pos+[80,0])
	
	label Class1lbl "Class . . . . . . . . . ." width:90 pos:[5,5]
	label Class1 "NIL" width:90 pos:(Class1lbl.pos+[80,0])
	
	label SuperClass1lbl "SuperClass . . . . ." width:90 pos:(Class1lbl.pos+[0,17])
	label SuperClass1 "NIL" width:90 pos:(SuperClass1lbl.pos+[80,0])
)


rollout InfoLister "Maxscript Properties" (

	pickbutton pck "Pick Object"
	
	edittext MSProperties "Class" width:190 height:18 pos:[20,35]
	edittext NodeProperties "Node" width:190 height:18
	
	edittext ModProperties "Mods" width:230 height:18 pos:(MSProperties.pos+[(MSProperties.width+15),0])
	
	on InfoLister open do (
		UpdateInfo()
		
		InfoLister.NodeProperties.pos=(MSProperties.pos+[0,(MSProperties.height+15)])
		
		InfoLister.height=(InfoLister.MSProperties.height+InfoLister.NodeProperties.height+60)
	)
	
	on pck picked obj do(
			Curselection=obj
			UpdateInfo()
			select obj
		
			InfoLister.NodeProperties.pos=(MSProperties.pos+[0,(MSProperties.height+15)])
	)
)



InfoRO = newRolloutFloater "Informator" 480 190
addRollout Stats InfoRO
addRollout InfoLister InfoRO


fn UpdateInfo = (
	if Curselection != undefined and CurSelection != "" do(
		
		if (SuperClassOf CurSelection) == GeometryClass then(
			--TriCount
			Stats.TriCount.text=(GetNumfaces CurSelection.mesh)as string
			--VertCount
			Stats.VertCount.text=(GetNumVerts CurSelection.mesh)as string
		)else(
			--TriCount
			Stats.TriCount.text=0 as string
			--VertCount
			Stats.VertCount.text=0 as string
		)
		--ClassOf
		Stats.Class1.text=(classof CurSelection)as string
		--SuperClassOf
		Stats.SuperClass1.text=(superclassof CurSelection)as string
		

		--MaxScript Properties
		local MSProps="" as stringstream
			InfoLister.MSProperties.height=18
		for i in (getPropNames CurSelection) do (
			i=("#" + (i as string))
			local MSProps2=replace (i as string) 1 1 "$." 
	
			format "%
" MSProps2 to:MSProps
			InfoLister.MSProperties.height=InfoLister.MSProperties.height+12
		)
		InfoLister.MSProperties.text= MSProps	


		--Node Properties
		local MSProps="" as stringstream
			InfoLister.NodeProperties.height=18
		for i in (getPropNames (classof(superclassof CurSelection))) do (
			i=("#" + (i as string))
			local MSProps2=replace (i as string) 1 1 "$." 
	
			format "%
" MSProps2 to:MSProps
			InfoLister.NodeProperties.height=InfoLister.NodeProperties.height+12
		)
		InfoLister.NodeProperties.text= MSProps	


		--modifiers Properties
		local MSProps="" as stringstream
			InfoLister.ModProperties.height=18
		for i in (CurSelection.modifiers) do (
			for L in (getPropNames i) do(
				L=("#" + (L as string))
				local MSProps2=replace (L as string) 1 1 ("$.modifiers[#"+i.name+"]." )
	
				format "%
" MSProps2 to:MSProps
				InfoLister.ModProperties.height=InfoLister.ModProperties.height+12
			)
		)
		InfoLister.ModProperties.text= MSProps

	
	)
	try(InfoLister.height=(InfoLister.MSProperties.height+InfoLister.NodeProperties.height+60)	)catch()
)


#14

hey guys, this thread inspired me to make a lil infoFloater wich shows you the parameters of the picked obj in scripter friendly format. nothing fancy but usefull imo


try(closeRolloutFloater InfoRO)catch()
sleep 0.1

CurSelection=""

fn UpdateInfo = ()


rollout Stats "Statistics" (
	label TriCountlbl "Triangle Count . . . . ." width:90 pos:[180,5]
	label Tricount "NIL" width:90 pos:(TriCountlbl.pos+[80,0])

	label VertCountlbl "Vertex Count . . . . ." width:90 pos:(TriCountlbl.pos+[0,17])
	label Vertcount "NIL" width:90 pos:(VertCountlbl.pos+[80,0])
	
	label Class1lbl "Class . . . . . . . . . ." width:90 pos:[5,5]
	label Class1 "NIL" width:90 pos:(Class1lbl.pos+[80,0])
	
	label SuperClass1lbl "SuperClass . . . . ." width:90 pos:(Class1lbl.pos+[0,17])
	label SuperClass1 "NIL" width:90 pos:(SuperClass1lbl.pos+[80,0])
)


rollout InfoLister "Maxscript Properties" (

	pickbutton pck "Pick Object"
	
	edittext MSProperties "Class" width:190 height:18 pos:[20,35]
	edittext NodeProperties "Node" width:190 height:18
	
	edittext ModProperties "Mods" width:230 height:18 pos:(MSProperties.pos+[(MSProperties.width+15),0])
	
	on InfoLister open do (
		UpdateInfo()
		
		InfoLister.NodeProperties.pos=(MSProperties.pos+[0,(MSProperties.height+15)])
		
		InfoLister.height=(InfoLister.MSProperties.height+InfoLister.NodeProperties.height+60)
	)
	
	on pck picked obj do(
			Curselection=obj
			UpdateInfo()
			select obj
		
			InfoLister.NodeProperties.pos=(MSProperties.pos+[0,(MSProperties.height+15)])
	)
)



InfoRO = newRolloutFloater "Informator" 480 190
addRollout Stats InfoRO
addRollout InfoLister InfoRO


fn UpdateInfo = (
	if Curselection != undefined and CurSelection != "" do(
		
		if (SuperClassOf CurSelection) == GeometryClass then(
			--TriCount
			Stats.TriCount.text=(GetNumfaces CurSelection.mesh)as string
			--VertCount
			Stats.VertCount.text=(GetNumVerts CurSelection.mesh)as string
		)else(
			--TriCount
			Stats.TriCount.text=0 as string
			--VertCount
			Stats.VertCount.text=0 as string
		)
		--ClassOf
		Stats.Class1.text=(classof CurSelection)as string
		--SuperClassOf
		Stats.SuperClass1.text=(superclassof CurSelection)as string
		

		--MaxScript Properties
		local MSProps="" as stringstream
			InfoLister.MSProperties.height=18
		for i in (getPropNames CurSelection) do (
			i=("#" + (i as string))
			local MSProps2=replace (i as string) 1 1 "$." 
	
			format "%
" MSProps2 to:MSProps
			InfoLister.MSProperties.height=InfoLister.MSProperties.height+12
		)
		InfoLister.MSProperties.text= MSProps	


		--Node Properties
		local MSProps="" as stringstream
			InfoLister.NodeProperties.height=18
		for i in (getPropNames (classof(superclassof CurSelection))) do (
			i=("#" + (i as string))
			local MSProps2=replace (i as string) 1 1 "$." 
	
			format "%
" MSProps2 to:MSProps
			InfoLister.NodeProperties.height=InfoLister.NodeProperties.height+12
		)
		InfoLister.NodeProperties.text= MSProps	


		--modifiers Properties
		local MSProps="" as stringstream
			InfoLister.ModProperties.height=18
		for i in (CurSelection.modifiers) do (
			for L in (getPropNames i) do(
				L=("#" + (L as string))
				local MSProps2=replace (L as string) 1 1 ("$.modifiers[#"+i.name+"]." )
	
				format "%
" MSProps2 to:MSProps
				InfoLister.ModProperties.height=InfoLister.ModProperties.height+12
			)
		)
		InfoLister.ModProperties.text= MSProps

	
	)
	try(InfoLister.height=(InfoLister.MSProperties.height+InfoLister.NodeProperties.height+60)	)catch()
)


#15

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.