Ejemplo n.º 1
0
 def drawNametag(self):
     self.setPriority(MarginGlobals.MP_low)
     if self.textNodePath is not None:
         self.textNodePath.removeNode()
         self.textNodePath = None
     if self.arrow is not None:
         self.arrow.removeNode()
         self.arrow = None
     if self.font is None:
         return
     if self.icon is not None:
         self.contents.attachNewNode(self.icon)
     if self.isClickable():
         foreground, background = self.nametagColor[self.clickState]
     else:
         foreground, background = self.nametagColor[PGButton.SInactive]
     self.textNode.setTextColor(foreground)
     self.textNodePath = self.contents.attachNewNode(self.textNode, 1)
     self.textNodePath.setTransparency(foreground[3] < 1)
     self.textNodePath.setAttrib(DepthWriteAttrib.make(0))
     self.textNodePath.setY(self.TEXT_Y_OFFSET)
     self.panel = NametagGlobals.cardModel.copyTo(self.contents, 0)
     self.panel.setColor(background)
     self.panel.setTransparency(background[3] < 1)
     x = (self.textNode.getLeft() + self.textNode.getRight()) / 2.0
     z = (self.textNode.getBottom() + self.textNode.getTop()) / 2.0
     self.panel.setPos(x, 0, z)
     self.panelWidth = self.textNode.getWidth() + self.PANEL_X_PADDING
     self.panelHeight = self.textNode.getHeight() + self.PANEL_Z_PADDING
     self.panel.setScale(self.panelWidth, 1, self.panelHeight)
     self.arrow = NametagGlobals.arrowModel.copyTo(self.contents)
     self.arrow.setZ(self.ARROW_OFFSET + self.textNode.getBottom())
     self.arrow.setScale(self.ARROW_SCALE)
     self.arrow.setColor(self.nametagColor[4])
Ejemplo n.º 2
0
    def prepareOffscreenBuffer(self):
        """ Creates an offscreen buffer for this target """

        self.debug("Preparing offscreen buffer")

        # Init buffer object
        self._createBuffer()

        # Prepare fullscreen quad
        self._quad = self._makeFullscreenQuad()

        # Prepare fullscreen camera
        bufferCam = self._makeFullscreenCam()
        initialState = NodePath("is")

        if not self._writeColor:
            initialState.setAttrib(ColorWriteAttrib.make(ColorWriteAttrib.COff), 1000)

        initialState.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MNone), 1000)

        bufferCam.setInitialState(initialState.getState())

        bufferCamNode = self._quad.attachNewNode(bufferCam)

        bufferRegion = self._buffer.getInternalBuffer().getDisplayRegion(0)
        bufferRegion.setCamera(bufferCamNode)
        bufferRegion.setActive(1)

        self._setSizeShaderInput()
Ejemplo n.º 3
0
    def _prepare_early_z(self, early_z=False):
        """ Prepares the earlyz stage """
        if early_z:
            self._prepass_cam = Camera(Globals.base.camNode)
            self._prepass_cam.set_tag_state_key("EarlyZShader")
            self._prepass_cam.set_name("EarlyZCamera")
            self._prepass_cam_node = Globals.base.camera.attach_new_node(
                self._prepass_cam)
            Globals.render.set_tag("EarlyZShader", "Default")
        else:
            self._prepass_cam = None

        # modify default camera initial state
        initial = Globals.base.camNode.get_initial_state()
        initial_node = NodePath("IntialState")
        initial_node.set_state(initial)

        if early_z:
            initial_node.set_attrib(
                DepthWriteAttrib.make(DepthWriteAttrib.M_off))
            initial_node.set_attrib(
                DepthTestAttrib.make(DepthTestAttrib.M_equal))
        else:
            initial_node.set_attrib(
                DepthTestAttrib.make(DepthTestAttrib.M_less_equal))

        Globals.base.camNode.set_initial_state(initial_node.get_state())
Ejemplo n.º 4
0
    def prepareOffscreenBuffer(self):
        """ Creates an offscreen buffer for this target """

        self.debug("Preparing offscreen buffer")

        # Init buffer object
        self._createBuffer()

        # Prepare fullscreen quad
        self._node = NodePath("RTRoot")
        self._quad = self._makeFullscreenQuad()
        self._quad.reparentTo(self._node)

        # Prepare fullscreen camera
        bufferCam = self._makeFullscreenCam()
        initialState = NodePath("is")

        if not self._writeColor:
            initialState.setAttrib(
                ColorWriteAttrib.make(ColorWriteAttrib.COff), 1000)

        initialState.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MNone),
                               1000)

        bufferCam.setInitialState(initialState.getState())

        self._camera = self._node.attachNewNode(bufferCam)

        bufferRegion = self._internalBuffer.getDisplayRegion(0)
        bufferRegion.setCamera(self._camera)
        bufferRegion.setActive(1)
        self._setSizeShaderInput()

        self._active = True
        self._registerBuffer()
Ejemplo n.º 5
0
    def drawNametag(self):
        # Set our priority in the margin system:
        self.setPriority(MarginGlobals.MP_low)

        if self.textNodePath is not None:
            self.textNodePath.removeNode()
            self.textNodePath = None

        if self.arrow is not None:
            self.arrow.removeNode()
            self.arrow = None

        if self.font is None:
            # We can't draw this without a font.
            return

        # Attach the icon:
        if self.icon is not None:
            self.contents.attachNewNode(self.icon)

        if self.isClickable():
            foreground, background = self.nametagColor[self.clickState]
        else:
            foreground, background = self.nametagColor[PGButton.SInactive]

        # Set the color of the TextNode:
        self.textNode.setTextColor(foreground)

        # Attach the TextNode:
        self.textNodePath = self.contents.attachNewNode(
            self.textNode.generate(), 1)
        self.textNodePath.setTransparency(foreground[3] < 1)
        self.textNodePath.setAttrib(DepthWriteAttrib.make(0))
        self.textNodePath.setY(self.TEXT_Y_OFFSET)

        # Attach a panel behind the TextNode:
        self.panel = NametagGlobals.cardModel.copyTo(self.contents, 0)
        self.panel.setColor(background)
        self.panel.setTransparency(background[3] < 1)

        # Reposition the panel:
        x = (self.textNode.getLeft() + self.textNode.getRight()) / 2.0
        z = (self.textNode.getBottom() + self.textNode.getTop()) / 2.0
        self.panel.setPos(x, 0, z)

        # Resize the panel:
        self.panelWidth = self.textNode.getWidth() + self.PANEL_X_PADDING
        self.panelHeight = self.textNode.getHeight() + self.PANEL_Z_PADDING
        self.panel.setScale(self.panelWidth, 1, self.panelHeight)

        #self.cTag.set_panel_size(self.panelWidth, self.panelHeight)

        # Add an arrow:
        self.arrow = NametagGlobals.arrowModel.copyTo(self.contents)
        self.arrow.setZ(self.ARROW_OFFSET + self.textNode.getBottom())
        self.arrow.setScale(self.ARROW_SCALE)
        self.arrow.setColor(
            NametagGlobals.NametagColors[NametagGlobals.CCOtherPlayer][0][0])
