scale and pos confusing me

hi. i know it’s very basic, but the way scale affects positioning is causing me some grey hairs.

read that the smart solution is to create a dummy node and then use that to move the other nodes around.

but i find that it is still being affected by the scale command. which means i am fundamentally mis-understanding something.

    def make_planet(self):
        self.move_node = render.attachNewNode('dummy_node')
        self.planet_node = render.attachNewNode('planet_node'+str(self.ID))
        self.planet_mesh = loader.loadModel('sphere')
        self.texture = loader.loadTexture("whiteTexture.png")
        self.planet_mesh.setTexture(self.texture,1)
        self.planet_mesh.setScale(self.size)
        self.planet_mesh.reparentTo(self.planet_node)
        self.planet_node.reparentTo(self.move_node)
        self.move_node.setPos(self.pos[0],self.pos[1],self.pos[2])
        self.square_select(self.move_node)
        return self.planet_node

so, if someone could explain where i am stuffing up that’d be appreciated. :smiley:

Hmm… On the face of it I think that it should work, but there’s little information to work with in what you’ve posted.

So, a few questions which may help:

How are you finding scaling to affect positioning?

Is your planet sphere mesh centred around the origin? (Within its own space, that is; where is the model’s (0, 0, 0) in the modelling program?)

What does “self.square_select” do?

I find the “ls” command very helpful with scene graph questions. It prints out the scene graph structure along with (local) transforms on the console. So you can see what exactly the result of your reparenting is:

render.ls()
self.move_node.ls()

Hello,

this is no direct answer to your question, but for my understanding:

        return self.planet_node

Is there a special need to have an extra move-handler (and return the contained NP)? I have not heard that it is better to have an extra NP in line to move a single other node, just as a matter of principle. It is a good idea, though, if you want to apply the rotation to the planet and the translation and rotation referred to the solar-system to the other NP, for example.

        self.planet_node = render.attachNewNode('planet_node'+str(self.ID))

It may be a little more efficient to not attach the new node to render, as the attachNewNode is a combination of creating the NodePath and attaching it to render. Better:

self.planet_node = NodePath('planet_node'+str(self.ID))
[..]
self.planet_node.reparentTo(self.move_node)

or directly attach the new node to your move-node:

        self.planet_node = self.move_node.attachNewNode('planet_node'+str(self.ID))

BR,
Michael

At a first glance your hierarchy looks fine, and I think it does make sense to have basically:

translation_node
scale_node
geometry_node

to separate the translation and scale and prevent them from interfering with each other.

At a guess I’d suggest that your problem isn’t in the node setup, the way setPos() and setScale() work, or your understanding of their interactions, and so is more likely in the data you’re passing in. How are self.pos and self.size being set? Is there any chance that self.pos is being affected by self.size before you even get to the make_planet function?

–jonah

Thanks for the help I learnt some new and interesting things. After trying many fixes including flatten light and compass effects I substituted the geometry mesh with a new one, and that solved it. Maddening while trying, but a big relief to solve.

rather than join the people spamming the boards, i’d ask a related question here.

I am now having huge trouble getting the planet to orbit a point. rotating around its own axis is no problem using hprinterval, but when i try do the same thing to its parent node nothing happens.

now i have managed to get the planets moon rotating around the planet so cannot for the life of me figure out why the planet refuses to budge.

to help me understand what is going on i have highlited each node in a color and then lablled it in space.

on the left you can see the multiple yellow boxes each represent the self.planet_np (2nd level node path) of multiple other planets as well.

red is the top level move_np mesh

and there is also a green square which somehow represents the actual mesh is if fact sitting right under the yellow node path representational mesh at 0,0,0

