Instantiating Prefab with limitDOFjoint origin issue

I’m having an issue with a prefab with a limitDOFjoint (from the ECS Physics samples) applied keeping the object static on all axes except one (for rotation)

When I instantiate that prefab in ECS, I must then move it to its position. When I try that it snaps back to origin (0,0,0) and won’t allow the entity to be moved into its position. Rightly so if the joint is applied before the position change, but that creates an issue with placing prefabs in my sub-scene. (They’re spawned through a custom map system)

Any idea what might be going on here? :thinking: I have even reverse engineered the limitDOFjoint to apply it AFTER positioning the entity, but the same result happens. However, I’m afraid I might not have understood what I reverse engineered; a system attempts to read the LoneObstacleProperties which contains two bool3 variables, one for each position/rotation axes (similarly to the limitDOFjoint) then create a limitDOFjoint based on my property variables using the reverse-engineered script from the Physics samples (limitDOFjoint.cs).

That script is posted below, and I can provide more code (subclasses) if necessary. I understand most things in the script except for how RigidTransforms work, why the joint seemingly needs 2 bodies (the un-altered script defaults to RigidTransform.identity when no 2nd body is provided. Seeing as my results are the same, I must be binding the wrong RigidTransform to the joint, at least that’s my guess. I defer to the experts :sweat_smile:

Maybe this is a corner case, but it’s killing my game at this point :man_shrugging::sos::pray:

    [BurstCompile]
    [RequireMatchingQueriesForUpdate]
    public partial struct LoneObstacleLockAxes : ISystem
    {
        public void OnCreate(ref SystemState state)
        {
            state.RequireForUpdate(state.EntityManager.CreateEntityQuery(
                new ComponentType[]
                {
                    typeof(LoneObstacleProperties),
                    typeof(LocalToWorld),
                    typeof(PhysicsWorldIndex)
                }
            ));

        }

        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            var ecb = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>().CreateCommandBuffer(state.WorldUnmanaged);

            foreach (var (loneObstacleProperties, localToWorld, localTransform, worldIndex, entity) in
                SystemAPI.Query<RefRW<LoneObstacleProperties>, LocalToWorld, LocalTransform, PhysicsWorldIndex>().WithEntityAccess())
            {
                if (!math.any(loneObstacleProperties.ValueRO.lockLinearAxes) 
                    && !math.any(loneObstacleProperties.ValueRO.lockAngularAxes))
                    continue;

                if (loneObstacleProperties.ValueRO.axesLocked)
                    continue;

                RigidTransform bFromA = math.mul(math.inverse(RigidTransform.identity),
                    Math.DecomposeRigidBodyTransform(localToWorld.Value));

                Debug.Log($"LocalToWorld: {localToWorld.Value}");

                PhysicsJoint physicsJoint = CreateLimitDOFJoint(bFromA, loneObstacleProperties.ValueRO);

                var worldIndexValue = worldIndex.Value;
                CreateJointEntity(
                    worldIndexValue,
                    new PhysicsConstrainedBodyPair(
                        entity,
                        Entity.Null,
                        false),
                    physicsJoint,
                    ecb
                );

                loneObstacleProperties.ValueRW.axesLocked = true;
            }
        }

        public Entity CreateJointEntity(uint worldIndex, PhysicsConstrainedBodyPair constrainedBodyPair,
            PhysicsJoint joint, EntityCommandBuffer ecb)
        {
            using (var joints = new NativeArray<PhysicsJoint>(1, Allocator.Temp) { [0] = joint })
            using (var jointEntities = new NativeList<Entity>(1, Allocator.Temp))
            {
                CreateJointEntities(worldIndex, constrainedBodyPair, joints, jointEntities, ecb);
                return jointEntities[0];
            }
        }

        public void CreateJointEntities(uint worldIndex, PhysicsConstrainedBodyPair constrainedBodyPair, 
            NativeArray<PhysicsJoint> joints, NativeList<Entity> newJointEntities, EntityCommandBuffer ecb)
        {
            if (!joints.IsCreated || joints.Length == 0)
                return;

            if (newJointEntities.IsCreated)
                newJointEntities.Clear();
            else
                newJointEntities = new NativeList<Entity>(joints.Length, Allocator.Temp);

            // create all new joints
            var multipleJoints = joints.Length > 1;

            for (var i = 0; i < joints.Length; ++i)
            {
                var jointEntity = ecb.CreateEntity();
                ecb.AddSharedComponent(jointEntity, new PhysicsWorldIndex(worldIndex));
                ecb.AddComponent(jointEntity, constrainedBodyPair);
                ecb.AddComponent(jointEntity, joints[i]);

                newJointEntities.Add(jointEntity);
            }

            if (multipleJoints)
            {
                // set companion buffers for new joints
                for (var i = 0; i < joints.Length; ++i)
                {
                    var companions = ecb.AddBuffer<PhysicsJointCompanion>(newJointEntities[i]);
                    for (var j = 0; j < joints.Length; ++j)
                    {
                        if (i == j)
                            continue;
                        companions.Add(new PhysicsJointCompanion { JointEntity = newJointEntities[j] });
                    }
                }
            }
        }

        public PhysicsJoint CreateLimitDOFJoint(RigidTransform offset, LoneObstacleProperties properties)
        {
            var constraints = new FixedList512Bytes<Constraint>();
            if (math.any(properties.lockLinearAxes))
            {
                constraints.Add(new Constraint
                {
                    ConstrainedAxes = properties.lockLinearAxes,
                    Type = ConstraintType.Linear,
                    Min = 0,
                    Max = 0,
                    SpringFrequency = Constraint.DefaultSpringFrequency,
                    DampingRatio = Constraint.DefaultDampingRatio,
                    MaxImpulse = float.PositiveInfinity,
                });
            }
            if (math.any(properties.lockAngularAxes))
            {
                constraints.Add(new Constraint
                {
                    ConstrainedAxes = properties.lockAngularAxes,
                    Type = ConstraintType.Angular,
                    Min = 0,
                    Max = 0,
                    SpringFrequency = Constraint.DefaultSpringFrequency,
                    DampingRatio = Constraint.DefaultDampingRatio,
                    MaxImpulse = float.PositiveInfinity,
                });
            }

            var joint = new PhysicsJoint
            {
                BodyAFromJoint = BodyFrame.Identity,
                BodyBFromJoint = offset
            };
            joint.SetConstraints(constraints);
            return joint;
        }

I think I might have corrected it by myself.

                //RigidTransform bFromA = math.mul(math.inverse(RigidTransform.identity),
                //    Math.DecomposeRigidBodyTransform(localToWorld.Value));

                RigidTransform bFromA = new RigidTransform(localTransform.ToMatrix());

That seemed to do the trick. I didn’t think it needed to come from localToWorld and found the toMatrix() method quite handy.