Ejemplo n.º 6
0
 def makeWindow(x, y, z, parentNode, color, scale, hpr, flip=False):
     node = node_r if not flip else node_l
     window = node.copyTo(parentNode)
     window.setColor(color)
     window.setScale(NodePath(), scale)
     window.setHpr(hpr)
     window.setPos(x, 0, z)
     window.setEffect(DecalEffect.make())
     window.setAttrib(DepthWriteAttrib.makeDefault(), 0)
Ejemplo n.º 7
0
def DoubleSidedNoZ():
    global _DoubleSidedNoZ
    if not _DoubleSidedNoZ:
        _DoubleSidedNoZ = RenderState.make(
            CullFaceAttrib.make(CullFaceAttrib.MCullNone),
            DepthTestAttrib.make(DepthTestAttrib.MOff),
            DepthWriteAttrib.make(DepthWriteAttrib.MOff),
            CullBinAttrib.make("fixed", LEGlobals.WidgetSort))
    return _DoubleSidedNoZ
Ejemplo n.º 8
0
 def select(self):
     self.state3D = self.state3D.setAttrib(ColorScaleAttrib.make(Vec4(1, 0.75, 0.75, 1)))
     self.state2D = self.state2D.setAttrib(ColorAttrib.makeFlat(Vec4(1, 0, 0, 1)))
     self.state2D = self.state2D.setAttrib(CullBinAttrib.make("fixed", LEGlobals.SelectedSort))
     self.state2D = self.state2D.setAttrib(DepthWriteAttrib.make(False))
     self.state2D = self.state2D.setAttrib(DepthTestAttrib.make(False))
     self.solid.setFaceGeomState(self.geom3D, self.state3D)
     self.solid.setFaceGeomState(self.geom2D, self.state2D)
     self.show3DLines()
     self.isSelected = True
    def drawNametag(self):
        # Set our priority in the margin system:
        self.setPriority(MarginGlobals.MP_low)

        if self.textNodePath is not None:
            self.textNodePath.removeNode()
            self.textNodePath = None

        if self.arrow is not None:
            self.arrow.removeNode()
            self.arrow = None

        if self.font is None:
            # We can't draw this without a font.
            return

        # Attach the icon:
        if self.icon is not None:
            self.contents.attachNewNode(self.icon)

        if self.isClickable():
            foreground, background = self.nametagColor[self.clickState]
        else:
            foreground, background = self.nametagColor[PGButton.SInactive]

        # Set the color of the TextNode:
        self.textNode.setTextColor(foreground)

        # Attach the TextNode:
        self.textNodePath = self.contents.attachNewNode(self.textNode, 1)
        self.textNodePath.setTransparency(foreground[3] < 1)
        self.textNodePath.setAttrib(DepthWriteAttrib.make(0))
        self.textNodePath.setY(self.TEXT_Y_OFFSET)

        # Attach a panel behind the TextNode:
        self.panel = NametagGlobals.cardModel.copyTo(self.contents, 0)
        self.panel.setColor(background)
        self.panel.setTransparency(background[3] < 1)

        # Reposition the panel:
        x = (self.textNode.getLeft()+self.textNode.getRight()) / 2.0
        z = (self.textNode.getBottom()+self.textNode.getTop()) / 2.0
        self.panel.setPos(x, 0, z)

        # Resize the panel:
        self.panelWidth = self.textNode.getWidth() + self.PANEL_X_PADDING
        self.panelHeight = self.textNode.getHeight() + self.PANEL_Z_PADDING
        self.panel.setScale(self.panelWidth, 1, self.panelHeight)

        # Add an arrow:
        self.arrow = NametagGlobals.arrowModel.copyTo(self.contents)
        self.arrow.setZ(self.ARROW_OFFSET + self.textNode.getBottom())
        self.arrow.setScale(self.ARROW_SCALE)
        self.arrow.setColor(self.nametagColor[0][0])
Ejemplo n.º 10
0
def DashedLineNoZ():
    global _DashedLineNoZ
    if not _DashedLineNoZ:
        shattr = getStippleShader()
        _DashedLineNoZ = RenderState.make(
            shattr,
            DepthTestAttrib.make(DepthTestAttrib.MOff),
            DepthWriteAttrib.make(DepthWriteAttrib.MOff),
            CullFaceAttrib.make(CullFaceAttrib.MCullNone),
        )
        _DashedLineNoZ = _DashedLineNoZ.setAttrib(CullBinAttrib.make("fixed", LEGlobals.BoxSort))
    return _DashedLineNoZ
