Bullet softbody (character's cloak) position issues. Can't get correct behavior

Hi,
I have cloak applied to character as softbody. It fits tight to the character if character z position is 0 (by default). This is fine. But my character also has capsule collider (rigidbody) and character itself needs to be shifted down, i.e. self.korlan.setZ(-1) to be fully covered by capsule. A-a-a-nd then my cloak, of course, leaves higher than character. I set cloak z position to -1 and here softbody behavior becomes broken: when I rotate character the cloak rotates too, but it gets shifted to left or right or just rotates in the opposite direction.

I don’t know how to solve this trouble with Bullet. Can someone help me?

Here is my sample code:

        # cloth stuff
        self.world = BulletWorld()
        self.world.setGravity(Vec3(0, 0, -9.81))

        self.debugNP = render.attachNewNode(BulletDebugNode('Debug'))
        # self.debugNP.show()
        # self.debugNP.node().showWireframe(True)
        # self.debugNP.node().showConstraints(True)
        # self.debugNP.node().showBoundingBoxes(False)
        # self.debugNP.node().showNormals(True)

        self.world.setDebugNode(self.debugNP.node())

        # Plane
        shape = BulletPlaneShape(Vec3(0, 0, 1), 0)
        node = BulletRigidBodyNode('Ground')
        node.addShape(shape)
        np = render.attachNewNode(node)
        np.setPos(0, 0, 0)
        self.world.attachRigidBody(node)

        self.world.setGroupCollisionFlag(0, 0, False)

        # Player
        self.korlan = Actor("{0}/Assets/Actors/NPC/NPC_Ernar/Korlan.egg.bam".format(self.game_dir),
                            {"idle": '{0}/Assets/Animations/Korlan-Standing_idle_female.egg'.format(self.game_dir),
                             "walk": "{0}/Assets/Animations/Korlan-Walking.egg".format(self.game_dir),
                             "left_t": "{0}/Assets/Animations/exp/Korlan-left_turn.egg".format(self.game_dir),
                             "right_t": "{0}/Assets/Animations/exp/Korlan-right_turn.egg".format(self.game_dir),
                             "slash": "{0}/Assets/Animations/Korlan-great_sword_slash.egg".format(self.game_dir),
                             "draw_bow": "{0}/Assets/Animations/archering/Korlan-archer_standing_draw_arrow.egg".format(
                                 self.game_dir),
                             }
                            )

        self.korlan.reparent_to(render)

        self.korlanRigidNP = None

        # Player
        bullet_char_contr_node = BulletCharacterControllerNode(shape,
                                                               0.4,
                                                               col_name)
        self.korlanRigidNP = self.render.attach_new_node(bullet_char_contr_node)
        self.world.attach(bullet_char_contr_node)
        self.korlanRigidNP.set_collide_mask(BitMask32.bit(0))
        self.korlan.reparent_to(self.korlanRigidNP)
        self.korlan.setZ(-1)

        # Soft body world information
        info = self.world.getWorldInfo()
        info.setAirDensity(0.2)
        info.setWaterDensity(0)
        info.setWaterOffset(0)
        info.setWaterNormal(Vec3(0, 0, 0))

        # load the cloak model
        model = loader.loadModel('{0}/Assets/Actors/Korlan/Korlan_cloak.bam'.format(self.game_dir))
        geom = model.findAllMatches('**/+GeomNode').getPath(0).node().modifyGeom(0)
        geomNode = GeomNode('')
        geomNode.addGeom(geom)
        node = BulletSoftBodyNode.makeTriMesh(info, geom)
        node.linkGeom(geomNode.modifyGeom(0))

        # material and properties setup
        node.getMaterial(0).setLinearStiffness(0.5)
        node.getCfg().setDampingCoefficient(0.01)
        node.getCfg().setPoseMatchingCoefficient(0.2)
        node.setPose(False, False)
        node.setTotalMass(1)

        softNP = render.attachNewNode(node)
        self.world.attachSoftBody(node)
        geomNP = softNP.attachNewNode(geomNode)
        geomNP.setTwoSided(True)
        
        # pin it down
        self.hip_bone = self.korlan.exposeJoint(None, 'modelRoot', 'Korlan:Spine2')
        self.pin = self.hip_bone.attachNewNode(BulletRigidBodyNode('pin'))
        
        # the model should know what verts to pin
        for vertex in json.loads(model.getTag('pins')):
            softNP.node().appendAnchor(softNP.node().getClosestNodeIndex(Vec3(*vertex), True), self.pin.node())

        # Task
        taskMgr.add(self.update, 'updateWorld')
        
    def update(self, task):
        dt = globalClock.getDt()
        # self.pin is a child of self.hip_bone but without these lines it won't work
        self.pin.setPos(render, self.hip_bone.getPos(render))
        self.pin.setHpr(render, self.hip_bone.getHpr(render))
        self.world.doPhysics(dt, 10, 0.004)

        return task.cont

Here how it does look:


with self.korlan.setZ(-1) (after reparenting to rigidbody node)


with self.korlan.setZ(0) (after reparenting to rigidbody node)