Hey Polycounters,
I have a question/in need of some help. I have been wracking my brain trying to figure this one out for a while now.
So I have this tool that lists the stats of objects in the scene. The last column of my tool lists the number of materials you have assigned to the faces of each object. This works fine, however......the way in which I have done it does not allow me to sort them properly.
So how have I done it? Well, first I gather the objects in the scene into an array called mySelectedObjects, then I loop through each object selecting faces based on material ID's, if the number of faces selected is more than 0 then the material count is increased, if it is 0 then it is not. I do this for however many sub materials are in the multi-material (if any) then I iterate through the two arrays and place them in the listview.
The problem comes when I try to sort by material ID. Sorting by verts, faces etc. is easily done because I just pinch the stats using object.verts.count and then rearrange it that way. But because the material count array is just a set of numbers, I am running into problems. I can qsort the numbers array but I need to be able to sort the objects array with it.
The way I am doing it at the moment is SO SO SO SLOW and isn't really working for me....I am sorting the material array, then looping through it and then looping throuigh the objects array, counting the materials assigned and if it matches then put it in a temporary array in order and then use that to popuylate the list.
But it is getting very confusing and very slow. There must be an easier way!! Anyway here is the function for this:
Any help/tips would be greatly appreciated, this is the last thing I have to do before the tool is finished and I can release it!
Replies
fn sortByMaterial = ( --Sorts through the (now sorted) master material array and matches objects from the MSO array to the value -- If a match is found then the object is placed into a new temporary array -- The object is removed from the MSO array so that duplicates are not placed into the new temp array -- The MSO array is then remade using the order from the tempArray local arrayTrack = 1 -- variable to hold the index number of the MSO array object matId = 1 matNum = 0 count = masterMatArray.count for i = 1 to count do ( index = masterMatArray[i] for j in mySelectedObjects do ( mat = j.material if(classof j != Editable_Poly and classof j != Editable_Mesh and classof j != Editable_Patch) then ( if(mat != undefined) then ( matNum = 1 ) else ( matNum = 0 ) arrayTrack += 1 ) else ( if(classof mat != MultiMaterial) then ( if(mat != undefined) then ( matNum = 1 ) else ( matNum = 0 ) ) else ( numberOfSubs = mat.numsubs if(classOf j == Editable_Mesh or classOf j == Editable_Patch) then ( -- print("Converted " + i as string) copyOfObj = copy j convertToPoly copyOfObj j = copyOfObj isCopied = true ) for k = 1 to numberOfSubs do ( j.selectbymaterial matID faceArr = getFaceSelection j --print("face array count is: " + faceArr.numberset as string) if(faceArr.numberset != 0) then ( matNum += 1 ) matID += 1 ) ) deselect j.faces arrayTrack += 1 ) if(matNum == index) then ( append tempArray j ) matNum = 0 matID = 1 if(isCopied == true) then ( delete j isCopied = false ) ) ) mySelectedObjects = #() for l in tempArray do ( append mySelectedObjects l ) tempArray = #() )As it stands I gather a list of objects and apply the number of materials present on the object to a user defined property. Then when you go through and sort the objects in the list you are simply sorting the list data rather than the scene data. The only time the script gathers scene data is when the user clicks refresh or when the user clicks a deleted item in which case it automatically refreshes
edit: For the sake of completeness and to avoid cross-referencing to another site:
try destroyDialog sortByIDCount catch() rollout sortByIDCount "ID Count Test" width:225 ( dotNetControl dncObjList "System.Windows.Forms.ListView" width:200 height:200 button btnFill "Fill It" width:200 height:25 local dnListItem = dotNetClass "System.Windows.Forms.ListViewItem" local lastColumn, reversed = false fn compileListItemSorter = ( source = "using System;\n" source += "using System.Windows.Forms;\n" source += "using System.Collections;\n" source += "class ListViewItemComparer : IComparer\n" source += "{\n" source += " private int c;\n" source += " private bool num = false;\n" source += " private int dir = 1;\n" source += " public ListViewItemComparer() { c = 0; }\n" source += " public ListViewItemComparer(int column, bool numeric, bool reverse)\n" source += " { c = column; num = numeric; dir = reverse ? -1 : 1; }\n" source += " public int Compare(object x, object y)\n" source += " {\n" source += " if (num) return Convert.ToInt32(((ListViewItem)x).SubItems[c].Text).CompareTo(\n" source += " Convert.ToInt32(((ListViewItem)y).SubItems[c].Text)) * dir;\n" source += " else return String.Compare(((ListViewItem)x).SubItems[c].Text,\n" source += " ((ListViewItem)y).SubItems[c].Text) * dir;\n" source += " }\n" source += "}" local csharpProvider = dotNetObject "Microsoft.CSharp.CSharpCodeProvider" local compilerParams = dotNetObject "System.CodeDom.Compiler.CompilerParameters" compilerParams.GenerateInMemory = true compilerParams.ReferencedAssemblies.Add("System.Windows.Forms.dll") compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source) compilerResults.CompiledAssembly ) fn getIDCount obj ids:#{} = ( local mesh = obj.mesh local faces = mesh.faces as bitarray for face in faces do append ids (getFaceMatID mesh face) delete mesh ids.numberSet ) fn getItem obj channelCount = ( local item = dotNetObject dnListItem obj item.SubItems.Add (channelCount as string) item ) fn getItems objs = for obj in objs collect case classOf obj.material of ( MultiMaterial : getItem obj.name (getIDCount obj) UndefinedClass : getItem obj.name 0 default : getItem obj.name 1 ) on sortByIDCount open do ( dncObjList.View = dncObjList.View.Details dncObjList.BorderStyle = dncObjList.BorderStyle.FixedSingle dncObjList.FullRowSelect = true dncObjList.GridLines = true dncObjList.Columns.Add "Object Name" 100 dncObjList.Columns.Add "ID Channels" 100 compileListItemSorter() ) on dncObjList columnClick columnHeader do ( local column = columnHeader.Column if column == lastColumn then reversed = NOT reversed else reversed = false dncObjList.ListViewItemSorter = dotNetObject "ListViewItemComparer" \ column (column == 1) reversed dncObjList.ListViewItemSorter = undefined lastColumn = column ) on btnFill pressed do ( dncObjList.Items.Clear() dncObjList.Items.AddRange (getItems selection) ) ) createDialog sortByIDCountI've added the reversed check to the comparer (of course, it's not to be taken to the letter - for example, it doesn't make much sense to compile the assembly on each run and so on and so on).