Ejemplo n.º 11
0
 def generate(self,
              text,
              font,
              textColor=(0, 0, 0, 1),
              balloonColor=(1, 1, 1, 1),
              wordWrap=10.0,
              button=None):
     root = NodePath('balloon')
     balloon = self.model.copyTo(root)
     top = balloon.find('**/top')
     middle = balloon.find('**/middle')
     bottom = balloon.find('**/bottom')
     if top.isEmpty() or middle.isEmpty() or bottom.isEmpty():
         raise StandardError('invalid chat balloon model')
     balloon.setColor(balloonColor)
     if balloonColor[3] < 1.0:
         balloon.setTransparency(1)
     t = root.attachNewNode(TextNode('text'))
     t.node().setFont(font)
     t.node().setWordwrap(wordWrap)
     t.node().setText(text)
     t.node().setTextColor(textColor)
     width, height = t.node().getWidth(), t.node().getHeight()
     if height < self.MIN_HEIGHT:
         height = self.MIN_HEIGHT
     bubblePadding = self.BUBBLE_PADDING
     if width == self.MIN_WIDTH:
         bubblePadding /= 2
     else:
         bubblePadding *= 0.75
     t.setAttrib(DepthWriteAttrib.make(0))
     t.setPos(self.TEXT_SHIFT)
     t.setX(t, self.TEXT_SHIFT_PROP * width)
     t.setZ(t, height)
     if button:
         np = button.copyTo(root)
         np.setPos(t, width - bubblePadding, 0, -height + bubblePadding)
         np.setPos(np, self.BUTTON_SHIFT)
         np.setScale(self.BUTTON_SCALE)
         t.setZ(t, bubblePadding * 2)
     if width < self.MIN_WIDTH:
         width = self.MIN_WIDTH
         t.setX(t, width / 2)
         t.node().setAlign(TextNode.ACenter)
     width *= 1 + self.BUBBLE_PADDING_PROP
     width += bubblePadding
     balloon.setSx(width / self.NATIVE_WIDTH)
     if button:
         height += bubblePadding * 2
     middle.setSz(height)
     top.setZ(top, height - 1)
     return root
    def registerEarlyZTagState(self, name, state):
        """ Registers a new tag state """
        if not self.prepassCam:
            return

        # state.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullClockwise), 10000)
        state.setAttrib(ColorWriteAttrib.make(ColorWriteAttrib.COff), 10000)
        state.setAttrib(AlphaTestAttrib.make(AlphaTestAttrib.MNone, 1.0), 10000)
        state.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MOn), 10000)
        state.setAttrib(DepthTestAttrib.make(DepthTestAttrib.MLess), 10000)
        state.setAttrib(TransparencyAttrib.make(TransparencyAttrib.MNone), 10000)

        self.prepassCam.setTagState(name, state.getState()) 
Ejemplo n.º 13
0
    def create(self):

        earlyZ = self.pipeline.settings.enableEarlyZ

        if earlyZ:
            self.prepassCam = Camera(Globals.base.camNode)
            self.prepassCam.setTagStateKey("EarlyZShader")
            self.prepassCam.setName("EarlyZCamera")
            self.prepassCamNode = Globals.base.camera.attachNewNode(
                self.prepassCam)
            Globals.render.setTag("EarlyZShader", "Default")
        else:
            self.prepassCam = None

        # modify default camera initial state
        initial = Globals.base.camNode.getInitialState()
        initialNode = NodePath("IntiialState")
        initialNode.setState(initial)

        if earlyZ:
            initialNode.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MOff))
            initialNode.setAttrib(DepthTestAttrib.make(DepthTestAttrib.MEqual))
            pass
        else:
            initialNode.setAttrib(
                DepthTestAttrib.make(DepthTestAttrib.MLessEqual))

        # initialNode.setAttrib(ColorWriteAttrib.make(ColorWriteAttrib.COff), 10000)
        # initialNode.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullClockwise), 10000)
        Globals.base.camNode.setInitialState(initialNode.getState())

        self.target = RenderTarget("DeferredScenePass")
        self.target.addColorAndDepth()
        self.target.addAuxTextures(3)
        self.target.setAuxBits(16)
        self.target.setDepthBits(32)
        self.target.setCreateOverlayQuad(False)

        if earlyZ:
            self.target.prepareSceneRender(earlyZ=True,
                                           earlyZCam=self.prepassCamNode)
        else:
            self.target.prepareSceneRender()

        self.target.setClearColor(True, color=Vec4(1, 1, 0, 1))

        if earlyZ:
            self.target.setClearDepth(False)
        else:
            self.target.setClearDepth(True)
Ejemplo n.º 14
0
    def registerEarlyZTagState(self, name, state):
        """ Registers a new tag state """
        if not self.prepassCam:
            return

        # state.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullClockwise), 10000)
        state.setAttrib(ColorWriteAttrib.make(ColorWriteAttrib.COff), 10000)
        state.setAttrib(AlphaTestAttrib.make(AlphaTestAttrib.MNone, 1.0),
                        10000)
        state.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MOn), 10000)
        state.setAttrib(DepthTestAttrib.make(DepthTestAttrib.MLess), 10000)
        state.setAttrib(TransparencyAttrib.make(TransparencyAttrib.MNone),
                        10000)

        self.prepassCam.setTagState(name, state.getState())
    def create(self):

        earlyZ = self.pipeline.settings.enableEarlyZ

        if earlyZ:
            self.prepassCam = Camera(Globals.base.camNode)
            self.prepassCam.setTagStateKey("EarlyZShader")
            self.prepassCam.setName("EarlyZCamera")
            self.prepassCamNode = Globals.base.camera.attachNewNode(self.prepassCam)
            Globals.render.setTag("EarlyZShader", "Default")
        else:
            self.prepassCam = None

        # modify default camera initial state
        initial = Globals.base.camNode.getInitialState()
        initialNode = NodePath("IntiialState")
        initialNode.setState(initial)
        
        if earlyZ:
            initialNode.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MOff))
            initialNode.setAttrib(DepthTestAttrib.make(DepthTestAttrib.MEqual))
            pass
        else:
            initialNode.setAttrib(DepthTestAttrib.make(DepthTestAttrib.MLessEqual))

        # initialNode.setAttrib(ColorWriteAttrib.make(ColorWriteAttrib.COff), 10000)
        # initialNode.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullClockwise), 10000)
        Globals.base.camNode.setInitialState(initialNode.getState())

        self.target = RenderTarget("DeferredScenePass")
        self.target.addColorAndDepth()
        self.target.addAuxTextures(3)
        self.target.setAuxBits(16)
        self.target.setDepthBits(32)
        self.target.setCreateOverlayQuad(False)
        
        if earlyZ:
            self.target.prepareSceneRender(earlyZ=True, earlyZCam=self.prepassCamNode)
        else:
            self.target.prepareSceneRender()

        self.target.setClearColor(True, color=Vec4(1, 1, 0, 1))

        if earlyZ:
            self.target.setClearDepth(False)
        else:
            self.target.setClearDepth(True)
