Notifications
Clear all

[Closed] Asset Browser : Again

I’ve learned so much from everyone here, I couldn’t have come as far as I have without all the invaluable advice and examples. Below is my first go at an asset browser/creator tool. At the moment, it is lacking a ton of planned features but I will get there eventually.

The two big pieces I owe a lot of credit to are two custom dotnet assemblies:
HitchHiker from LoneRobot
KClasses from Artur Leão that he posted here.
I used the Tab Control example from thetutorial by Paul Neal at Pen Productions

Vimeo Demo

ATTENTION: Since this is partially a work related project, I can’t promise that I will release updates or a final version. I definitely don’t have too much time to do support or to add feature requests or any of that. But this is somewhat functional as it is and noobs like me can learn something from it.

You’ll need to download the two .dlls I listed above and put them in your max root folder

Also, we use VRay, so the Create tab will always be set up to render vray thumbnails. If you don’t have VRay, this tab won’t work for you but the browse feature will still work. Basically, you need a .jpg with the exact same name as your .max asset file in the same folder. It is probably best if this jpg is 500×500.

If you have a single small monitor, I appologize. This tool is 1200×900.

I’m at a crossroads now. As I learn more about dotnet, I am contemplating dropping the max rollouts and going with maxForms. If only to force myself to learn them. Maybe your opinions can help me choose a direction at this point. I’d like to figure this out before I go too much further so I don’t have too much to convert.

I plan on adding these features:

[ol]
[li]I would like to figure out how to save the state of each tab when switching and restore the state when switching back[list=1][/li][li]Can I set the ‘current folder’ from the tree using a path string? Aside from the initial ProcessTree method. I want it to process a whole tree but jump to a specific folder in that tree on open or simply when switching back to the browser Tab.[/li]

[/ol]
[li]Add option to save tab states to .ini on close. There will be checkboxes in the Settings tab to set user options and save them to .ini in #userScripts[/li][li]The Create tab will eventually have the tools to add new folders, add keywords to the .jpgs via the tags property hopefully. I hope to add the keyword/tag feature using categorized lists of predefined keywords. I picture them in lists of checkboxes or checkButtons.So a user creating an asset can quickly add keywords without too much manual data entry. It would also be good if the user can add keywords dynamically to the UI…[/li][li]Possibly, add resizing to the whole tool based on percentage widths for the treeview and thumbnail browser… [/li]
[/list]I’m sure it is a formatting mess. Any tips or direction on any of it will be greatly appreciated

