Notifications
Clear all

[Closed] Output Map Curve Access / Comparison

Hi,

have any of you found methods of accessing output curves or checking if two output curves are identical? Preferably via Maxscript, but any other methods could be interesting too!

We’re currently working on some material comparison features, which crawl through materials properties and create checksums to identify identical materials. The only property we are currently unable to evaluate properly are output curves.

I’m aware of how custom curves can be set but couldn’t work out methods for evaluating existing curve information. I was hoping that more recent 3dsmax releases (currently using 2019) would have addressed those problems but it seems as though points still need to be animated to enable access to their point positions (handles still being a problem here).

6 Replies
1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

see …\3ds Max <…> SDK\maxsdk\samples\utilities\meditutils\instancemap.cpp
there is TexoutComp function which demonstrates how it can be done.

modify this to fit your needs

fn EnableColorMap texoutput state redraw:true =
(
	local g = (dotNetClass "Autodesk.Max.GlobalInterface").Instance
	
	local out = g.animatable.getanimbyhandle (dotNetObject "System.UIntPtr" (getHandleByAnim texoutput))
	
	local TEXOUT_INVERT        = 2
	local TEXOUT_CLAMP         = 4
	local TEXOUT_ALPHA_RGB     = 8
	local TEXOUT_COLOR_MAP     = 16
	local TEXOUT_COLOR_MAP_RGB = 32
		
	out.SetFlag TEXOUT_COLOR_MAP (if state then 1 else 0)
	
	if redraw do texoutput.output_amount = texoutput.output_amount

)

-- EnableColorMap meditMaterials[1].diffuseMap.output true

get whatever curve you need and you can access any of its points positions at the given time

if isProperty texoutput #Mono_Color_Map do
(
	mono = g.animatable.getanimbyhandle (dotNetObject "System.UIntPtr" (getHandleByAnim texoutput.Mono_Color_Map))
)
	
if isProperty texoutput #RGB_Color_Map do
(
	rgbc = g.animatable.getanimbyhandle (dotNetObject "System.UIntPtr" (getHandleByAnim texoutput.RGB_Color_Map))
)

see ICurveCtl in sdk reference

Thanks for that! I wasn’t aware of it being possible to access so much through dotnet.

I started moving on with the example of the mono curve but unfortunately ended up encountering other problems.

mono_curve = mono.GetControlCurve 0
for i = 1 to mono_curve.numpts do
(
        pt = mono_curve.GetPoint 0 i
)

Some parts confused me here:

While the ICurve reference (3dsmax2019) refers to .GetNumPts() for getting the point count, the function doesn’t seem to exist on the dotnet wrapper. Is this common or just me accessing things wrongly?

The .GetPoint function failed for me constantly:

"-- Runtime error: No 'GetPoint' method found which matched argument list"

Input seems to be expected as follows:

--.<Autodesk.Max.ICurvePoint>GetPoint <System.Int32>t <System.Int32>index <Autodesk.Max.IInterval>valid

Since the instancemap.cpp example seems to only supply time and index, I started by only supplying those two arguments. After that failed, I tried converting them to their corresponding dotnetobject values, which didn’t help either. Also supplying the IInterval (“Autodesk.Max.IInterval”) unfortunately didn’t get me any further either.

Any ideas?

1 Reply
(@serejah)
Joined: 11 months ago

Posts: 0

I agree with Denis that c# is unreliable in general (at least for me).
See if this code works in 2019.
In 2014 it throws an error on attempt to access curve_pts[1].p. Maybe you can get the values by reading from memory directly. If none of the above works try to compile c# dll for your max version (that’s exactly where it becomes unreliable as different max versions may have or not have a particular method or property, or have them renamed).


g = (dotNetClass "Autodesk.Max.GlobalInterface").Instance
texoutput = meditMaterials[1].diffuseMap.output
mono = g.animatable.getanimbyhandle (dotNetObject "System.UIntPtr" (getHandleByAnim texoutput.Mono_Color_Map))
	
	
mono_curve = mono.GetControlCurve 0	
validity_interval = g.interval.create()	
curve_pts = for i = 0 to mono_curve.numpts - 1 collect
(
	pt = mono_curve.GetPoint (currenttime as integer) i validity_interval
)

/*
show curve_pts[1]
  .Flags : <System.Int32>
  .Implementation : <System.Object>, read-only
  .In : <Autodesk.Max.IPoint2>
  .INativeObject__Handle : <System.IntPtr>, read-only
  .Out : <Autodesk.Max.IPoint2>
  .P : <Autodesk.Max.IPoint2>

showmethods curve_pts[1]
  .[static]<CurvePoint*>__ConvertToUnmanaged <Autodesk.Max.ICurvePoint>managed
  .<Autodesk.Max.ICurvePoint>Assign <Autodesk.Max.ICurvePoint>rhs
  .Dispose()
  .<System.Boolean>Equals <Autodesk.Max.ICurvePoint>other
  .<System.Boolean>Equals <System.Object>obj
  .[static]<System.Boolean>Equals <System.Object>objA <System.Object>objB
  .<System.Int32>GetHashCode()
  .<System.Type>GetType()
  .[static]<System.Boolean>ReferenceEquals <System.Object>objA <System.Object>objB
  .<System.String>ToString()
  .<CurvePoint*>unmanaged()
  .<CurvePoint*>unmanagedCurvePoint()
*/ 