now it should be simple to rotate teh mesh around the green or yellow nodes. but it just doesnt happen.

    def make_planet(self):
        self.move_np = render.attachNewNode('dummy_node') # empty node
        self.planet_np = render.attachNewNode('planet_node'+str(self.ID))
        self.planet_mesh = loader.loadModel('smiley')
        self.texture = loader.loadTexture(self.p_tex)
        #self.planet_mesh.reparentTo(render)
        self.planet_mesh.setTexture(self.texture,1)
        self.texture.setMagfilter(Texture.FTLinear)
        self.texture.setMinfilter(Texture.FTLinear)
        #self.planet_mesh.setHpr(0,45,0)
        self.planet_mesh.setScale(self.size)
        #self.planet_mesh.setEffect(CompassEffect.make(render, CompassEffect.PScale))
        #self.planet_mesh.flattenLight()
        self.planet_mesh.reparentTo(self.planet_np)
        self.planet_np.reparentTo(self.move_np)
        self.move_np.setPos(self.pos[0],self.pos[1],self.pos[2]+random.randint(2,8))
        #self.planet_mesh.setPos(0,0,0)
        self.square_select(self.move_np,True) #red
        self.square_select(self.planet_np,True,(1,1,0.1,1)) # yellow
        self.square_select(self.planet_mesh,False,(0.1,0.8,0.1,1)) #green
        self.planet_mesh.setHpr(0,30,0)
        rotate = self.planet_mesh.hprInterval(5, Vec3(359,30,0)) # rotate on own axis
        rotate2 = self.planet_np.hprInterval(30, Vec3(360,0,0)) # rotate around another axis
        parallel2 = Parallel(rotate,rotate2)
        parallel2.loop()
        return self.planet_np

i have got the moon to rotate around the parent planet using the below code so i do not understand why on earth the planet wont rotate around either of it’s parent nodes

    def make_moon(self,pos):
        self.moon_move_np = render.attachNewNode('moon_dummy_node')
        self.moon_np = render.attachNewNode('moon_node'+str(self.ID))
        self.moon_mesh = loader.loadModel('smiley')
        self.texture = loader.loadTexture(self.m_tex)
        self.moon_mesh.setTexture(self.texture,1)
        self.texture.setMagfilter(Texture.FTLinear)
        self.texture.setMinfilter(Texture.FTLinear)
        #self.moon_mesh.reparentTo(render)
        self.moon_mesh.setScale(0.5)
        self.moon_mesh.flattenLight()
        self.moon_mesh.reparentTo(self.moon_np)
        self.moon_np.reparentTo(self.moon_move_np)
        self.moon_move_np.reparentTo(self.move_np)
        self.moon_np.setPos(0,self.size*3,0)
        #self.square_select(self.moon_np,False,(0.4,0.4,1,1))
        #self.square_select(self.moon_mesh,False,(0.1,0.1,0.8,1))
        #self.square_select(self.moon_move_np,False,(0.9,0.9,0.9,1))

        orbit = self.moon_move_np.hprInterval(self.moon_speed, Vec3(359,0,0))
        parallel = Parallel(orbit)
        parallel.loop()

NOT solver. i though it was but the moon orbits gone whakky. back to drawing board. how can such a simple thing be so deceptively tricky?

edit — changing

self.move_np.setPos(self.pos[0],self.pos[1],self.pos[2]+random.randint(2,8))

to

self.planet_mesh.setPos(self.pos[0],self.pos[1],self.pos[2]+random.randint(2,8))

solves the problem. now the planet moves. but it stuffs up the moons which now orbit around 0,0,0

I fear that I haven’t read through all of your code, so please forgive me if the following is already implemented, or is not suitable for your setup, but the following occurs to me as a means of getting orbits working:

Option 1:
This should be fairly simple, but might call for a little more work if you want non-circular orbits.

In short, parent each object to the object about which it orbits.

In detail:
I’ll for now presume that the NodePath around which the planet is to orbit is called “sun”. (In your hierarchy it might be something like “sun.move_np”; for now I’ll stick to simply calling it “sun”.)

You then parent the planet’s top-level NodePath (your “move_np” node, I believe) to “sun”; rotating “sun” should then cause the planet to appear to orbit, albeit with “sun” likely turning to face it as it does so. The moon could be treated similarly, with the planet replacing “sun”.

If you want to de-couple the rotation of the model from the orbital speed of its children, you might find it useful to add a new child NodePath to each “move_np”, a sibling of whichever NodePath you currently have there, and then parent your orbiting bodies to that and have that rotate. If you want different orbital speeds for all bodies, simply have a new sibling NodePath for each orbiting body.

