SDK-C# Set transform controller value
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);
}
I tested on Max 2018 - 2022 and none of them is working. I can build and max will not crash, but nothing is changing in the scene.
It’s 2016. And no, nothing is changing in the scene. But the same with your code.
It was just to try something without ‘ExecuteMaxScript’.
Sorry.
have you tried setting the sub controllers (position rotation scale) from a decomp_affine of the transform matrix ?
virtual Control * GetPositionController ()
virtual Control * GetRotationController ()
virtual Control * GetScaleController ()
CoreExport void decomp_affine ( Matrix3 A, AffineParts * parts )
where
struct AffineParts: public MaxHeapOperators {
/*! The translation components. */
Point3 t; /* Translation components */
/*! The essential rotation. */
Quat q; /* Essential rotation */
/*! The stretch rotation. This is the axis system of the scaling application. */
Quat u; /* Stretch rotation */
/*! The stretch factors. These are the scale factors for x, y and z. */
Point3 k; /* Stretch factors */
/*! Sign of the determinant. */
float f; /* Sign of determinant */
};
from my first post
Control *c;
c = node->GetTMController()->GetPositionController();