Ejemplo n.º 16
0
    def drawNametag(self):
        if self.font is None:
            # We can't draw this without a font.
            return

        # Attach the icon:
        if self.icon is not None:
            self.contents.attachNewNode(self.icon)

        if self.isClickable():
            foreground, background = self.nametagColor[self.clickState]
        else:
            foreground, background = self.nametagColor[PGButton.SInactive]

        # Set the color of the TextNode:
        self.textNode.setTextColor(foreground)

        # Attach the TextNode:
        textNodePath = self.contents.attachNewNode(self.textNode.generate(), 1)
        textNodePath.setTransparency(foreground[3] < 1)
        textNodePath.setAttrib(DepthWriteAttrib.make(0))
        textNodePath.setY(self.TEXT_Y_OFFSET)

        # Attach a panel behind the TextNode:
        self.panel = NametagGlobals.cardModel.copyTo(self.contents, 0)
        self.panel.setColor(background)
        self.panel.setTransparency(background[3] < 1)

        # Reposition the panel:
        x = (self.textNode.getLeft()+self.textNode.getRight()) / 2.0
        z = (self.textNode.getBottom()+self.textNode.getTop()) / 2.0
        self.panel.setPos(x, 0, z)

        # Resize the panel:
        self.panelWidth = self.textNode.getWidth() + self.PANEL_X_PADDING
        self.panelHeight = self.textNode.getHeight() + self.PANEL_Z_PADDING
        self.panel.setScale(self.panelWidth, 1, self.panelHeight)
        
        self.cTag.set_panel_size(self.panelWidth, self.panelHeight)
        
        if not base.config.GetBool('want-nametags', True):
            self.contents.hide()
Ejemplo n.º 17
0
 def drawNametag(self):
     if self.font is None:
         return
     if self.icon is not None:
         self.contents.attachNewNode(self.icon)
     if self.isClickable():
         foreground, background = self.nametagColor[self.clickState]
     else:
         foreground, background = self.nametagColor[PGButton.SInactive]
     self.textNode.setTextColor(foreground)
     textNodePath = self.contents.attachNewNode(self.textNode, 1)
     textNodePath.setTransparency(foreground[3] < 1)
     textNodePath.setAttrib(DepthWriteAttrib.make(0))
     textNodePath.setY(-0.1)
     self.panel = NametagGlobals.cardModel.copyTo(self.contents, 0)
     self.panel.setColor(background)
     self.panel.setTransparency(background[3] < 1)
     x = (self.textNode.getLeft() + self.textNode.getRight()) / 2.0
     z = (self.textNode.getBottom() + self.textNode.getTop()) / 2.0
     self.panel.setPos(x, 0, z)
     self.panelWidth = self.textNode.getWidth() + self.PANEL_X_PADDING
     self.panelHeight = self.textNode.getHeight() + self.PANEL_Z_PADDING
     self.panel.setScale(self.panelWidth, 1, self.panelHeight)
Ejemplo n.º 18
0
 def __init__(self,
              model,
              modelWidth,
              modelHeight,
              textNode,
              foreground=VBase4(0, 0, 0, 1),
              background=VBase4(1, 1, 1, 1),
              reversed=False,
              button=None):
     NodePath.__init__(self, 'chatBalloon')
     self.model = model
     self.modelWidth = modelWidth
     self.modelHeight = modelHeight
     self.textNode = textNode
     self.foreground = foreground
     self.background = background
     self.button = button
     self.textNode.setTextColor(self.foreground)
     self.balloon = self.model.copyTo(self)
     self.balloon.setColor(self.background)
     self.balloon.setTransparency(self.background[3] < 1)
     self.textNodePath = self.attachNewNode(self.textNode)
     self.textNodePath.setTransparency(self.foreground[3] < 1)
     self.textNodePath.setAttrib(DepthWriteAttrib.make(0))
     middle = self.balloon.find('**/middle')
     top = self.balloon.find('**/top')
     self.textWidth = self.textNode.getWidth()
     if self.textWidth < self.TEXT_MIN_WIDTH:
         self.textWidth = self.TEXT_MIN_WIDTH
     paddedWidth = self.textWidth + self.BALLOON_X_PADDING * 2
     self.balloon.setSx(paddedWidth / modelWidth)
     self.textHeight = textNode.getHeight()
     if self.textHeight < self.TEXT_MIN_HEIGHT:
         self.textHeight = self.TEXT_MIN_HEIGHT
     paddedHeight = self.textHeight + self.BALLOON_Z_PADDING * 2
     middle.setSz(paddedHeight - 1.5)
     top.setZ(middle, 1)
     if reversed:
         self.balloon.setSx(-self.balloon.getSx())
         self.balloon.setTwoSided(True)
     self.width = paddedWidth
     self.height = paddedHeight
     self.center = self.balloon.getBounds().getCenter()
     self.textNodePath.setPos(self.center)
     self.textNodePath.setY(self.TEXT_Y_OFFSET)
     self.textNodePath.setX(self.textNodePath, -(self.textWidth / 2))
     if self.textWidth == self.TEXT_MIN_WIDTH:
         centerX = (self.TEXT_MIN_WIDTH - self.textNode.getWidth()) / 2.0
         self.textNodePath.setX(self.textNodePath, centerX)
     self.textNodePath.setZ(top,
                            -self.BALLOON_Z_PADDING + self.TEXT_Z_OFFSET)
     if self.textHeight == self.TEXT_MIN_HEIGHT:
         centerZ = (ChatBalloon.TEXT_MIN_HEIGHT -
                    self.textNode.getHeight()) / 2.0
         self.textNodePath.setZ(self.textNodePath, -centerZ)
     self.textNodePath.setX(self.textNodePath, self.TEXT_X_OFFSET)
     if self.button is not None:
         self.buttonNodePath = button.copyTo(self)
         self.buttonNodePath.setPos(self.textNodePath, self.textWidth, 0,
                                    -self.textHeight)
         self.buttonNodePath.setPos(self.buttonNodePath,
                                    ChatBalloon.BUTTON_SHIFT)
         self.buttonNodePath.setScale(ChatBalloon.BUTTON_SCALE)
     else:
         self.buttonNodePath = None
     self.setAntialias(AntialiasAttrib.MMultisample)
     return
