The float4x4.TRS method applies scale on top of rotation instead of the other way around. This makes no difference for uniform scaling but makes a big difference for non-uniform scaling.
public static float4x4 TRS(float3 translation, quaternion rotation, float3 scale)
{
float3x3 r = float3x3(rotation);
return float4x4( float4(r.c0 * scale.x, 0.0f),
float4(r.c1 * scale.y, 0.0f),
float4(r.c2 * scale.z, 0.0f),
float4(translation, 1.0f));
}
The indicated TRS transformation matches Unity’s GO transformation matrix behavior. The actual TSR transformation does not.
My suggestion is to make TRS perform the indicated transformation and add a high-performance variant with a float scale parameter for uniform scaling.
// Current unexpected behavior.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float4x4 TSR (float3 translation, quaternion rotation, float3 scale) {
float3x3 r = float3x3(rotation);
return float4x4(float4(r.c0 * scale.x, 0.0f),
float4(r.c1 * scale.y, 0.0f),
float4(r.c2 * scale.z, 0.0f),
float4(translation, 1.0f));
}
// Correct expected behavior.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float4x4 TRS (float3 translation, quaternion rotation, float3 scale) {
float3x3 m = mul(Unity.Mathematics.float3x3.Scale(scale), float3x3(rotation));
return float4x4(float4(m.c0, 0.0f),
float4(m.c1, 0.0f),
float4(m.c2, 0.0f),
float4(translation, 1.0f));
}
// Optimized for uniform scale.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float4x4 TRS (float3 translation, quaternion rotation, float scale) {
float3x3 r = float3x3(rotation);
return float4x4(float4(r.c0 * scale, 0.0f),
float4(r.c1 * scale, 0.0f),
float4(r.c2 * scale, 0.0f),
float4(translation, 1.0f));
}