Annoying behaviour of scale change while reparenting

Hi,

I just want to mention an annoying behaviour that has been bugging my code for some days. Although basic it was well hidden so I just over simplifyied it to bring it to your attention.

So, I have an object that I want to be picked up alternatively by picker0 or picker1.
One can assumes for instance that the object is a book that will be passed from avatar0 to avatar1.

At load time I know the dimensions of the object and the avatars and I scale them properly in the scene.

The issue is the following, whenever the object is passed (reparented) from picker0 to picker1, its size changes… which is quite annoying.

import direct.directbase.DirectStart
from pandac.PandaModules import *
import time
import sys

class TEST(object):
    def __init__(self):
        """ simple coordinate test """
        base.disableMouse()

        # picker 0
        self.p0 = loader.loadModel('unit_cube.egg')
        self.p0.reparentTo(render)
        self.p0.setScale(1,1,1)     # scale 1
        self.p0.setColor(1,0,0,1)   # red
        self.p0.setPos(-10,0,0)
        
        # picker 1
        self.p1 = loader.loadModel('unit_cube.egg')
        self.p1.reparentTo(render)
        self.p1.setScale(5,5,5)     # scale 5
        self.p1.setColor(0,0,1,1)   # blue
        self.p1.setPos(10,0,0) 
        
        # object
        self.obj = loader.loadModel('unit_cube.egg')
        self.obj.reparentTo(render)
        self.obj.setScale(1,1,1)     # scale 1
        self.obj.setColor(0,1,0,1)   # green
        self.obj.setPos(0,0,0)

        base.camera.setPos(0,-200,0)
        base.accept( "escape", sys.exit)
        
        self.toggle = True
        taskMgr.add(self.Update,'toggle_reparent')


    def Update(self,task): 
        """ makes the object move to new father """
        if self.toggle:
            self.obj.reparentTo(self.p0)
            self.obj.setPos(0,0,5)  
        else:
            self.obj.reparentTo(self.p1)
            self.obj.setPos(0,0,5)  

        time.sleep(1)
        self.toggle = not self.toggle
        return task.cont

TEST()
run()

Have a look, you’ll see the annoyance, since it is not assumed to change shape or size !!

So my question is the following: is there a simple way, once objects model are loaded into Panda scene, to make sure that after having resized them properly at init, they don’t change size anylonger (unless asked)???

thks,
Jean-Claude

I’ve got two possibilities depending on your requirements. One way is to call flattenLight on the parent after scaling but before setting position or parenting anything to it. This sets the vertices in their current position and puts the node scale back to 1, which might not be what you want.

        # picker 1 
        self.p1 = loader.loadModel('unit_cube.egg') 
        self.p1.reparentTo(render) 
        self.p1.setScale(5,5,5)     # scale 5 
        self.p1.flattenLight()
        self.p1.setColor(0,0,1,1)   # blue 
        self.p1.setPos(10,0,0)

The other way answering specifically your question in bold type is to apply a CompassEffect with the PScale property to the child node.

        # object 
        self.obj = loader.loadModel('unit_cube.egg') 
        self.obj.reparentTo(render) 
        self.obj.setScale(1,1,1)     # scale 1
        self.obj.setEffect(CompassEffect.make(render, CompassEffect.PScale))
        self.obj.setColor(0,1,0,1)   # green 
        self.obj.setPos(0,0,0)

Edit: breaking that down a bit I realize I need to explain a little more…

CompassEffect.make(render, CompassEffect.PScale)

Okay, the render in that line is where the node will be inheriting its scale from now on. This isn’t quite what you asked for. I don’t know if it is legal to specify the same node you are applying the effect to. Could be! But if not you might end up having to create a “dummy” node to hold the scale.

the compass effect should do the trick. another way to do this would be to use wrtReparentTo which will change the parent without changing position, rotation or scale. you’d have to update position and rotation later on but might be useful in some situations.

Or maybe something like below when reparenting… (if you want to keep only the scale):

size = np.getScale(oldParent)
np.reparentTo(newParent)
np.setScale(oldParent, size)

Although I think what you really want is just np.wrtReparentTo(newParent) like Thomas said.

Thank you guys.

Just to mention that wrtReparentTo is not the way since then the relative scale of each parent object would have to be considered anytime a transformation (setPos,…) will occur (this would be a pain).

BTW. flattenLight wouldn’t do the trick since it would screw up the picker’s structure which is a rigged animated character!

So I’ll go with the Compass trick, which appears much more elegant.

Here is the code

import direct.directbase.DirectStart
from pandac.PandaModules import *
import time
import sys

class TEST(object):
    def __init__(self):
        """ simple coordinate test """
        base.disableMouse()

        # picker a0
        self.pa0 = loader.loadModel('unit_cube.egg')
        self.pa0.reparentTo(render)
        self.pa0.setScale(1,1,1)     # scale 1
        self.pa0.setColor(1,0,0,1)   # red
        self.pa0.setPos(-10,0,10)
        
        # picker a1
        self.pa1 = loader.loadModel('unit_cube.egg')
        self.pa1.reparentTo(render)
        self.pa1.setScale(5,5,5)     # scale 5
        self.pa1.setColor(0,0,1,1)   # blue
        self.pa1.setPos(10,0,10) 
        
        # object a
        self.obja = loader.loadModel('unit_cube.egg')
        self.obja.reparentTo(render)
        self.obja.setScale(1,1,1)     # scale 1
        self.obja.setColor(0,1,0,1)   # green
        self.obja.setPos(0,0,10)

        #--- revisited approach

        # picker b0
        self.pb0 = loader.loadModel('unit_cube.egg')
        self.pb0.reparentTo(render)
        self.pb0.setScale(1,1,1)     # scale 1
        self.pb0.setEffect(CompassEffect.make(render, CompassEffect.PScale)) 
        self.pb0.setColor(1,0,0,1)   # red
        self.pb0.setPos(-10,0,-10)
        
        # picker b1
        self.pb1 = loader.loadModel('unit_cube.egg')
        self.pb1.reparentTo(render)
        self.pb1.setScale(5,5,5)     # scale 5
        self.pb1.setEffect(CompassEffect.make(render, CompassEffect.PScale)) 
        self.pb1.setColor(0,0,1,1)   # blue
        self.pb1.setPos(10,0,-10) 
        
        # object b
        self.objb = loader.loadModel('unit_cube.egg')
        self.objb.reparentTo(render)
        self.objb.setScale(1,1,1)     # scale 1
        self.objb.setEffect(CompassEffect.make(render, CompassEffect.PScale)) 
        self.objb.setColor(0,1,0,1)   # green
        self.objb.setPos(0,0,-10)

        base.camera.setPos(0,-200,0)
        base.accept( "escape", sys.exit)
        
        self.toggle = True
        taskMgr.add(self.Update, 'toggle_reparent')


    def Update(self,task): 
        """ this task makes the object node move to new father """
        if self.toggle:
            self.obja.reparentTo(self.pa0)
            self.obja.setPos(0,0,5)
            
            self.objb.reparentTo(self.pb0)
            self.objb.setPos(0,0,5)
            
        else:
            self.obja.reparentTo(self.pa1)
            self.obja.setPos(0,0,5)
            
            self.objb.reparentTo(self.pb1)
            self.objb.setPos(0,0,5)  

        time.sleep(1)
        self.toggle = not self.toggle
        return task.cont

TEST()
run()

Actually I don’t know if the Compass calculation will add an overhead behind the scene…

Having said so, I tend to consider that there should be a way in PANDA3D to freeze the scale of models and set a standard unit once all the pieces are loaded in a scene !