Ejemplo n.º 19
0
    def __init__(self,
                 model,
                 modelWidth,
                 modelHeight,
                 textNode,
                 foreground=VBase4(0, 0, 0, 1),
                 background=VBase4(1, 1, 1, 1),
                 reversed=False,
                 button=None,
                 is2d=False):
        NodePath.__init__(self, 'chatBalloon')
        # We don't want chat bubbles to glow from the bloom filter, it looks terrible.
        #CIGlobals.applyNoGlow(self)

        #self.setLightOff(1)
        self.hide(CIGlobals.ShadowCameraBitmask
                  | CIGlobals.ReflectionCameraBitmask)
        # Disable bloom on the chat bubbles so you can actually read the text
        self.setAttrib(BloomAttrib.make(False))
        #self.setColorScale(0.8, 0.8, 0.8, 1.0, 1)

        self.model = model
        self.modelWidth = modelWidth
        self.modelHeight = modelHeight
        self.textNode = textNode
        self.foreground = foreground
        self.background = background
        self.button = button
        self.is2d = is2d

        # Set the TextNode color:
        self.textNode.setTextColor(self.foreground)

        # Create a balloon:
        self.balloon = self.model.copyTo(self)
        self.balloon.setColor(self.background)
        self.balloon.setTransparency(self.background[3] < 1)
        # self.balloon.setLightOff(1)
        # if is2d:
        # Don't use the black outline on 2d chat balloons (old toontown).
        #self.balloon.setTextureOff(1)
        #self.balloon.setShaderOff(1)
        #else:
        #self.balloon.setShaderAuto(2)

        # Attach the TextNode:
        self.textNodePath = self.attachNewNode(self.textNode.generate())
        self.textNodePath.setTransparency(self.foreground[3] < 1)
        self.textNodePath.setAttrib(DepthWriteAttrib.make(0))

        # Resize the balloon as necessary:
        middle = self.balloon.find('**/middle')
        top = self.balloon.find('**/top')
        self.textWidth = self.textNode.getWidth()
        if self.textWidth < self.TEXT_MIN_WIDTH:
            self.textWidth = self.TEXT_MIN_WIDTH
        paddedWidth = self.textWidth + (self.BALLOON_X_PADDING * 2)
        self.balloon.setSx(paddedWidth / modelWidth)
        self.textHeight = textNode.getHeight()
        if self.textHeight < self.TEXT_MIN_HEIGHT:
            self.textHeight = self.TEXT_MIN_HEIGHT
        paddedHeight = self.textHeight + (self.BALLOON_Z_PADDING * 2)
        middle.setSz(paddedHeight - 1.5)  # Compensate for the top, as well.
        top.setZ(middle, 1)

        if reversed:
            self.balloon.setSx(-self.balloon.getSx())
            self.balloon.setTwoSided(
                True)  # Render the backface of the balloon.

        self.width = paddedWidth
        self.height = paddedHeight

        # Position the TextNode:
        self.center = self.balloon.getBounds().getCenter()
        self.textNodePath.setPos(self.center)
        self.textNodePath.setY(self.TEXT_Y_OFFSET)
        self.textNodePath.setX(self.textNodePath, -(self.textWidth / 2))
        if self.textWidth == self.TEXT_MIN_WIDTH:
            centerX = (self.TEXT_MIN_WIDTH - self.textNode.getWidth()) / 2.0
            self.textNodePath.setX(self.textNodePath, centerX)
        self.textNodePath.setZ(top,
                               -self.BALLOON_Z_PADDING + self.TEXT_Z_OFFSET)
        if self.textHeight == self.TEXT_MIN_HEIGHT:
            centerZ = (ChatBalloon.TEXT_MIN_HEIGHT -
                       self.textNode.getHeight()) / 2.0
            self.textNodePath.setZ(self.textNodePath, -centerZ)
        self.textNodePath.setX(self.textNodePath, self.TEXT_X_OFFSET)

        # Add a button if one is given:
        if self.button is not None:
            self.buttonNodePath = button.copyTo(self)
            self.buttonNodePath.setPos(self.textNodePath, self.textWidth, 0,
                                       -self.textHeight)
            self.buttonNodePath.setPos(self.buttonNodePath,
                                       ChatBalloon.BUTTON_SHIFT)
            self.buttonNodePath.setScale(ChatBalloon.BUTTON_SCALE)
        else:
            self.buttonNodePath = None

        self.balloon.clearModelNodes()
        self.balloon.flattenStrong()
Ejemplo n.º 20
0
    def apply(self, nodePath):
        if self.is_unset():
            return

        attrib = DepthWriteAttrib.make(self.is_on())
        nodePath.setAttrib(attrib)