Option 2
This is perhaps a little more mathematical, but, given the appropriate maths, should allow fairly easily for more varied orbits.

In this case, give each orbiting body a reference to a NodePath around which it is intended to orbit - the planet in the case of the moon, or the sun in the case of the planet, for example. (I suggest a NodePath rather than a point to allow for bodies orbiting bodies that are themselves orbiting, as in the case of the moon.)

You might then employ a LerpFunc (see the bottom of this page), the function of which would then be the equation that describes the position of the body for a given point in time (whether circular or elliptical), and which takes the NodePath mentioned above (about which the body orbits), using its position as the origin point for the function.

Try this:

from direct.showbase.ShowBase import ShowBase 
from direct.interval.IntervalGlobal import Parallel
from panda3d.core import Vec3
class Game(ShowBase): 
    def __init__(self): 
        ShowBase.__init__(self) 
        # Empty node for "sun" around which everything orbits
        self.sun_np = render.attachNewNode('sun_node')
        self.sun_np.setPos(0, 50, 0)   # Offset to put in front of camera

        self.planet_a_np = self.make_planet(5, 1, 1, False)
        self.planet_a_np.reparentTo(self.sun_np)

        self.planet_b_np = self.make_planet(20, 2, 6, True)
        self.planet_b_np.reparentTo(self.sun_np)

    def make_planet(self, orbital_distance, size, speed, add_moon): 
        # Empty node as a "handle" to move everything around.
        move_np = render.attachNewNode('dummy_node')

        # Offset for "orbital distance" from movement handle.
        planet_np = move_np.attachNewNode('planet_node') 
        planet_np.setPos(orbital_distance, 0, 0)

        # Mesh to represent planet. 
        planet_mesh = loader.loadModel('smiley') 
        planet_mesh.reparentTo(planet_np) 
        planet_mesh.setScale(size)

        # Adjust handle's rotation to orbit planet.
        orbital_rotation = move_np.hprInterval(speed * 10, Vec3(360, 0, 0))
        orbital_rotation.loop()

        # Adjust planet MESH's rotation to rotate planet.
        own_rotation = planet_mesh.hprInterval(speed, Vec3(0, 360, 0))
        own_rotation.loop()

        if add_moon :
            # Offset for "orbital distance" from planet center.
            moon_np = planet_np.attachNewNode('moon_node')
            moon_np.setPos(size * 2.5, 0, 0)

            # Adjust planet NODE's rotation to orbit moon.
            moon_orbital_rotation = planet_np.hprInterval(speed * 4, Vec3(360, 0, 0))
            moon_orbital_rotation.loop()

            # Mesh to represent moon.
            moon_mesh = loader.loadModel('smiley') 
            moon_mesh.reparentTo(moon_np) 
            moon_mesh.setScale(0.25)

        return move_np

Game().run()

I moved things around to make it clearer what the hierarchical relationship was, removed extra dross, and added some whitespace for readability. More importantly, I changed which nodes the intervals are applied to.

I think of the hierarchy as a stick with a ball on the end, where move_np is the end of the stick we hold on to and planet_np is the end of the stick with the ball. planet_np.setPos() controls the length of the stick, planet_mesh.setScale() controls the size of the ball. To “orbit” we change the rotation on move_np; to rotate the ball we change the rotation on planet_np. The same idea applies to the moon, except here planet_np becomes the holding end and moon_np is the ball end.

Two other notes:

  1. I removed a bunch of instances of “self.” from make_planet(). Because I’m calling it multiple times, these would end up redefining the relevant variables and stomping on each other.
  2. My make_planet() is returning move_np, not planet_np. In my analogy above, move_np is the “holding end” of the stick, or the “handle”, and as the root of the hierarchy is the only thing anyone external should care about.

Finally, note that changing rotation will of course only be able to provide completely circular orbits. If you want elliptical ones you’ll need to use maths to compute new values for planet_np.setPos().

–jonah

thank you Thaumaturge, i get a better sense of the hierachy needed and I’ll probably move on to the math option you mentioned next

11thPenguin that the ball and rodwas just the kind of insight i was lacking. thank you very much. your sample code helped me understand a ton more.