So, We can’t do it in C#?
SDK-C# Set transform controller value
what about…
object setXFormPacket = globalInterface.SetXFormPacket.Create(matrix,globalInterface.Matrix3.Create());
object* ptr = &setXFormPacket ;
node.TMController.SetValue(coreInterface.Time, ptr, true, GetSetMethod.Absolute);
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')
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);
}
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.
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.
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. 
Calling C++ function inside C#
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);
}
}
Maybe we can use that hack : ManagedServices.MaxscriptSDK.ExecuteStringMaxscriptQuery
any idea how we can send transform controller handler value?
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
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.
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]}]";
}
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.
And same for the set:
private static void SetTMControllerValue(IControl tMController, IMatrix3 matrix)
{
object matobj = (object)matrix;
tMController.SetValue(ip.Time, matobj, true, GetSetMethod.Relative);
}