StencilAttrib not updating as expected

I managed to animate the Stencil frameSize thanks to a previous topic I had opened, now the issue is I can only animate it once not twice,

Here is my code:

from panda3d.core import *
from direct.gui.DirectGui import *
from direct.interval.LerpInterval import *

import direct.directbase.DirectStart

buttonState = 0

class Stencil():

    def createView(self):
        self.viewingSquare = render.attachNewNode(self.cm.generate())
        self.viewingSquare.reparentTo(base.camera)
        self.viewingSquare.setPos(0, 5, 0)

        self.viewingSquare.node().setAttrib(self.constantOneStencil)
        self.viewingSquare.node().setAttrib(ColorWriteAttrib.make(0))
        self.viewingSquare.setBin('background', 0)
        self.viewingSquare.setDepthWrite(0)
    def createStencil(self):
        self.constantOneStencil = StencilAttrib.make(1, StencilAttrib.SCFAlways,
                                                StencilAttrib.SOZero, StencilAttrib.SOReplace,
                                                StencilAttrib.SOReplace, 1, 0, 1)

        self.stencilReader = StencilAttrib.make(1, StencilAttrib.SCFEqual,
                                           StencilAttrib.SOKeep, StencilAttrib.SOKeep,
                                           StencilAttrib.SOKeep, 1, 1, 0)

        self.cm = CardMaker("cardmaker")
        self.cm.setFrame(-.5, .5, -.5, .5)
        self.createView()

stencil = Stencil()
stencil.createStencil()

def changestencil(t):
    stencil.cm.setFrame(-t,t,-t,t)
    stencil.createView()

if buttonState == 0:
    anim = LerpFunc(changestencil,fromData=0.5,toData=1,duration=1)
else:
    anim = LerpFunc(changestencil,toData=0.5,fromData=1,duration=1)

def buttonCommand():
    global buttonState
    if buttonState == 0:
        buttonState = 1
    else:
        buttonState = 0
    anim.start()

button = DirectButton(scale=6,command=buttonCommand)
button.node().setAttrib(stencil.stencilReader)

run()

As you can see, on the first click of the button when buttonState is 0, the animation works fine but on the second click when buttonState is 1, the StencilAttrib doesnā€™t update although all the functions are executed.
I know the code might be a bit messy, but what can I do to get the result I expect? and any suggestions for cleaner code would be much appreciated :slight_smile:

Looking at your code, when you create a new ā€œviewingSquareā€, I donā€™t see anything removing the previous ā€œviewingSquareā€ā€“could it simply be that your old squares are obstructing your new ones?

[edit]
Also, if Iā€™m reading this correctly, you only ever create ā€œanimā€ once, since the code that does it doesnā€™t appear to be in a function or method. Thus you presumably just restart the same anim, created with a ā€œbuttonStateā€ of 0, over and over.

If Iā€™m correct, I would suggest perhaps moving that code into the ā€œbuttonCommandā€ function.

[edit 2]
As to cleaner codeā€¦ I think that much of this comes down to what works for you.

For myself, I prefer to keep just about everything in a class. That seems to me to keep things much neater and clearer than having code all over the place.

However, if a more function-and-global-based approach works for you, then I say stick with it!

I changed the second click to make the stencil grow bigger instead of smaller to test your observation and you were right! The old stencil was blocking out the new. Only thing is now is how do I remove ā€˜self.viewingSquareā€™?

ps: sorry for the rookie questions, Iā€™m developing my first app so itā€™s a slow process

Simply call the ā€œremoveNodeā€ methodā€“something like this: ā€œself.viewingSquare.removeNode()ā€.

There are two caveats that occur to me:

  • First, donā€™t attempt to use a NodePath that youā€™ve removed; it will presumably no longer be valid.
    • However, in this case I imagine that youā€™ll be immediately creating a new NodePath and storing it in the variable that held the old NodePath, so this might not be a problem!
  • Second, if you havenā€™t yet created a variable called ā€œviewingSquareā€, you will presumably hit a Python error.
    • To prevent this, it might be worth creating the ā€œself.viewingSquareā€ variable at some point before your first call to ā€œcreateViewā€ (perhaps in the ā€œStencilā€ classā€™s constructor?). Since at this point you havenā€™t yet created the actual square, you might give it a value of ā€œNoneā€, and then check for that before attempting to remove the NodePath.

Donā€™t worry about it! It seems only reasonable to me that someone new to an activity might have questions regarding the basics. :slight_smile:

By the way, may I suggest my Panda tutorial? It should take you through the process of creating a very simple gameā€“with source codeā€“from a discussion of the basics of using Panda to building a distributable application. If youā€™re interested, you should find it here:

Yess that works perfectly, thank you once again :smiley:
Iā€™m defining self.viewingSquare straight after I remove it so that works fine, and also Iā€™m checking if it exists before trying to remove with

if hasattr(self,'viewingSquare'):

Oh wow Iā€™ll definitely go through that before continuing! Wish I had found that when I first started learning. It would be great if there was a link to that tutorial at the end of https://www.panda3d.org/manual/?title=Tutorial_End. I personally wouldā€™ve found it very helpful :smiley:

1 Like

Excellent, and my pleasure! :slight_smile:

Ah, that works! Well done on that solution. :slight_smile:

Iā€™m glad of it! I hope that it helps! :slight_smile:

That would be nice, perhaps, but I suppose that, since itā€™s a community tutorial (as Iā€™m not one of the Panda devs), itā€™s not an ā€œofficialā€ part of Panda, and hence not in the official manual.

Thatā€™s understandable, looks a great tutorial nonetheless.

1 Like

Iā€™ve realised thereā€™s a small delay when making the button smaller. How can I get rid off this?

from panda3d.core import *
from direct.gui.DirectGui import *
from direct.interval.LerpInterval import *

import direct.directbase.DirectStart

buttonState = 0

class Stencil():

    def createView(self):
        if hasattr(self,'viewingSquare'):
            self.viewingSquare.removeNode()
        self.viewingSquare = render.attachNewNode(self.cm.generate())
        self.viewingSquare.reparentTo(base.camera)
        self.viewingSquare.setPos(0, 5, 0)

        self.viewingSquare.node().setAttrib(self.constantOneStencil)
        self.viewingSquare.node().setAttrib(ColorWriteAttrib.make(0))
        self.viewingSquare.setBin('background', 0)
        self.viewingSquare.setDepthWrite(0)
    def createStencil(self):
        self.constantOneStencil = StencilAttrib.make(1, StencilAttrib.SCFAlways,
                                                StencilAttrib.SOZero, StencilAttrib.SOReplace,
                                                StencilAttrib.SOReplace, 1, 0, 1)

        self.stencilReader = StencilAttrib.make(1, StencilAttrib.SCFEqual,
                                           StencilAttrib.SOKeep, StencilAttrib.SOKeep,
                                           StencilAttrib.SOKeep, 1, 1, 0)

        self.cm = CardMaker("cardmaker")
        self.cm.setFrame(-.5, .5, -.5, .5)
        self.createView()

stencil = Stencil()
stencil.createStencil()

def changestencil(t):
    stencil.cm.setFrame(-t,t,-t,t)
    stencil.createView()


def buttonCommand():
    global buttonState
    if buttonState == 0:
        anim = LerpFunc(changestencil, fromData=0.5, toData=1, duration=1)
    else:
        anim = LerpFunc(changestencil,toData=0.5,fromData=1,duration=1)
    if buttonState == 0:
        buttonState = 1
    else:
        buttonState = 0
    anim.start()

button = DirectButton(scale=6,command=buttonCommand)
button.node().setAttrib(stencil.stencilReader)

run()