Ejemplo n.º 21
0
    def __init__(self):
        # Initialize the ShowBase class from which we inherit, which will
        # create a window and set up everything we need for rendering into it.
        ShowBase.__init__(self)
        self.setBackgroundColor((0, 0, 0, 0))

        # Preliminary capabilities check.

        if not self.win.getGsg().getSupportsBasicShaders():
            self.t = addTitle("Firefly Demo: Video driver reports that Cg "
                              "shaders are not supported.")
            return
        if not self.win.getGsg().getSupportsDepthTexture():
            self.t = addTitle("Firefly Demo: Video driver reports that depth "
                              "textures are not supported.")
            return

        # This algorithm uses two offscreen buffers, one of which has
        # an auxiliary bitplane, and the offscreen buffers share a single
        # depth buffer.  This is a heck of a complicated buffer setup.

        self.modelbuffer = self.makeFBO("model buffer", 1)
        self.lightbuffer = self.makeFBO("light buffer", 0)

        # Creation of a high-powered buffer can fail, if the graphics card
        # doesn't support the necessary OpenGL extensions.

        if self.modelbuffer is None or self.lightbuffer is None:
            self.t = addTitle("Toon Shader: Video driver does not support "
                              "multiple render targets")
            return

        # Create four render textures: depth, normal, albedo, and final.
        # attach them to the various bitplanes of the offscreen buffers.

        self.texDepth = Texture()
        self.texDepth.setFormat(Texture.FDepthStencil)
        self.texAlbedo = Texture()
        self.texNormal = Texture()
        self.texFinal = Texture()

        self.modelbuffer.addRenderTexture(self.texDepth,
            GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPDepthStencil)
        self.modelbuffer.addRenderTexture(self.texAlbedo,
            GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor)
        self.modelbuffer.addRenderTexture(self.texNormal,
            GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPAuxRgba0)

        self.lightbuffer.addRenderTexture(self.texFinal,
            GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor)

        # Set the near and far clipping planes.

        self.cam.node().getLens().setNear(50.0)
        self.cam.node().getLens().setFar(500.0)
        lens = self.cam.node().getLens()

        # This algorithm uses three cameras: one to render the models into the
        # model buffer, one to render the lights into the light buffer, and
        # one to render "plain" stuff (non-deferred shaded) stuff into the
        # light buffer.  Each camera has a bitmask to identify it.

        self.modelMask = 1
        self.lightMask = 2
        self.plainMask = 4

        self.modelcam = self.makeCamera(self.modelbuffer,
            lens=lens, scene=render, mask=self.modelMask)
        self.lightcam = self.makeCamera(self.lightbuffer,
            lens=lens, scene=render, mask=self.lightMask)
        self.plaincam = self.makeCamera(self.lightbuffer,
            lens=lens, scene=render, mask=self.plainMask)

        # Panda's main camera is not used.

        self.cam.node().setActive(0)

        # Take explicit control over the order in which the three
        # buffers are rendered.

        self.modelbuffer.setSort(1)
        self.lightbuffer.setSort(2)
        self.win.setSort(3)

        # Within the light buffer, control the order of the two cams.

        self.lightcam.node().getDisplayRegion(0).setSort(1)
        self.plaincam.node().getDisplayRegion(0).setSort(2)

        # By default, panda usually clears the screen before every
        # camera and before every window.  Tell it not to do that.
        # Then, tell it specifically when to clear and what to clear.

        self.modelcam.node().getDisplayRegion(0).disableClears()
        self.lightcam.node().getDisplayRegion(0).disableClears()
        self.plaincam.node().getDisplayRegion(0).disableClears()
        self.cam.node().getDisplayRegion(0).disableClears()
        self.cam2d.node().getDisplayRegion(0).disableClears()
        self.modelbuffer.disableClears()
        self.win.disableClears()

        self.modelbuffer.setClearColorActive(1)
        self.modelbuffer.setClearDepthActive(1)
        self.lightbuffer.setClearColorActive(1)
        self.lightbuffer.setClearColor((0, 0, 0, 1))

        # Miscellaneous stuff.

        self.disableMouse()
        self.camera.setPos(-9.112, -211.077, 46.951)
        self.camera.setHpr(0, -7.5, 2.4)
        random.seed()

        # Calculate the projection parameters for the final shader.
        # The math here is too complex to explain in an inline comment,
        # I've put in a full explanation into the HTML intro.

        proj = self.cam.node().getLens().getProjectionMat()
        proj_x = 0.5 * proj.getCell(3, 2) / proj.getCell(0, 0)
        proj_y = 0.5 * proj.getCell(3, 2)
        proj_z = 0.5 * proj.getCell(3, 2) / proj.getCell(2, 1)
        proj_w = -0.5 - 0.5 * proj.getCell(1, 2)

        # Configure the render state of the model camera.

        tempnode = NodePath(PandaNode("temp node"))
        tempnode.setAttrib(
            AlphaTestAttrib.make(RenderAttrib.MGreaterEqual, 0.5))
        tempnode.setShader(loader.loadShader("model.sha"))
        tempnode.setAttrib(DepthTestAttrib.make(RenderAttrib.MLessEqual))
        self.modelcam.node().setInitialState(tempnode.getState())

        # Configure the render state of the light camera.

        tempnode = NodePath(PandaNode("temp node"))
        tempnode.setShader(loader.loadShader("light.sha"))
        tempnode.setShaderInput("texnormal", self.texNormal)
        tempnode.setShaderInput("texalbedo", self.texAlbedo)
        tempnode.setShaderInput("texdepth", self.texDepth)
        tempnode.setShaderInput("proj", (proj_x, proj_y, proj_z, proj_w))
        tempnode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd,
            ColorBlendAttrib.OOne, ColorBlendAttrib.OOne))
        tempnode.setAttrib(
            CullFaceAttrib.make(CullFaceAttrib.MCullCounterClockwise))
        # The next line causes problems on Linux.
        # tempnode.setAttrib(DepthTestAttrib.make(RenderAttrib.MGreaterEqual))
        tempnode.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MOff))
        self.lightcam.node().setInitialState(tempnode.getState())

        # Configure the render state of the plain camera.

        rs = RenderState.makeEmpty()
        self.plaincam.node().setInitialState(rs)

        # Clear any render attribs on the root node. This is necessary
        # because by default, panda assigns some attribs to the root
        # node.  These default attribs will override the
        # carefully-configured render attribs that we just attached
        # to the cameras.  The simplest solution is to just clear
        # them all out.

        render.setState(RenderState.makeEmpty())

        # My artist created a model in which some of the polygons
        # don't have textures.  This confuses the shader I wrote.
        # This little hack guarantees that everything has a texture.

        white = loader.loadTexture("models/white.jpg")
        render.setTexture(white, 0)

        # Create two subroots, to help speed cull traversal.

        self.lightroot = NodePath(PandaNode("lightroot"))
        self.lightroot.reparentTo(render)
        self.modelroot = NodePath(PandaNode("modelroot"))
        self.modelroot.reparentTo(render)
        self.lightroot.hide(BitMask32(self.modelMask))
        self.modelroot.hide(BitMask32(self.lightMask))
        self.modelroot.hide(BitMask32(self.plainMask))

        # Load the model of a forest.  Make it visible to the model camera.
        # This is a big model, so we load it asynchronously while showing a
        # load text.  We do this by passing in a callback function.
        self.loading = addTitle("Loading models...")

        self.forest = NodePath(PandaNode("Forest Root"))
        self.forest.reparentTo(render)
        self.forest.hide(BitMask32(self.lightMask | self.plainMask))
        loader.loadModel([
            "models/background",
            "models/foliage01",
            "models/foliage02",
            "models/foliage03",
            "models/foliage04",
            "models/foliage05",
            "models/foliage06",
            "models/foliage07",
            "models/foliage08",
            "models/foliage09"],
            callback=self.finishLoading)

        # Cause the final results to be rendered into the main window on a
        # card.

        self.card = self.lightbuffer.getTextureCard()
        self.card.setTexture(self.texFinal)
        self.card.reparentTo(render2d)

        # Panda contains a built-in viewer that lets you view the results of
        # your render-to-texture operations.  This code configures the viewer.

        self.bufferViewer.setPosition("llcorner")
        self.bufferViewer.setCardSize(0, 0.40)
        self.bufferViewer.setLayout("vline")
        self.toggleCards()
        self.toggleCards()

        # Firefly parameters

        self.fireflies = []
        self.sequences = []
        self.scaleseqs = []
        self.glowspheres = []
        self.fireflysize = 1.0
        self.spheremodel = loader.loadModel("misc/sphere")

        # Create the firefly model, a fuzzy dot
        dotSize = 1.0
        cm = CardMaker("firefly")
        cm.setFrame(-dotSize, dotSize, -dotSize, dotSize)
        self.firefly = NodePath(cm.generate())
        self.firefly.setTexture(loader.loadTexture("models/firefly.png"))
        self.firefly.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.M_add,
            ColorBlendAttrib.O_incoming_alpha, ColorBlendAttrib.O_one))

        # these allow you to change parameters in realtime

        self.accept("escape", sys.exit, [0])
        self.accept("arrow_up",   self.incFireflyCount, [1.1111111])
        self.accept("arrow_down", self.decFireflyCount, [0.9000000])
        self.accept("arrow_right", self.setFireflySize, [1.1111111])
        self.accept("arrow_left",  self.setFireflySize, [0.9000000])
        self.accept("v", self.toggleCards)
        self.accept("V", self.toggleCards)
