Buggy code:
public class SimulationNode { private static final Logger LOGGER = Logger . getLogger ( SimulationNode . class . getName ( ) ) ; private BlenderContext blenderContext ; private String name ; private List < SimulationNode > children = new ArrayList < SimulationNode > ( ) ; private List < Constraint > constraints ; private List < Animation > animations ; private Spatial spatial ; private Skeleton skeleton ; private AnimControl animControl ; private Transform spatialStartTransform ; private Map < Bone , Transform > boneStartTransforms ; public SimulationNode ( Long featureOMA , BlenderContext blenderContext ) { } private SimulationNode ( Long featureOMA , BlenderContext blenderContext , boolean rootNode ) { } public boolean contains ( Constraint constraint ) { } private void reset ( ) { } private void simulateSpatial ( ) { } private void simulateSkeleton ( ) { if ( ( ( constraints ) != null ) && ( ( constraints . size ( ) ) > 0 ) ) { Set < Long > alteredOmas = new HashSet < Long > ( ) ; if ( ( animations ) != null ) { TempVars vars = TempVars . get ( ) ; AnimChannel animChannel = animControl . createChannel ( ) ; for ( Animation animation : animations ) { float [ ] animationTimeBoundaries = this . computeAnimationTimeBoundaries ( animation ) ; int maxFrame = ( ( int ) ( animationTimeBoundaries [ 0 ] ) ) ; float maxTime = animationTimeBoundaries [ 1 ] ; Map < Integer , VirtualTrack > tracks = new HashMap < Integer , VirtualTrack > ( ) ; for ( int frame = 0 ; frame < maxFrame ; ++ frame ) { this . reset ( ) ; for ( Track track : animation . getTracks ( ) ) { float time = ( ( com . jme3 . animation . BoneTrack ) ( track ) ) . getTimes ( ) [ frame ] ; track . setTime ( time , 1 , animControl , animChannel , vars ) ; skeleton . updateWorldVectors ( ) ; } for ( Bone rootBone : skeleton . getRoots ( ) ) { if ( ( skeleton . getBoneIndex ( rootBone ) ) > 0 ) { this . applyConstraints ( rootBone , alteredOmas , frame ) ; } } for ( Long boneOMA : alteredOmas ) { BoneContext boneContext = blenderContext . getBoneContext ( boneOMA ) ; int boneIndex = skeleton . getBoneIndex ( boneContext . getBone ( ) ) ; if ( ! ( tracks . containsKey ( boneIndex ) ) ) { tracks . put ( boneIndex , new VirtualTrack ( maxFrame , maxTime ) ) ; } } alteredOmas . clear ( ) ; for ( Map . Entry < Integer , VirtualTrack > trackEntry : tracks . entrySet ( ) ) { Bone bone = skeleton . getBone ( trackEntry . getKey ( ) ) ; Transform startTransform = boneStartTransforms . get ( bone ) ; Vector3f bonePositionDifference = bone . getLocalPosition ( ) . subtract ( startTransform . getTranslation ( ) ) ; <START_BUG> Quaternion boneRotationDifference = bone . getLocalRotation ( ) . mult ( startTransform . getRotation ( ) . inverse ( ) ) . normalizeLocal ( ) ; <END_BUG> Vector3f boneScaleDifference = bone . getLocalScale ( ) . divide ( startTransform . getScale ( ) ) ; trackEntry . getValue ( ) . setTransform ( frame , new Transform ( bonePositionDifference , boneRotationDifference , boneScaleDifference ) ) ; } } for ( Map . Entry < Integer , VirtualTrack > trackEntry : tracks . entrySet ( ) ) { Track newTrack = trackEntry . getValue ( ) . getAsBoneTrack ( trackEntry . getKey ( ) ) ; if ( newTrack != null ) { boolean trackReplaced = false ; for ( Track track : animation . getTracks ( ) ) { if ( ( ( ( com . jme3 . animation . BoneTrack ) ( track ) ) . getTargetBoneIndex ( ) ) == ( trackEntry . getKey ( ) . intValue ( ) ) ) { animation . removeTrack ( track ) ; animation . addTrack ( newTrack ) ; trackReplaced = true ; break ; } } if ( ! trackReplaced ) { animation . addTrack ( newTrack ) ; } } } } vars . release ( ) ; animControl . clearChannels ( ) ; this . reset ( ) ; } } } private void applyConstraints ( Bone bone , Set < Long > alteredOmas , int frame ) { } public void simulate ( ) { } private float [ ] computeAnimationTimeBoundaries ( Animation animation ) { } private List < Constraint > findConstraints ( Long ownerOMA , BlenderContext blenderContext ) { } @ Override public String toString ( ) { } No newline ; at end ; of file ; }

Fix:
Quaternion boneRotationDifference = startTransform . getRotation ( ) . inverse ( ) . mult ( bone . getLocalRotation ( ) ) . normalizeLocal ( ) ;