/* Credits:
  HitchHiker.dll by LoneRobot  http://lonerobot.net/?p=260 
  KClasses.dll by Artur Leão  http://dimensao3.com/al/ 
  dotnet Tabbed Control tutorial from PEN Productions  http://penproductions.ca/tutorials/dotNet/tabControl/tabControl.htm 
  Instance replace function based on Denis Trofimov's example  http://forums.cgsociety.org/showpost.php?p=6264096&postcount=10 
   */
  
  --Destroy the dialog if it is already open 
  try(destroyDialog ro_Assets)catch()
  dotnet.loadAssembly "KClasses.dll"
  dotnet.loadAssembly "HitchHiker.dll"
  
  
  atw = 1200
  ath = 860
  
  assetPath = @"\\zgf\data\ZGFResources\3D\Assets"
  
  
  --Create a rollout
  rollout ro_Assets "Asset Tools v0.0.1" width:atw height:ath
  (
  	--Add a dot net tab control
  	dotNetControl tabs "system.windows.forms.tabControl" width:(atw-600) height:25 
  	--Add a sub rollout.
  	subRollout subRoll "Sub" width:(atw-30) height:850 offset:[0,-5]
  
  	--Write Summary Function
  	fn getAllFileData theDatFile = 
  	(
  		pages = #(#summary, #contents, #custom)
  		for pg in pages do
  		(
  			format "%
" (pg as string) to:theDatFile
  			for i=1 to (fileProperties.getNumProperties pg) do
  			(
  				local pname = (fileProperties.getPropertyName pg i)
  				local pval = (fileProperties.getPropertyValue pg i)
  				format "% : " pname to:theDatFile
  				if (pname == "Headers") then
  				(
  					format "
" to:theDatFile
  					for hdr in pval do
  					(
  						format "%
" hdr to:theDatFile
  						local docs = fileProperties.getItems hdr
  						if docs != undefined then
  						for d in docs do format "%
" d to:theDatFile
  					)
  				)
  				else format " %
" pval to:theDatFile
  			)
  		)
  	)
  	
  	--ReadSummaryFile
  	fn readSummaryInfo sf = 
  	(
  		theMaxFile = maxFileName
  		theSimpleName = getFilenameFile theMaxFile
  		tempText = ""
  		sumFile = openFile sf  mode:"r"
  		while not eof sumFile do (append tempText ((readline sumFile) +"
"))
  		close sumFile
  		return tempText
  	)
  	
  	--Merge Placement follows mouse
  	fn PlaceObj msg ir obj faceNum shift ctrl alt =
  	(   
  		  if ir == undefined then
  				  (
  						myStr = mouse.pos					
  				  )
  		  else
  				(
  					myStr =  ir.pos
  				)
  		  case msg of
  		  (
  			  #freeMove:
  				(
  					mergeObj.pos = myStr 
  				)
  			  #mousePoint:
  						(
  							instance selection
  							selection.pos = myStr
  						)
  			  #mouseAbort: 
  						(
  							delete selection
  							exit
  						)
  		  )
  		  #continue 
  	)
  	--Replace Instances
  	  fn instanceReplaceSelection nodes makeunique:off = 
  	(
  		act = off
  		setCommandPanelTaskMode mode:#create
  		nodes = #()
  		for o in selection do append nodes o
  		nodes
  		
  
  		file = theMergeFile
  		if nodes.count > 0 and doesfileexist file do
  		(
  			clearSelection()
  			-- merge only object with the name
  			mergeMaxFile file  #select #mergeDups #neverReparent #useMergedMtlDups quiet:on
  			if isvalidnode (node = selection[1]) do
  			(
  				-- replace all old instances with new one
  				for obj in nodes do 
  				(
  					instancereplace obj node
  					obj.mat = node.mat
  				)
  				-- make them unique if necessary
  				if makeunique do InstanceMgr.MakeObjectsUnique nodes #individual
  				delete node
  				act = on
  			)
  		)
  		act
  	)
  	--Replace Selection
  	fn copyReplaceSelection nodes = 
  	(
  		act = off
  		setCommandPanelTaskMode mode:#create
  		file = theMergeFile
  		if nodes.count > 0 and doesfileexist file do
  		(
  			clearSelection()
  			-- merge only object with the name
  			mergeMaxFile file  #select #mergeDups #neverReparent #useMergedMtlDups quiet:on
  			if isvalidnode (node = selection[1]) do
  			(
  				-- replace all old instances with new one
  				for obj in nodes do 
  				(
  					InstanceMgr.MakeObjectsUnique nodes #group
  					instanceReplace obj node
  					obj.mat = node.mat
  				)
  				delete node
  				act = on
  			)
  		)
  		act
  	)
  	  
  	--Tab initilization function.
  	fn initTabs tab labels:#() =
  	(
  		--Clear any existing tabs incase we do an update of them at some point. 
  		tab.tabPages.clear()
  		--Set the size mode so that we can control their width. 
  		tab.sizeMode=tab.sizeMode.fixed
  		--Set the width of every tab.
  		tab.itemSize=dotnetObject "System.Drawing.Size" ((tab.width/labels.count)-2) 25
  		
  		--Loop through all the labels that we pass to the function and add a tab by the same name. 
  		 for x in labels do tab.tabPages.add x
  	)
  	
  
  	
  -------------------------------------------TABS--------------------------------------------------------------------------	
  	--Add rollouts to the subRollout based on which tab is currently selected. 
  	fn addTabRollout index=
  	(
  		--Remove any existing rollouts first. 
  		 for x in subRoll.rollouts do removeSubRollout subRoll x
  		
  		sub=case index of
  		(
  			--BROWSER
  			default: 
  			(
  				rollout ro_Browser "Browser"
  				(
  					--Main Elements
  					dotnetcontrol tvFolders "KClasses.dnFolders" pos:[0,0] width:290 height:800
  					dotNetControl HitchHiker "lonerobot.ui.character.HitchHiker" pos:[810,0] width:340 height:800
  					dotNetControl pic_Preview "pictureBox" pos:[300,0] width:500 height:500 
  					
  					--Info Area
  					dotnetControl dgr_Info "dataGrid" pos:[525,510] width:275 height:260
  					
  					button btn_Merge "Merge" pos:[700,780] width:100 height:20
  					button btn_ReplaceInst "Replace Instances" pos:[600,780] width:100 height:20
  					button btn_ReplaceCopy "Replace Selection" pos:[500,780] width:100 height:20
  					
  					button btn_test "test" pos:[300,780]
  					
  					on btn_test pressed do
  					(
  						print tvFolders.Location
  					)
  					
  					on ro_Browser open do
  					(
  						--initialize thumbnail browser
  					HitchHiker.ButtonBackColor = (dotnetclass "system.drawing.color").black
  					HitchHiker.filetype = (dotnetclass "lonerobot.ui.character.HitchHiker+filetypes").jpg
  					HitchHiker.displaystyle = (dotnetclass "lonerobot.ui.character.HitchHiker+displaystyles").imagetext
  					HitchHiker.Gap = 0
  					HitchHiker.RecursiveSearch = false
  					HitchHiker.ShowToolBar = false
  					HitchHiker.populate assetPath
  						
  						--inititalize Preview window
  					pic_Preview.BackColor = (dotnetClass "System.Drawing.Color").black
  						
  					--initialize Info area
  					--dgr_Info.label = "Info"
  						--iniitialize treeview
  					--clearListener()
  					start=timestamp()
  					tvFolders.ProcessTree(assetPath)
  					--tvFolders.select (getINISetting (getdir #userScripts + "\AssetTools.ini") "Browser" "TreeViewDefaultLocation" )
  					end=timestamp()
  					format "Processing took % seconds
" ((end - start) / 1000.0)
  					)
  					
  					--Thumbnail Handlers
  					on HitchHiker thumbpicked sender args do
  					(
  						theMergeFileThumb = args.FileName
  						theFile = theMergeFileThumb
  						theName = getFilenameFile theFile
  						thePath = getFileNamePath theFile
  						global theMergeFile = (thePath + theName + ".max")
  						global theSummaryFile = (thePath + theName + ".dat")
  						
  						pic_Preview.Load args.FileName
  						
  						if doesFileExist theSummaryFile then 
  						(
  							dgr_Info.text = (readSummaryInfo theSummaryFile)
  						)
  						else
  						(
  							dgr_Info.text ="No Data"
  						)
  						print args.FileName
  					)
  					
  					--TreeView Handlers
  					on tvFolders NodeMouseClick sender eb do
  					(
  						print eb.node.dirName
  						HitchHiker.populate eb.node.dirName
  						setINISetting (getdir #userScripts + "\AssetTools.ini") "Browser" "TreeViewDefaultLocation" eb.node.dirName
  						--showproperties eb.node
  					)
  					
  					--Replace Selection Button Handler
  					on btn_ReplaceCopy pressed do
  					(
  						if selection == undefined then
  							(
  								messagebox "Nothing Selected"
  							)
  							else
  							(
  								sellectedObs = #()
  								for o in selection do append sellectedObs o
  								copyReplaceSelection sellectedObs
  							)
  					)
  					
  					--Replace all instances Button Handler
  					on btn_ReplaceInst pressed do
  					(
  						if selection == undefined then
  							(
  								messagebox "Nothing Selected"
  							)
  							else
  							(
  								sellectedObs = #()
  								for o in selection do append sellectedObs o
  								instanceReplaceSelection sellectedObs
  							)
  					)
  					
  					--Merge Button Handler
  					on btn_Merge pressed do
  					(
  						if selection != undefined do (global mergeSurf = selection[1])
  						
  						mergeMaxFile theMergeFile #mergeDups #useSceneMtlDups #select
  						global mergeObj =  selection
  						if mergSurf != undefined
  							then 
  								(mouseTrack  on:mergeSurf snap:#3D trackCallback:PlaceObj)
  							else
  								(mouseTrack  snap:#3D trackCallback:PlaceObj)
  					)
  					
  						
  					on ro_Browser close do 
  					(
  						HitchHiker.housekeeping()
  						tvFolders.dispose()
  					)
  				)
  			)
  			--CREATOR
  			1:
  			(
  				rollout CreateAsset "Asset Creation"
  				(
  					
  					dotnetcontrol tvFolders "KClasses.dnFolders" pos:[atw-300,0] width:290 height:800
  					button btn_Render "Render Thumbnail" height:48 pos:[0,50]
  					
  					--ButtonEvents
  					on btn_Render pressed do
  					(
  						--ProcessFileName
  						theMaxFile = maxFileName
  						theMaxFilePath = maxFilePath
  						theSimpleName = getFilenameFile theMaxFile
  						--RenderSetup
  						sysGamma = IDisplayGamma
  						sysGamma.colorCorrectionMode = #gamma
  						sysGamma.gamma = 2.2
  						backgroundColor = black
  						renderSceneDialog.close()
  						vr = renderers.current = vray()
  						vr.gi_on = true
  						vr.gi_primary_type = 0
  						vr.gi_secondary_type = 3
  						vr.options_lights = true
  						vr.options_defaultLights = 1
  						setRenderType #view
  						rendTimeType = 1
  						renderWidth = 500
  						renderHeight = 500
  						renderPixelAspect = 1.0
  						
  						print (theMaxFilePath + theSimpleName + ".jpg") 
  						
  						--Create Thumbnail
  						thumbnail = bitmap 500 500 filename:(theMaxFilePath + theSimpleName + ".jpg") 
  						render to:thumbnail
  						save thumbnail
  						close thumbnail
  						
  						--Create Summary File
  						datFile = createFile (theMaxFilePath + theSimpleName + ".dat") 
  						getAllFileData datFile
  						close datFile
  					)
  				)
  			)
  			--SETTINGS
  			2:
  			(
  				rollout Settings "Settings"
  				(
  					button testBt "test Button"
  					on testBt pressed do (messageBox "You pressed the button" title:"Alert!")
  				)
  			)
  		)
  		addSubRollout subRoll sub
  	)
  	
  		--When the mouse button is released on a tab get the selected tab and pass it to our addTabRollout function. 
  	on tabs MouseUp senderArg arg do
  	(
  		addTabRollout senderArg.SelectedIndex
  	)
  	
  	--When the rollout opens initilize the tab control
  	on ro_Assets open do
  	(
  		initTabs tabs labels:#("Browse", "Create", "Settings")
  		addTabRollout 0
  	)
  
  )
  --Create the dialog
  createDialog ro_Assets   style:#(#style_resizing, #style_titleBar, #style_sysmenu)