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()