this one works if compiled

CurveOps = 
(

compilerParams = dotNetObject "System.CodeDom.Compiler.CompilerParameters" #(getDir #maxRoot + "Autodesk.Max.dll")
compilerParams.GenerateInMemory = true
compilerResults = (dotNetObject "Microsoft.CSharp.CSharpCodeProvider").CompileAssemblyFromSource compilerParams #(
        "using System;
        using Autodesk.Max;
        
        public static class Const {
            public static readonly IGlobal Global = Autodesk.Max.GlobalInterface.Instance;
            public static IInterval FOREVER = Global.Interval.Create();

            static Const() {
                FOREVER.SetInfinite();
            }
        }

        public class CurveOps
		{
            public static float[] GetCurvePointsValues(int handle)
			{
				IAnimatable anim = Const.Global.Animatable.GetAnimByHandle((UIntPtr)handle);

				IICurveCtl curve = (IICurveCtl)anim;
            
				var c = curve.GetControlCurve( 0 );
				
				float[] values = new float[ c.NumPts * 6 ];

				for ( int i = 0; i < c.NumPts; i++ )
				{
					var pt = c.GetPoint( 0, i, Const.FOREVER );
					
					values[6 * i] = pt.P.X;
					values[i + 1] = pt.P.Y;
					values[i + 2] = pt.In.X;
					values[i + 3] = pt.In.Y;
					values[i + 4] = pt.Out.X;
					values[i + 5] = pt.Out.Y;
				}
			
               return values;
               
            }
        }"
    )

    for err = 0 to compilerResults.errors.count - 1 do print (compilerResults.errors.item[err].ToString())
    compilerResults.CompiledAssembly.CreateInstance "CurveOps"
)		

CurveOps.GetCurvePointsValues (getHandleByAnim texoutput.Mono_Color_Map)

I don’t mess with c# max api. But I know that there are guys in this forum who are more experienced than me in this area.

Thats been really helpful, thanks! Definitely something I will have to look at in more detail. I’ve only really been focusing on mxs, pymxs, MaxPlus and some dotnet a couple of years ago for UIs. Will probably take some time wrapping my head around all the additional possiblities of implementing features and where to use what.

Couldn’t access curve_pts[1].p in 2019 either unforunately so I used your lower code and adjusted it to our needs. I’m fairly unfamiliar with C# and C++, so the extended code probably isn’t very clean, but it seems to get the job done. Here it is, in case some people find this thread and want to make use of it:

"using System;
    using Autodesk.Max;

    public static class Const {
        public static readonly IGlobal Global = Autodesk.Max.GlobalInterface.Instance;
        public static IInterval FOREVER = Global.Interval.Create();

        static Const() {
            FOREVER.SetInfinite();
        }
    }

    public class CurveOps
    {
        public static float[] get_mono_curve_point_values(int handle)
        {
            IAnimatable anim = Const.Global.Animatable.GetAnimByHandle((UIntPtr)handle);
            IICurveCtl curve = (IICurveCtl)anim;
            var c = curve.GetControlCurve(0);
            float[] values = new float[c.NumPts*6];

            for (int i=0; i<c.NumPts; i++)
            {
                var pt = c.GetPoint(0, i, Const.FOREVER);
                int i_base = i*6;

                values[i_base] = pt.P.X;
                values[i_base + 1] = pt.P.Y;
                values[i_base + 2] = pt.In.X;
                values[i_base + 3] = pt.In.Y;
                values[i_base + 4] = pt.Out.X;
                values[i_base + 5] = pt.Out.Y;
            }

            return values;
        }

        public static float[][] get_rgb_curve_point_values(int handle)
        {
            IAnimatable anim = Const.Global.Animatable.GetAnimByHandle((UIntPtr)handle);
            IICurveCtl curve = (IICurveCtl)anim;
            
            float[][] rgb_values = new float[curve.NumCurves][];
            
            for (int c_idx=0; c_idx<curve.NumCurves; c_idx++)
            {
                var c = curve.GetControlCurve(c_idx);
                float[] values = new float[c.NumPts*6];
                
                for (int i=0; i<c.NumPts; i++)
                {
                    var pt = c.GetPoint(0, i, Const.FOREVER);
                    int i_base = i*6;

                    values[i_base] = pt.P.X;
                    values[i_base + 1] = pt.P.Y;
                    values[i_base + 2] = pt.In.X;
                    values[i_base + 3] = pt.In.Y;
                    values[i_base + 4] = pt.Out.X;
                    values[i_base + 5] = pt.Out.Y;
                }

                rgb_values[c_idx] = values;
            }  

            return rgb_values;
        }
    }"