Ejemplo n.º 22
0
    def __init__(self):
        DebugObject.__init__(self, "LightManager")

        self._initArrays()

        # create arrays to store lights & shadow sources
        self.lights = []
        self.shadowSources = []
        self.allLightsArray = ShaderStructArray(Light, 30)

        self.cullBounds = None
        self.shadowScene = render

        ## SHADOW ATLAS ##
        # todo: move to separate class

        # When you change this, change also SHADOW_MAP_ATLAS_SIZE in configuration.include,
        # and reduce the default shadow map resolution of point lights
        self.shadowAtlasSize = 512
        self.maxShadowMaps = 24

        # When you change it , change also SHAODOW_GEOMETRY_MAX_VERTICES and
        # SHADOW_MAX_UPDATES_PER_FRAME in configuration.include!
        self.maxShadowUpdatesPerFrame = 2
        self.tileSize = 128
        self.tileCount = self.shadowAtlasSize / self.tileSize
        self.tiles = []

        self.updateShadowsArray = ShaderStructArray(
            ShadowSource, self.maxShadowUpdatesPerFrame)
        self.allShadowsArray = ShaderStructArray(ShadowSource,
                                                 self.maxShadowMaps)

        # self.shadowAtlasTex = Texture("ShadowAtlas")
        # self.shadowAtlasTex.setup2dTexture(
        #     self.shadowAtlasSize, self.shadowAtlasSize, Texture.TFloat, Texture.FRg16)
        # self.shadowAtlasTex.setMinfilter(Texture.FTLinear)
        # self.shadowAtlasTex.setMagfilter(Texture.FTLinear)

        self.debug("Init shadow atlas with tileSize =", self.tileSize,
                   ", tileCount =", self.tileCount)

        for i in xrange(self.tileCount):
            self.tiles.append([None for j in xrange(self.tileCount)])

        # create shadow compute buffer
        self.shadowComputeCamera = Camera("ShadowComputeCamera")
        self.shadowComputeCameraNode = self.shadowScene.attachNewNode(
            self.shadowComputeCamera)
        self.shadowComputeCamera.getLens().setFov(90, 90)
        self.shadowComputeCamera.getLens().setNearFar(10.0, 100000.0)

        self.shadowComputeCameraNode.setPos(0, 0, 150)
        self.shadowComputeCameraNode.lookAt(0, 0, 0)

        self.shadowComputeTarget = RenderTarget("ShadowCompute")
        self.shadowComputeTarget.setSize(self.shadowAtlasSize,
                                         self.shadowAtlasSize)
        # self.shadowComputeTarget.setLayers(self.maxShadowUpdatesPerFrame)
        self.shadowComputeTarget.addRenderTexture(RenderTargetType.Depth)
        self.shadowComputeTarget.setDepthBits(32)
        self.shadowComputeTarget.setSource(self.shadowComputeCameraNode,
                                           base.win)
        self.shadowComputeTarget.prepareSceneRender()

        self.shadowComputeTarget.getInternalRegion().setSort(3)
        self.shadowComputeTarget.getRegion().setSort(3)

        self.shadowComputeTarget.getInternalRegion().setNumRegions(
            self.maxShadowUpdatesPerFrame + 1)
        self.shadowComputeTarget.getInternalRegion().setDimensions(
            0, (0, 1, 0, 1))
        self.shadowComputeTarget.setClearDepth(False)

        self.depthClearer = []

        for i in xrange(self.maxShadowUpdatesPerFrame):
            buff = self.shadowComputeTarget.getInternalBuffer()
            dr = buff.makeDisplayRegion()
            dr.setSort(2)
            dr.setClearDepthActive(True)
            dr.setClearDepth(1.0)
            dr.setClearColorActive(False)
            dr.setDimensions(0, 0, 0, 0)
            self.depthClearer.append(dr)

        self.queuedShadowUpdates = []

        # Assign copy shader
        self._setCopyShader()
        # self.shadowComputeTarget.setShaderInput("atlas", self.shadowComputeTarget.getColorTexture())
        # self.shadowComputeTarget.setShaderInput(
        #     "renderResult", self.shadowComputeTarget.getDepthTexture())

        # self.shadowComputeTarget.setActive(False)

        # Create shadow caster shader
        self.shadowCasterShader = BetterShader.load(
            "Shader/DefaultShadowCaster.vertex",
            "Shader/DefaultShadowCaster.fragment",
            "Shader/DefaultShadowCaster.geometry")

        self.shadowComputeCamera.setTagStateKey("ShadowPass")
        initialState = NodePath("ShadowCasterState")
        initialState.setShader(self.shadowCasterShader, 30)
        self.shadowComputeCamera.setInitialState(
            RenderState.make(ColorWriteAttrib.make(ColorWriteAttrib.C_off),
                             DepthWriteAttrib.make(DepthWriteAttrib.M_on),
                             100))

        self.shadowComputeCamera.setTagState("True", initialState.getState())
        self.shadowScene.setTag("ShadowPass", "True")

        self._createDebugTexts()

        self.updateShadowsArray.bindTo(self.shadowScene, "updateSources")
        self.updateShadowsArray.bindTo(self.shadowComputeTarget,
                                       "updateSources")

        self.numShadowUpdatesPTA = PTAInt.emptyArray(1)

        # Set initial inputs
        for target in [self.shadowComputeTarget, self.shadowScene]:
            target.setShaderInput("numUpdates", self.numShadowUpdatesPTA)

        self.lightingComputator = None
        self.lightCuller = None
