SDK-C# Set transform controller value


#11

I tried something like this:

    ISetXFormPacket setXFormPacket2 = globalInterface.SetXFormPacket.Create();
    setXFormPacket2.Aa = globalInterface.AngAxis.Create();
    setXFormPacket2.Command = SetXFormCommand.Set;
    setXFormPacket2.LocalOrigin = true;
    setXFormPacket2.P = globalInterface.Point3.Create();
    setXFormPacket2.Q = globalInterface.Quat.Create();
    setXFormPacket2.TmAxis = globalInterface.Matrix3.Create();
    setXFormPacket2.TmParent = globalInterface.Matrix3.Create();

    IntPtr setXFormPacket = Wrappers.CustomMarshalerSetXFormPacket.GetInstance(string.Empty).MarshalManagedToNative(setXFormPacket2);
    node.TMController.SetValue(time, setXFormPacket, true, GetSetMethod.Absolute);

But again crashing …


#12

“&” in &pckt means we should send the pointer not variable, right?

yes in you are passing the memory address of the object… you could also do it like this

SetXFormPacket*  pckt = new SetXFormPacket(destTM);
tmControl->SetValue(ip->GetTime(), pckt);
delete pckt;

#13

So, We can’t do it in C#?


#14

what about…

object setXFormPacket = globalInterface.SetXFormPacket.Create(matrix,globalInterface.Matrix3.Create());
object* ptr = &setXFormPacket ;
node.TMController.SetValue(coreInterface.Time, ptr, true, GetSetMethod.Absolute);

#15

I got several errors for this:
Compiler Error CS0214: Pointers and fixed size buffers may only be used in an unsafe context
Compiler Error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('object')


#16

declare it as unsafe


#17

“object” type can not be declared as pointer.


#18

could try something like…

  unsafe
    {
        object setXFormPacket = globalInterface.SetXFormPacket.Create(matrix,globalInterface.Matrix3.Create());
        void* ptr = &setXFormPacket ;
        node.TMController.SetValue(coreInterface.Time, ptr, true, GetSetMethod.Absolute);
    }

#19

Not working…

|Error|CS0208|Cannot take the address of, get the size of, or declare a pointer to a managed type 
|Error|CS1503|Argument 2: cannot convert from 'void*' to 'object

which dotnet version do you use? mine is 4.7.2 and “Allow unsafe code” option is enabled.


#20

I’m not using dotnet , just making guesses as I know nothing about c# and max. The only I can think of trying is to create your own c# callable c++ function wrapper to handle it.


#21

it should work, but actually got some errors.


#22

Not sure if this works (or something similar):

        public static unsafe void Test (IMatrix3 matrix, INode node)
        {
            ISetXFormPacket setXFormPacket = globalInterface.SetXFormPacket.Create(matrix, globalInterface.Matrix3.Create());
            void* nativePtr = setXFormPacket.Handle.ToPointer();
            IntPtr* ptr = (IntPtr*)nativePtr;
            node.TMController.SetValue(coreInterface.Time, *ptr, true, GetSetMethod.Absolute);
        }

At least, it compiles. :slight_smile:


Calling C++ function inside C#
#23

INode is not defined, so I used IINode instead.

.Handle is not property of setXFormPacket. so I used .NativePointer instead

Yes, it will be compiled this way, But it will crash the Max again.

private readonly static IGlobal globalInterface = GlobalInterface.Instance;
private readonly static IInterface coreInterface = globalInterface.COREInterface;

public static unsafe void Test(IMatrix3 matrix, IINode node)
{
    int time = coreInterface.Time;
    ISetXFormPacket setXFormPacket = globalInterface.SetXFormPacket.Create(matrix, globalInterface.Matrix3.Create());
    void* nativePtr = setXFormPacket.NativePointer.ToPointer();
    IntPtr* ptr = (IntPtr*)nativePtr;
    node.TMController.SetValue(coreInterface.Time, *ptr, true, GetSetMethod.Absolute);
    globalInterface.COREInterface.RedrawViews(time, RedrawFlags.Normal, null);
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    IINode selNode = coreInterface.GetSelNode(0);
    if (selNode == null) MessageBox.Show("Selection empty.");
    else
    {
        IMatrix3 matrix = globalInterface.Matrix3.Create(true);
        Test(matrix, selNode);
    }
}

#24

Maybe we can use that hack : ManagedServices.MaxscriptSDK.ExecuteStringMaxscriptQuery
any idea how we can send transform controller handler value?


#25

what’s that?
if it is an animatable then simply use getHandleByAnim on mxs side and Animatable.GetAnimByHandle on c# side

Alternatively you can pass maxscript values to c# methods directly, but you’ll have to use some c# argument naming rules for it to work. Check MaxPlusArgNameParsingConstants in …\maxsdk\samples\maxscript\mxsdotNet\utils.h

for example if you want to pass point3 value to the c# method its signature should be like that
… MyMethod( IntPtr point3_pointer )

Class Animatable


#26

I couldn’t find that. where is it?


#27

OK, I ended up with this, but super slow!

private static string MatrixToString(IMatrix3 matrix)
{
    string ss = "";
    for (int i = 0; i < 4; i++)
    {
        IPoint3 point3 = matrix.GetRow(i);
        ss += "[" + point3.X.ToString() + ", " + point3.Y.ToString() + ", " + point3.Z.ToString() + "] ";
    }
    return "matrix3 " + ss;
}

private IMatrix3 GetTMControllerValue(IControl tMController)
{
    string mxsCode = "(getanimbyhandle "+ globalInterface.Animatable.GetHandleByAnim(tMController) +").value";
    IFPValue fPValue = globalInterface.FPValue.Create();
    globalInterface.ExecuteMAXScriptScript(mxsCode, true, fPValue, false);
    return fPValue.M;
}

private void SetTMControllerValue(IControl tMController, IMatrix3 matrix)
{
    string mxsCode = "(getanimbyhandle " + globalInterface.Animatable.GetHandleByAnim(tMController) + ").value = " + MatrixToString(matrix);
    globalInterface.ExecuteMAXScriptScript(mxsCode, true, null, false);
}

Actually this is very bad that C# SetValue doesn’t work and nobody can solve it.


#28

I bet there’s an easier way to get IMatrix3 from the controller, without messing with the script execution
btw you can copy matrix3 values like this

        static public string Matrix3ToString( IMatrix3 tm )
		{
			var values = new float[12];

			Marshal.Copy(tm.Handle, values, 0, 12);

			return $"Matrix3 [{values[0]},{values[1]},{values[2]}] [{values[3]},{values[4]},{values[5]}] [{values[6]},{values[7]},{values[8]}] [{values[9]},{values[10]},{values[11]}]";

		}

#29

SetValue is the main problem


#30

This code works for the getter, except that the values I get don’t seem very right.

        private static IMatrix3 GetTMControllerValue(IControl tMController)
        {
            IMatrix3 mat = global.Matrix3.Create();
            object matobj = (object)mat;
            IInterval interval = global.Interval.Create();
            interval.SetInfinite();
            tMController.GetValue(ip.Time, ref matobj, interval, GetSetMethod.Relative);
            mat = matobj as IMatrix3;
            return mat;
        }

Perhaps I’m missing something. It’s been for three or four years I don’t work with 3dsMax.