When copy transform from entity’s local to world to gameobject’s transform, there is an issue in implementation when there is negative scale involved in heirarchy. Below is original implementation. If some negative scale is invovled, Matrix.Rotation computation take negative scale into consideration once, and lossy scale take negative scale second time.
public unsafe void Execute(int index, TransformAccess transform)
{
var ltw = localToWorld[entities[index]];
var mat = *(UnityEngine.Matrix4x4*) & ltw;
transform.localPosition = ltw.Position;
transform.localRotation = mat.rotation;
transform.localScale = mat.lossyScale;
}
Here is my solution, maybe not the best, but it indeed solve the problem.
public unsafe void Execute(int index, TransformAccess transform)
{
var ltw = localToWorld[entities[index]].Value;
var lossyScale = GetLossyScale(ltw);
transform.localPosition = GetPosition(ltw);
transform.localRotation = GetRotationStripScale(ltw, lossyScale);
transform.localScale = lossyScale;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static public float3 GetPosition(float4x4 Value)
{
return new float3(Value.c3.x, Value.c3.y, Value.c3.z);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static public quaternion GetRotation(float4x4 Value)
{
return new quaternion(Value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static public quaternion GetRotationStripScale(float4x4 Value, float3 scale)
{
var mt = math.mul(Value, float4x4.Scale(math.rcp(scale)));
return new quaternion(mt);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static public float3 GetLossyScale(float4x4 matrix)
{
var baseX = matrix.c0.xyz;
var baseY = matrix.c1.xyz;
var baseZ = matrix.c2.xyz;
if (math.dot(math.cross(baseX, baseY), baseZ) < 0)
return new float3(math.length(baseX), math.length(baseY), -math.length(baseZ));
else
return new float3(math.length(baseX), math.length(baseY), math.length(baseZ));
}