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 “self.top”.

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, self.top.hide(), then all the sides will be hidden.

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

Thanks,
George

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.setH(180)
        self.front.setPos(.5, .5, -.5)

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

        self.top = self.blockHolder.attachNewNode(cm.generate())
        self.top.setP(-90)
        self.top.setPos(-.5, -.5, .5)

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

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

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

        self.cube_coll = loader.loadModel("models/Cube_coll")
        self.cube_coll.reparentTo(self.blockHolder)
        #self.cube_coll.setPos(x, y, z)
        self.cube_coll.setCollideMask(BitMask32.bit(0))
        self.cube_coll.hide()
        self.cube_coll.setPythonTag("self", self)

        self.world.map[int(self.x)][int(self.y)][int(self.z)] = self

        self.stopDamaging = False
        self.world.taskMgr.add(self.update, "updateTask")
    def leftClick(self):
        if self.blocktype in ["Water"]:
            return False
        #self.blockHolder.removeNode()
        #self.world.map[int(self.x)][int(self.y)][int(self.z)] = None
        self.world.taskMgr.add(self.damage, "damageTask")
        #self.world.damaged.append(self)
        #print("x:" + str(self.x) + " y:" +str(self.y) + " z:" + str(self.z))
    def rightClick(self, norm):
        if self.blocktype == "Water":
            return False
        """print(norm)
        print(type(norm))
        print(norm[1])
        pos = (norm[0], norm[1], norm[2])"""
        #print(globals())
        #block = eval(self.world.holdingBlock + "()")
        #print(block)
        if self.world.map[self.x+int(norm[0])][self.y+int(norm[1])][self.z+int(norm[2])] == None:
            self.world.blockTypes[self.world.holdingBlock](self.world, self.x+int(norm[0]), self.y+int(norm[1]), self.z+int(norm[2]))
        elif self.world.map[self.x+int(norm[0])][self.y+int(norm[1])][self.z+int(norm[2])].blocktype == "Water":
            self.world.map[self.x+int(norm[0])][self.y+int(norm[1])][self.z+int(norm[2])].destroy()
            self.world.blockTypes[self.world.holdingBlock](self.world, self.x+int(norm[0]), self.y+int(norm[1]), self.z+int(norm[2]))
        #exec("getattr(" + self.world.holdingBlock + ", '__init__')(self.world, self.x+norm[0], self.y+norm[1], self.z+norm[2])")
        #getattr(globals(), self.world.holdingBlock)(self.parent, self.x+norm[0], self.y+norm[1], self.z+norm[3])
        #data = str((self.world, self.x+norm[0], self.y+norm[1], self.z+norm[2]))
        #exec(self.world.holdingBlock + data)
        return True
    def middleClick(self):
        print("x: " + str(self.x), "y: " + str(self.y), "z: " + str(self.z))
        #self.blockHolder.clearTexture()
        self.top.show()
    def destroy(self):
        self.blockHolder.removeNode()
        self.world.map[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.world.mining = self
        self.world.checkLeftClickTarget()
        task.delayTime = .1
        self.health -= 1
        x = int((self.health/self.maxhealth)*3)
        #print(self.health)
        #print(x)
        if self.health < self.maxhealth/5:
            self.blockHolder.setTexture(self.ts, self.world.cracks5Texture)
        elif self.health < self.maxhealth/5*2:
            self.blockHolder.setTexture(self.ts, self.world.cracks4Texture)
        elif self.health < self.maxhealth/5*3:
            self.blockHolder.setTexture(self.ts, self.world.cracks3Texture)
        elif self.health < self.maxhealth/5*4:
            self.blockHolder.setTexture(self.ts, self.world.cracks2Texture)
        elif self.health < self.maxhealth-1:
            self.blockHolder.setTexture(self.ts, self.world.cracks1Texture)          
        if self.health == 0:
            self.mine()
            return Task.done
        return Task.again
    def stopDamagingFunc(self):
        self.blockHolder.clearTexture()
        #print(self.world.mining == self)
        #self.blockHolder.setTexture(self.ts, self.world.blankTexture)
        self.health = self.maxhealth
        self.stopDamaging = True
        #print("STOOOOOOOPPPP!!!!!!")
    def mine(self):
        self.blockHolder.removeNode()
        self.world.map[int(self.x)][int(self.y)][int(self.z)] = None
        self.world.mining = None
        self.world.leftClick()
    def update(self, task):
        task.delayTime = 2
        x = self.x
        y = self.y
        z = self.z
        
        w = self.world.map

        sqs = {}
        try:
            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[self.top] = (w[x][y][z+1])
            sqs[self.bottom] = (w[x][y][z-1])
        except KeyError as e:
            pass
            #print(e)

        for a in sqs:
            if sqs[a] == None:
                a.show()
            elif sqs[a].blocktype == "Water":
                a.show()
            else:
                a.hide()
        return Task.again
        
        
        
        

Hi,

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 self.top 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.

David

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.

David