Cardmaker Problem

Hi, I’m making a minecrafty style game just to test whether I can do it, and whether Panda can do it as well.

Do generate each cube, I’m using the cardmaker to generate me a plane. I have each face of the cube named something relevant e.g “”.

However, if I apply something to one side, it does it to all the sides. For instance, I’m trying to hide certain sides using NodePath.hide(), but if I use, say,, then all the sides will be hidden.

Is this an error I have made, or is it something with the cardmaker?


Here’s the code for a basic cube which is extended by a class for each type of cube. The last function is the one where the problems lie.

from panda3d.core import BitMask32
from pandac.PandaModules import CardMaker
from direct.task import Task
from pandac.PandaModules import TextureStage
class Cube:
    def __init__(self):
        cm = CardMaker('cm')
        self.ts = TextureStage('ts')

        self.front = self.blockHolder.attachNewNode(cm.generate())
        self.front.setPos(.5, .5, -.5)

        self.back = self.blockHolder.attachNewNode(cm.generate())
        self.back.setPos(-.5, -.5, -.5) = self.blockHolder.attachNewNode(cm.generate()), -.5, .5)

        self.bottom = self.blockHolder.attachNewNode(cm.generate())
        self.bottom.setPos(-.5, .5, -.5)

        self.right = self.blockHolder.attachNewNode(cm.generate())
        self.right.setPos(-.5, .5, -.5)

        self.left = self.blockHolder.attachNewNode(cm.generate())
        self.left.setPos(.5, -.5, -.5)

        self.cube_coll = loader.loadModel("models/Cube_coll")
        #self.cube_coll.setPos(x, y, z)
        self.cube_coll.setPythonTag("self", self)[int(self.x)][int(self.y)][int(self.z)] = self

        self.stopDamaging = False, "updateTask")
    def leftClick(self):
        if self.blocktype in ["Water"]:
            return False
        #self.blockHolder.removeNode()[int(self.x)][int(self.y)][int(self.z)] = None, "damageTask")
        #print("x:" + str(self.x) + " y:" +str(self.y) + " z:" + str(self.z))
    def rightClick(self, norm):
        if self.blocktype == "Water":
            return False
        pos = (norm[0], norm[1], norm[2])"""
        #block = eval( + "()")
        if[self.x+int(norm[0])][self.y+int(norm[1])][self.z+int(norm[2])] == None:
  [](, self.x+int(norm[0]), self.y+int(norm[1]), self.z+int(norm[2]))
        elif[self.x+int(norm[0])][self.y+int(norm[1])][self.z+int(norm[2])].blocktype == "Water":
  [](, self.x+int(norm[0]), self.y+int(norm[1]), self.z+int(norm[2]))
        #exec("getattr(" + + ", '__init__')(, self.x+norm[0], self.y+norm[1], self.z+norm[2])")
        #getattr(globals(),, self.x+norm[0], self.y+norm[1], self.z+norm[3])
        #data = str((, self.x+norm[0], self.y+norm[1], self.z+norm[2]))
        #exec( + data)
        return True
    def middleClick(self):
        print("x: " + str(self.x), "y: " + str(self.y), "z: " + str(self.z))
    def destroy(self):
        self.blockHolder.removeNode()[int(self.x)][int(self.y)][int(self.z)] = None
    def damage(self, task):
        if self.stopDamaging == True:
            self.stopDamaging = False
            return Task.done = self
        task.delayTime = .1 -= 1
        x = int((*3)
        if < self.maxhealth/5:
        elif < self.maxhealth/5*2:
        elif < self.maxhealth/5*3:
        elif < self.maxhealth/5*4:
        elif < self.maxhealth-1:
        if == 0:
            return Task.done
        return Task.again
    def stopDamagingFunc(self):
        #print( == self)
        #self.blockHolder.setTexture(self.ts, = self.maxhealth
        self.stopDamaging = True
    def mine(self):
        self.blockHolder.removeNode()[int(self.x)][int(self.y)][int(self.z)] = None = None
    def update(self, task):
        task.delayTime = 2
        x = self.x
        y = self.y
        z = self.z
        w =

        sqs = {}
            sqs[self.right] = (w[x-1][y][z])
            sqs[self.left] = (w[x+1][y][z])
            sqs[self.front] = (w[x][y+1][z])
            sqs[self.back] = (w[x][y-1][z])
            sqs[] = (w[x][y][z+1])
            sqs[self.bottom] = (w[x][y][z-1])
        except KeyError as e:

        for a in sqs:
            if sqs[a] == None:
            elif sqs[a].blocktype == "Water":
        return Task.again


I don’t see anything obviously wrong in your code, but it’s difficult to tell just by looking at it. But in general, there’s nothing about CardMaker that would make it behave as you describe.

It does sound a little bit like what happens after you call flattenStrong(), though. That call, which is so very important to optimize your scene for rendering, does it by combining separate pieces into a common piece. This means that you can no longer address your pieces separately, and now refers to the same node that self.left and so on refer to as well.

If that is your problem, then you will have to find another solution. One solution is not to call flattenStrong() at all, but of course this may mean your rendering performance is unacceptably poor. Another solution will be to maintain another, unflattened copy of your scene. Parent the flattened copy under render. When you need to make a change, remove the flattened copy, and make a fresh copy from the unflattened copy. Make the appropriate change, and then flatten it again.


Ahhhhhhhh it seems so obvious now!

Thanks for the reply, I’ll try to find some way to sort this out. Would there be any difference with using flattenLight/Medium or using the rigid body combiner?

flattenLight() or flattenMedium() will not have the same combining effect, but they probably won’t give you the performance benefit you’re looking for either (for the same reason).

The RigidBodyCombiner may be another way to achieve some part of the performance benefit, though it doesn’t support hide/show.