Ejemplo n.º 23
0
    def __init__(self, pipeline):
        """ Creates a new LightManager. It expects a RenderPipeline as parameter. """
        DebugObject.__init__(self, "LightManager")

        self._initArrays()

        self.pipeline = pipeline
        self.settings = pipeline.getSettings()

        # Create arrays to store lights & shadow sources
        self.lights = []
        self.shadowSources = []
        self.queuedShadowUpdates = []
        self.allLightsArray = ShaderStructArray(Light, self.maxTotalLights)

        self.cullBounds = None
        self.shadowScene = Globals.render

        # Create atlas
        self.shadowAtlas = ShadowAtlas()
        self.shadowAtlas.setSize(self.settings.shadowAtlasSize)
        self.shadowAtlas.create()

        self.maxShadowMaps = 24
        self.maxShadowUpdatesPerFrame = self.settings.maxShadowUpdatesPerFrame
        self.numShadowUpdatesPTA = PTAInt.emptyArray(1)

        self.updateShadowsArray = ShaderStructArray(
            ShadowSource, self.maxShadowUpdatesPerFrame)
        self.allShadowsArray = ShaderStructArray(
            ShadowSource, self.maxShadowMaps)

        # Create shadow compute buffer
        self._createShadowComputationBuffer()

        # Create the initial shadow state
        self.shadowComputeCamera.setTagStateKey("ShadowPassShader")
        self.shadowComputeCamera.setInitialState(RenderState.make(
            ColorWriteAttrib.make(ColorWriteAttrib.C_off),
            DepthWriteAttrib.make(DepthWriteAttrib.M_on),
            # CullFaceAttrib.make(CullFaceAttrib.MCullNone),
            100))

        self._createTagStates()

        self.shadowScene.setTag("ShadowPassShader", "Default")

        # Create debug overlay
        self._createDebugTexts()

        # Disable buffer on start
        self.shadowComputeTarget.setActive(False)

        # Bind arrays
        self.updateShadowsArray.bindTo(self.shadowScene, "updateSources")
        self.updateShadowsArray.bindTo(
            self.shadowComputeTarget, "updateSources")

        # Set initial inputs
        for target in [self.shadowComputeTarget, self.shadowScene]:
            target.setShaderInput("numUpdates", self.numShadowUpdatesPTA)

        self.lightingComputator = None
        self.lightCuller = None
Ejemplo n.º 24
0
    def __init__(self,
                 model,
                 modelWidth,
                 modelHeight,
                 textNode,
                 foreground=VBase4(0, 0, 0, 1),
                 background=VBase4(1, 1, 1, 1),
                 reversed=False,
                 button=None):
        NodePath.__init__(self, 'chatBalloon')

        self.model = model
        self.modelWidth = modelWidth
        self.modelHeight = modelHeight
        self.textNode = textNode
        self.foreground = foreground
        self.background = background
        self.button = button

        # Set the TextNode color:
        self.textNode.setTextColor(self.foreground)

        # Create a balloon:
        self.balloon = self.model.copyTo(self)
        self.balloon.setColor(self.background)
        self.balloon.setTransparency(self.background[3] < 1)

        # Attach the TextNode:
        self.textNodePath = self.attachNewNode(self.textNode)
        self.textNodePath.setTransparency(self.foreground[3] < 1)
        self.textNodePath.setAttrib(DepthWriteAttrib.make(0))

        # Resize the balloon as necessary:
        middle = self.balloon.find('**/middle')
        top = self.balloon.find('**/top')
        self.textWidth = self.textNode.getWidth()
        if self.textWidth < self.TEXT_MIN_WIDTH:
            self.textWidth = self.TEXT_MIN_WIDTH
        paddedWidth = self.textWidth + (self.BALLOON_X_PADDING * 2)
        self.balloon.setSx(paddedWidth / modelWidth)
        self.textHeight = textNode.getHeight()
        if self.textHeight < self.TEXT_MIN_HEIGHT:
            self.textHeight = self.TEXT_MIN_HEIGHT
        paddedHeight = self.textHeight + (self.BALLOON_Z_PADDING * 2)
        middle.setSz(paddedHeight - 1.5)  # Compensate for the top, as well.
        top.setZ(middle, 1)

        if reversed:
            self.balloon.setSx(-self.balloon.getSx())
            self.balloon.setTwoSided(
                True)  # Render the backface of the balloon.

        self.width = paddedWidth
        self.height = paddedHeight

        # Position the TextNode:
        self.center = self.balloon.getBounds().getCenter()
        self.textNodePath.setPos(self.center)
        self.textNodePath.setY(self.TEXT_Y_OFFSET)
        self.textNodePath.setX(self.textNodePath, -(self.textWidth / 2))
        if self.textWidth == self.TEXT_MIN_WIDTH:
            centerX = (self.TEXT_MIN_WIDTH - self.textNode.getWidth()) / 2.0
            self.textNodePath.setX(self.textNodePath, centerX)
        self.textNodePath.setZ(top,
                               -self.BALLOON_Z_PADDING + self.TEXT_Z_OFFSET)
        if self.textHeight == self.TEXT_MIN_HEIGHT:
            centerZ = (ChatBalloon.TEXT_MIN_HEIGHT -
                       self.textNode.getHeight()) / 2.0
            self.textNodePath.setZ(self.textNodePath, -centerZ)
        self.textNodePath.setX(self.textNodePath, self.TEXT_X_OFFSET)

        # Add a button if one is given:
        if self.button is not None:
            self.buttonNodePath = button.copyTo(self)
            self.buttonNodePath.setPos(self.textNodePath, self.textWidth, 0,
                                       -self.textHeight)
            self.buttonNodePath.setPos(self.buttonNodePath,
                                       ChatBalloon.BUTTON_SHIFT)
            self.buttonNodePath.setScale(ChatBalloon.BUTTON_SCALE)
        else:
            self.buttonNodePath = None

        # Finally, enable anti-aliasing:
        self.setAntialias(AntialiasAttrib.MMultisample)