Bug ? Rare problem with LerpFunc

Hello i have a scenario, an ambient light named self.__sunlight and i want do a daycycle effect (Sun, Moon) changing ambient RGB values. then i have this methods on my FSM.

The problem is RARE, too rare i think, when i set the machine to SunState the colors start to up good, but if before that color value UP to 1.0, i cut the lerpFunc when the light RGB go in 0.6 and set the machine to MoonState, this stop and finish the current lerpFunc (in exitSunState),

later MUST start the MoonState lerpFunc, starting down FROM THE CURRENT COLOR VALUE CUTTED 0.6 (Because i finish it!)

but i can see a rare action of panda3d, i see as the ambientlight is setted directly to 1.0 and later start to down until 0.3…

the question is WHY set 1.0! if i cut the process before, in 0.6
And why this bug not is repeated when i cut MoonState to SunState, but yes in SunState to MoonState ?

When i cut MoonState to SunState, the screens NOT seted to the minimun 0.3 (look todata of lerp)

Why when cut SunState for set MoonState yes?

    def enterMoonState(self):
        print 'moon'
        self.__intervals['sunlight_fade'] = LerpFunc(self.__set_sunlight, fromData = self.__sunlight.getColor()[0], toData = 0.3, duration = 5.0, blendType = 'noBlend', extraArgs = [], name ='sunlight_fade')
        self.__intervals['sunlight_fade'].start()
    
    def exitMoontState(self):
        if self.__intervals.has_key('sunlight_fade'):
            self.__intervals['sunlight_fade'].finish()
            del(self.__intervals['sunlight_fade'])
    
    def enterSunState(self):
        print 'sun'
        self.__intervals['sunlight_fade'] = LerpFunc(self.__set_sunlight, fromData = self.__sunlight.getColor()[0], toData = 1.0, duration = 5.0, blendType='noBlend', extraArgs = [], name = 'sunlight_fade')
        self.__intervals['sunlight_fade'].start()
    
    def exitSunState(self):
        if self.__intervals.has_key('sunlight_fade'):
            self.__intervals['sunlight_fade'].finish()
            del(self.__intervals['sunlight_fade'])

You can test this executable.

or download from here: dpaste.com/112664/

You can see the problem or bug with this output:
moon
sun
Sunlight RGB value BEFORE finish: 0.854492008686
SUNlight RGB AFTER finish: 1.0 <— BUG?
moon
sun

from direct.showbase.DirectObject import DirectObject

from direct.gui.DirectGui import *

from direct.interval.IntervalGlobal import *

from pandac.PandaModules import *

from direct.fsm import FSM



import direct.directbase.DirectStart





class Game(object):



    def __init__(self):

        self.gamestate = None

        taskMgr.add(self.game_loop, 'game_loop')



    def game_loop(self, task):

        # Main game loop

        self.gamestate.update(task)

        return task.cont





    

class GameState(DirectObject):



    def __init__(self, game):

        DirectObject.__init__(self)

        self.game = game



    def update(self, task):

        print 'GameState.update() not implemented!'





        

class GameMap(GameState, FSM.FSM):



    def __init__(self, game):

        GameState.__init__(self, game)

        FSM.FSM.__init__(self, 'GameMap')

        self.npath = None

        self.__sunlight = None

        self.__intervals = {}

        self.__sun_ctrl = None

        self.__setup()



    def __setup(self):



        # Setup the user interface

        self.__day_ctrl = DirectOptionMenu(text='Day Control', scale=0.1,

                    items=['Sun/Moon Cycle', 'Eternal Moon', 'Eternal Sun'], highlightColor=(0.65,0.65,0.65,1),

                    pos=(-1.5, 0., 0.), command=self.__sun_ctrl_cb)

        self.__day_ctrl.set(0)

        # Setup the camera

        base.disableMouse()

        base.cam.setPosHpr(0., 0., 300., 0., -90., 0.)

        # Setup the terrain model

        self.terrain_npath = loader.loadModel("models/environment")

        self.terrain_npath.setTag('type', 'scenario')

        self.terrain_npath.reparentTo(render)

        # Setup light

        self.__sunlight = AmbientLight('sunlight')

        self.__sunlight.setColor(VBase4(0.0, 0.0, 0.0, 1.0))

        alnp = render.attachNewNode(self.__sunlight)

        render.setLight(alnp)

        

    def enterMoonState(self):

        print 'moon'

        self.__intervals['sunlight_fade'] = LerpFunc(self.__set_sunlight, fromData = self.__sunlight.getColor()[0], 

                                                toData = 0.3, duration = 7.0, blendType = 'noBlend',

                                                extraArgs = [], name ='sunlight_fade')

        self.__intervals['sunlight_fade'].start()

    

    def exitMoontState(self):

        if self.__intervals.has_key('sunlight_fade'):

            self.__intervals['sunlight_fade'].finish()

            del(self.__intervals['sunlight_fade'])

    

    def enterSunState(self):

        print 'sun'

        self.__intervals['sunlight_fade'] = LerpFunc(self.__set_sunlight, fromData = self.__sunlight.getColor()[0],

                                                toData = 1.0, duration = 7.0, blendType='noBlend',

                                                extraArgs = [], name = 'sunlight_fade')

        self.__intervals['sunlight_fade'].start()

    

    def exitSunState(self):

        if self.__intervals.has_key('sunlight_fade'):
	    print 'Sunlight RGB value BEFORE finish:', self.__sunlight.getColor()[0]

            self.__intervals['sunlight_fade'].finish()
	    print 'SUNlight RGB AFTER finish:', self.__sunlight.getColor()[0], '<--- BUG?'

            del(self.__intervals['sunlight_fade'])

                    

    def __set_sunlight(self, v):

        self.__sunlight.setColor(VBase4(v,v,v,1.0))

        

    def __sun_ctrl_cb(self, select):

        if select == 'Eternal Moon':

            if self.__intervals.has_key('auto_suncycle'):

                self.__intervals['auto_suncycle'].finish()

                del(self.__intervals['auto_suncycle'])

            self.request('MoonState')

        elif select == 'Eternal Sun':

            if self.__intervals.has_key('auto_suncycle'):

                self.__intervals['auto_suncycle'].finish()

                del(self.__intervals['auto_suncycle'])

            self.request('SunState')

        elif select == 'Sun/Moon Cycle':

            if not self.__intervals.has_key('auto_suncycle'):

                self.__intervals['auto_suncycle'] = Sequence(Func(self.request, 'SunState'), Wait(5.0),

                                                            	    Func(self.request, 'MoonState'), Wait(5.0),

                                                                    name='auto_suncycle')

                self.__intervals['auto_suncycle'].loop()

            

    def update(self, task):

        pass













