Get Used Material ID List from EditablePoly without face check

Hello,

Everybody on other forums use the check every face to read the Used Material IDs from an object, but there is a basic ID list in Editable Poly. I attached an image about it.

The only problem is that there is no chance to read this list from the Editable Poly.

Do somebody knows how can i read this list by script?

AttachmentSize
materialidlist.jpg77.43 KB

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
jahman's picture

.

You can also make an unsafe version of it and it will work at least two times faster.

if classof ::GetUsedMtlIDs != dotNetMethod do (
 
	local source = "using Autodesk.Max;
	using System;
	using System.Collections;
	using System.Collections.Generic;
 
	class MeshMaterialIDs
	{
	static private IGlobal global = GlobalInterface.Instance;
 
	static private IMesh GetMeshFromNode( IINode node )
	{            
		IObject obj = node.EvalWorldState( 0, true ).Obj;
		IClass_ID triClass = global.Class_ID.Create( 9, 0 );
		IClass_ID polyClass = global.Class_ID.Create( 1562457754, 0 );
		IClass_ID patchClass = global.Class_ID.Create( 4144, 0 );
 
		if ( obj.IsSubClassOf( triClass ) )
		{
			return ((ITriObject)obj).Mesh;
		}
		else if ( obj.IsSubClassOf( polyClass ) )
		{                
			ITriObject mesh = (ITriObject)((IPolyObject)obj).ConvertToType( 0, triClass );
			return mesh.Mesh;
		}
		else if ( obj.IsSubClassOf( patchClass ) )
		{
			return ((IPatchObject)obj).GetMesh( 0 );
		}
		else if ( obj.CanConvertToType( triClass ) == 1 )
		{
			ITriObject tri = (ITriObject)obj.ConvertToType( 0, triClass );
			return tri.Mesh_;
		}
		else
		{
			return null;
		}
	}
 
	static public int[] GetUsedMtlIDs( uint handle )
	{
		IINode node = global.COREInterface.GetINodeByHandle( handle );
		IMesh mesh = GetMeshFromNode( node );
 
		if ( mesh == null ) return null;
 
		BitArray used_ids = new BitArray( ushort.MaxValue );
		List<int> material_ids = new List<int>( ushort.MaxValue );
 
		foreach ( IFace face in mesh.Faces )
		{
			if ( used_ids.Get( face.MatID ) == false )
			{
				material_ids.Add( face.MatID + 1 );
				used_ids.Set( face.MatID, true );
			}
 
		}
 
		return material_ids.ToArray();
 
	}
 
	}"
 
	csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
	compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
	compilerParams.ReferencedAssemblies.Add("System.dll");		
	compilerParams.ReferencedAssemblies.Add((getdir #maxroot) + "Autodesk.Max.dll");			
	compilerParams.GenerateInMemory = on
	compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
 
 
	if (compilerResults.Errors.Count > 0 ) then
	(
		local errs = stringstream ""
		for i = 0 to (compilerResults.Errors.Count-1) do
		(
			local err = compilerResults.Errors.Item[i]
			format "Error:% Line:% Column:% %\n" err.ErrorNumber err.Line err.Column err.ErrorText to:errs
		)
		format "%\n" errs
		undefined
	)
 
	local assembly = compilerResults.CompiledAssembly.CreateInstance "MeshMaterialIDs"	
 
	global GetUsedMtlIDs = assembly.GetUsedMtlIDs
 
)
 
-- use it like this
GetUsedMtlIDs $.inode.handle
Matyrix's picture

jahman, you rock! i tried it

jahman, you rock! i tried it out on a 4.000.000 tri object and take 1 second! fantastic, thanks a lot!
furthermore it works on edit poly and edit mesh as well

jahman's picture

.

Glad to hear that. It should work ok with anything convertible to mesh.
upd
Here're my numbers :)
Numfaces 4224000
Time: 0.293sec. Mem: 544L
Unsafe Time: 0.058sec. Mem: 544L

Swordslayer's picture

 

That only shows when MultiSubObject material is assigned (and only would display all the ids if the multimaterial had the right number of matching id materials).

Matyrix's picture

Yes, you are right. When you

Yes, you are right. When you have more very high detailed objects, it's the fastest way to drop a multi material to the objects with 100+ sub material, and read the ID-s, and restore the materials - instead of check every face with ID information.

Swordslayer's picture

 

For personal use where you're pretty sure you don't exceed that, I agree; for a production-ready tool, you'd need 65536 submaterials (some pipelines use fixed id per material). But unless you're working with extra dense meshes, getting the ids used by ~10k mesh shouldn't take more than tenth of a second anyway.

Matyrix's picture

This tool works for specific

This tool works for specific workflow, you are right it's not a production-ready tool for everybody. Just for personal workflow.

One object is 5-10.000.000 polygon unfortunately.

jahman's picture

.

There's also <IGameMesh>.GetActiveMatIDs method available, but unfortunately I couldn't find a way to use it from maxscript.

Matyrix's picture

Wow, i think this is the

Wow, i think this is the right way, thanks!
It should be work somehow... I will read about it, hope can solve.

Swordslayer's picture

 

(
	local compilerParams = dotNetObject "System.CodeDom.Compiler.CompilerParameters" #(
		"System.dll", "System.Core.dll", getDir #maxRoot + "Autodesk.Max.dll",
		getDir #maxRoot + "\bin\assemblies\Autodesk.Max.Wrappers.dll")
	compilerParams.GenerateInMemory = on
 
	local compilerResults = (dotNetObject "Microsoft.CSharp.CSharpCodeProvider").CompileAssemblyFromSource compilerParams #(
		"using System;
		using System.Linq;
		using System.Collections.Generic;
		using Autodesk.Max;
		using Autodesk.Max.Wrappers;
 
		internal static class TabExtensions {
			public static IEnumerable<T> ToEnumerable<T>(this ITab<T> tab) {
				for (int i = 0; i < tab.Count; i++)
					yield return tab[i];
			}
		}
 
		class Poly {
			internal static readonly IGlobal Global = GlobalInterface.Instance;
 
			public static int[] GetSelectedObjMatIDs() {
				var pIgame = Global.IGameInterface;
				pIgame.InitialiseIGame(true);
				pIgame.SetStaticFrame(0);
 
				using (var currNode = Global.COREInterface.GetSelNode(0))
				using (var gameNode = pIgame.GetIGameNode(currNode))
				using (var gameMesh = Global.IGameMesh.Marshal((gameNode.IGameObject as Autodesk.Max.Wrappers.IGameObject).INativeObject__NativePointer)) {
					return gameMesh.InitializeData ? gameMesh.ActiveMatIDs.ToEnumerable().Select(id => id + 1).ToArray() : new int[0];
				}
			}
		}"
	)
	::poly = compilerResults.CompiledAssembly.CreateInstance "Poly"
)
 
if selection.count == 1 and isKindOf $ GeometryClass do Poly.GetSelectedObjMatIDs()

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.