Wrong position in the inheritance, third child

I was working on exporting from blender last night. And I discovered one strange thing that I think I’m missing something simple. I load several cubes sequentially and set all the positions. Then I attach them to each other.

from direct.showbase.ShowBase import ShowBase
from panda3d.core import NodePath

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        root = NodePath("root")
        root.reparentTo(render)

        box_0 = loader.loadModel("box")
        box_0.set_pos(0, 0, 0)

        box_1 = loader.loadModel("box")
        box_1.set_pos(10, -10, 0)
        
        box_2 = loader.loadModel("box")
        box_2.set_pos(0, -10, 0)

        box_0.reparentTo(root)
        box_1.reparentTo(box_0)
        box_2.reparentTo(box_1)
        
        print(root.ls())

game = Game()
game.run()
PandaNode root
  ModelRoot box.egg
    GeomNode box (1 geoms: S:(TextureAttrib))
    ModelRoot box.egg T:(pos 10 -10 0)
      GeomNode box (1 geoms: S:(TextureAttrib))
      ModelRoot box.egg T:(pos 0 -10 0)
        GeomNode box (1 geoms: S:(TextureAttrib))
None

And I get the wrong positions in the third child box_2.

It should be like this:

This can be obtained if all are attached to the same node.

from direct.showbase.ShowBase import ShowBase
from panda3d.core import NodePath

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        root = NodePath("root")
        root.reparentTo(render)

        box_0 = loader.loadModel("box")
        box_0.set_pos(0, 0, 0)

        box_1 = loader.loadModel("box")
        box_1.set_pos(10, -10, 0)
        
        box_2 = loader.loadModel("box")
        box_2.set_pos(0, -10, 0)

        box_0.reparentTo(root)
        box_1.reparentTo(root)
        box_2.reparentTo(root)
        
        print(root.ls())

game = Game()
game.run()
PandaNode root
  ModelRoot box.egg
    GeomNode box (1 geoms: S:(TextureAttrib))
  ModelRoot box.egg T:(pos 10 -10 0)
    GeomNode box (1 geoms: S:(TextureAttrib))
  ModelRoot box.egg T:(pos 0 -10 0)
    GeomNode box (1 geoms: S:(TextureAttrib))
None

I am trying to understand what is happening, because I first applied the transformations, and then attached them, in this case, the transformation should not be inherited.

The transformation is always relative to the parent, so the result is correct. The two hierarchies that you show are different, so the results are different as well :slight_smile: .

Specifically, in your first hierarchy, the second box has a position of (10 -10 0); this is relative to the first box, as you want it (because the coordinates of the first box are (0, 0, 0)). But the third box has a position of (0 -10 0), relative to the second box, so its net position takes the position of that second box into account. It is the sum of (10 -10 0) and (0 -10 0): (10 -20 0). Which is indeed what you get.

If you want to attach them to each other, you will have to subtract the coordinates of all of the ancestors.

Hmm, it’s strange that this is not the case in the blender. And logically, when I set the coordinates via the set_pos () method, then without specifying an explicit node, this is done relative to “render”

from direct.showbase.ShowBase import ShowBase
from panda3d.core import NodePath

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        root = NodePath("root")
        root.reparentTo(render)

        box_0 = loader.loadModel("box")
        box_0.set_pos(root, 0, 0, 0)

        box_1 = loader.loadModel("box")
        box_1.set_pos(root, 10, -10, 0)
        
        box_2 = loader.loadModel("box")
        box_2.set_pos(root, 0, -10, 0)

        box_0.reparentTo(root)
        box_1.reparentTo(box_0)
        box_2.reparentTo(box_1)

        print(root.ls())

game = Game()
game.run()

This has no effect whatsoever. Obviously, this is not how it should work. In their right mind, they should remain in their original places, after being attached to some node.

from direct.showbase.ShowBase import ShowBase
from panda3d.core import NodePath

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        root = NodePath("root")
        root.reparentTo(render)

        box_0 = loader.loadModel("box")
        box_1 = loader.loadModel("box")
        box_2 = loader.loadModel("box")

        box_0.reparentTo(root)
        box_1.reparentTo(box_0)
        box_2.reparentTo(box_1)
        
        box_0.set_pos(root, 0, 0, 0)
        box_1.set_pos(root, 10, -10, 0)
        box_2.set_pos(root, 0, -10, 0)

        print(root.ls())

game = Game()
game.run()

The only solution is to place the positions after attaching all the nodes, which, again, is strange.

Perhaps Blender always shows/uses the net coordinates? As I’ve only just begun working with Blender, I’m not in a good “position” to comment on that. :grin:

Are you sure about that? As far as I know, set_pos always sets the given coordinates relative to the parent (even if the node currently has no parent; the coordinates are still assumed to be in the (future) parent’s local space), unless you specify the reference node as first argument.

That would indeed have no effect. What happens is this:

  • you assign a position relative to root, which has coordinates (0, 0, 0). That’s the same as not specifying a reference node at all.

  • When you reparent the box nodes, the positions that were set previously will still be interpreted as relative to the new parent (that’s how it always worked, as far as I can tell).

If you want the boxes to stay in place after reparenting, you can call wrt_reparent_to instead. That’s what it’s for. :slight_smile: But it can introduce numerical inaccuracies, so use with caution.

The question here is when the transformation was installed, before or after. I don’t think that the existing transformation should change, depending on what the object is attached to. You first set up the node, and then attach it. There are also colors, rendering attributes, and effects. I don’t think this should reset when joining a node.

This is, I believe, the purpose of “wrtToReparentTo”: it reparents a NodePath without affecting its world-space position.

I tested it on another engine, same thing. This may be normal behavior when programming a game, but when you write a hierarchy generator in a thread, it’s annoying.

@Override
    public void simpleInitApp() {
        
        flyCam.setMoveSpeed(100);
        
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Blue);
        
        Box b = new Box(1, 1, 1);
        
        Node root = new Node();
        
        Geometry box_0 = new Geometry("Box", b);
        Node box_0_n = new Node();
        box_0_n.attachChild(box_0);
        box_0_n.setLocalTranslation(0, 0, 0);
        
        Geometry box_1 = new Geometry("Box", b);
        Node box_1_n = new Node();
        box_1_n.attachChild(box_1);
        box_1_n.setLocalTranslation(10, -10, 0);
        
        Geometry box_2 = new Geometry("Box", b);
        Node box_2_n = new Node();
        box_2_n.attachChild(box_2);
        box_2_n.setLocalTranslation(0, -10, 0);

        box_0.setMaterial(mat);
        box_1.setMaterial(mat);
        box_2.setMaterial(mat);

        root.attachChild(box_0_n);
        box_0_n.attachChild(box_1_n);
        box_1_n.attachChild(box_2_n);
        
        rootNode.attachChild(root);
    }

I think it would be useful to disable some behavior for inheritance, although I think this is not possible.

1 Like