if __name__ == '__main__':

    # Run the game

    game = Game()

    game.gamestate = GameMap(game)

    run()

I’m not entirely sure I understand the problem, but I think you’re asking why, when you call finish(), does the color value jump to its final value of 1.0?

The answer is that this is what finish() is supposed to do. Calling finish() tells the interval to skip to the end, and leave everything in its final state, exactly as if the interval had played all the way to the end and stopped normally.

If you don’t want this behavior, just call pause() instead. Calling pause() interrupts the interval exactly where it is, and leaves things where they are at the moment. If you never resume the interval, it will remain there. (But if you do resume the interval later, it will pick up where it left off.)

Normally, finish() is preferable to pause(), because it is predictable. After you have called finish(), the state of the world is in a known state, and it is always in the same state. After you call pause(), the state of the world is unknown and random.

David

and why this not change when cut the Moon lerp, why when i cut, moon and set sun, the light not is seted automatically to 0.3 ?

is RARE!

and if use pause, this never is removed from the manager, i need remove it, remove method request me a argument, really dont know why this have the same method that taskmanager, where request a taskname … :S

This is probably because of this typo in your code:

    def exitMoontState(self): 

You have misspelled “exitMoonState”, so your moon exit function is never called. Thus, the Moon interval is never finished, but a new Sun interval is started anyway. That means you have two intervals affecting the same object, and it’s random which one will win. This randomness probably accounts for the rarity you’re seeing.

Pausing an interval does remove it from the task manager. There is no need to call any explicit remove function.

David

Pause not possible that that remove it, because i can use resume, then this is stored waiting a resume no?

PD: OMG my exitMoonState…

resume() re-adds it to the task manager.

You can inspect the current active tasks on the task manager with:

print taskMgr

Or are you talking about some other manager?

David

I say that if i use PAUSE method, the task continue in the taskmanager, and this dont remove it.

Other thing:
a LerpFunc is managed by the TaskManager? why the lerp is renamed to LERPNAME-play ?

Well, I say that it doesn’t. :slight_smile:

>>> i = LerpFunc(f, 30, name = 'i')
>>> i.start()
>>> taskMgr
AsyncTaskManager TaskManager

  Task chain "default"
   Task                             sleep(s)   dt(ms)      avg      max   sort
  -----------------------------------------------------------------------------
   resetPrevTransform                             0.0      0.0      0.0    -51
   dataLoop                                       0.1      0.1      2.2    -50
   eventManager                                   0.1      0.2     10.7      0
   i-play                                                                    0
   ivalLoop                                       0.1      0.1      5.3     20
   collisionLoop                                  0.0      0.0      0.5     30
   shadowCollisionLoop                            0.0      0.0      0.6     44
   igLoop                                         3.9      2.4     11.8     50
   audioLoop                                      0.0      0.0      0.5     60

>>> i.pause()
0.97496804761498623
>>> taskMgr
AsyncTaskManager TaskManager

  Task chain "default"
   Task                             sleep(s)   dt(ms)      avg      max   sort
  -----------------------------------------------------------------------------
   resetPrevTransform                             0.0      0.0      0.0    -51
   dataLoop                                       0.1      0.1      2.2    -50
   eventManager                                   0.1      0.1     11.8      0
   ivalLoop                                       0.0      0.1      5.3     20
   collisionLoop                                  0.0      0.0      0.5     30
   shadowCollisionLoop                            0.0      0.0      0.6     44
   igLoop                                         8.4      1.8     25.4     50
   audioLoop                                      0.0      0.0      0.6     60

See, the task is gone.

Some interval types (those, like LerpFunc, that inherit from Interval, and are implemented entirely in Python) are managed directly by the TaskManager. Other intervals types (those that inherit from CInterval, and are implemented partially or entirely in C++) are managed by the IntervalManager. This behavior may change in a future version of Panda.

Why not? It’s just to help keep it from getting confused with other tasks that might have a similar name.

David

In Freenode chat some people tellme that not managed by taskmanager because is a lerp not a task

Then i can use the TaskMgr.getTaskName() for search it and pause it?

But if this change my task name this confuse me, i never know the current name of my task!

Don’t search for it in the task manager. Use pause() to remove it, that’s what the method is for. It really does remove it, trust me. If anyone tells you otherwise he’s mistaken.

David