I am trying to create playing cards as cube-like objects from triangles with a face, back and sides.
I want to assign different textures to the front and back of the card. However, running my example code below results in just the back texture being drawn and the rest of the card being black. Surprisingly, even the side walls of the card become black, although they should have no texture at all.
If I just apply a texture to the front of the card, the side walls are correctly drawn in grey and the back remains white (the color I set for the front and back over which the texture is put).
Here is my CardFactory code:
# Standard imports
from typing import Dict, Union
# External imports
from panda3d.core import *
class CardDimensions:
def __init__(self, width=6.35, height=8.89, thickness=0.2):
self.width = width
self.height = height
self.thickness = thickness
class CardNodePathFactory:
def __init__(self,
parent,
dimensions: CardDimensions,
textures_front_paths: Dict[int, str],
textures_back_paths: Union[str, Dict[int, str]]):
self.parent = parent
self.dimensions = dimensions
self.textures_front = {uuid: loader.loadTexture(path) for uuid, path in textures_front_paths.items()}
if isinstance(textures_back_paths, str):
texture_back = loader.loadTexture(textures_back_paths)
self.textures_back = {uuid: texture_back for uuid in textures_front_paths}
else:
self.textures_back = {uuid: loader.loadTexture(path) for uuid, path in textures_back_paths.items()}
def get_card_node_path(self, uuid: int) -> NodePath:
array = GeomVertexArrayFormat()
array.addColumn("vertex", 3, Geom.NTFloat32, Geom.CPoint)
array.addColumn("color", 3, Geom.NTFloat32, Geom.CColor)
array.addColumn("normal", 3, Geom.NT_float32, Geom.CNormal)
array.addColumn("texcoord.front", 2, Geom.NT_float32, Geom.CTexcoord)
array.addColumn("texcoord.back", 2, Geom.NT_float32, Geom.CTexcoord)
vertex_format = GeomVertexFormat()
vertex_format.addArray(array)
vertex_format = GeomVertexFormat.registerFormat(vertex_format)
vertexCount = 0
dimensions = [self.dimensions.width / 2, self.dimensions.thickness / 2, self.dimensions.height / 2]
tris = GeomTriangles(Geom.UHStatic)
vertex_data = GeomVertexData("vertex_data", vertex_format, Geom.UHStatic)
vertex_writer = GeomVertexWriter(vertex_data, "vertex")
color_writer = GeomVertexWriter(vertex_data, "color")
normal_writer = GeomVertexWriter(vertex_data, "normal")
texcoord_front_writer = GeomVertexWriter(vertex_data, "texcoord.front")
texcoord_back_writer = GeomVertexWriter(vertex_data, "texcoord.back")
for i in range(3):
for direction in (-1, 1):
normal = VBase3()
normal[i] = direction
if i == 1:
rgb = [1., 1., 1.]
else:
rgb = [0.5, 0.5, 0.5]
r, g, b = rgb
color = (r, g, b, 1.)
for a, b in ((-1., -1.), (-1., 1.), (1., 1.), (1., -1.)):
vertex = VBase3()
vertex[i] = direction * dimensions[i]
vertex[(i + direction) % 3] = a * dimensions[(i + direction) % 3]
vertex[(i + direction * 2) % 3] = b * dimensions[(i + direction * 2) % 3]
vertex_writer.addData3f(vertex)
color_writer.addData4f(color)
normal_writer.addData3f(normal)
if i == 1:
if direction == -1:
texcoord_front_writer.addData2f((a + 1) / 2, (b + 1) / 2)
texcoord_back_writer.addData2f(0, 0)
else:
texcoord_front_writer.addData2f(0, 0)
texcoord_back_writer.addData2f((b + 1) / 2, (a + 1) / 2)
else:
texcoord_front_writer.addData2f(0, 0)
texcoord_back_writer.addData2f(0, 0)
vertexCount += 4
tris.addVertices(vertexCount - 2, vertexCount - 3, vertexCount - 4)
tris.addVertices(vertexCount - 4, vertexCount - 1, vertexCount - 2)
geom = Geom(vertex_data)
geom.addPrimitive(tris)
node = GeomNode("geom_node")
node.addGeom(geom)
card_np = self.parent.attachNewNode(node)
card_np.setPos(0, 0, 0)
# set textures
if self.textures_front[uuid] is not None:
ts_front = TextureStage('ts_front')
ts_front.setTexcoordName('front')
card_np.setTexture(ts_front, self.textures_front[uuid])
if self.textures_back[uuid] is not None:
ts_back = TextureStage('ts_back')
ts_back.setTexcoordName('back')
card_np.setTexture(ts_back, self.textures_back[uuid])
return card_np
and here is the code I execute to run the environment:
from pandac.PandaModules import *
from direct.directbase.DirectStart import *
from direct.showbase.DirectObject import DirectObject
from direct.interval.IntervalGlobal import *
from direct.task import Task
import random, sys
class World(DirectObject):
def __init__(self):
base.disableMouse()
self.directionalLight = DirectionalLight('directionalLight')
self.directionalLightNP = camera.attachNewNode(self.directionalLight)
self.directionalLightNP.setHpr(20., -20., 0.)
render.setLight(self.directionalLightNP)
self.ambientLight = AmbientLight('ambientLight')
self.ambientLight.setColor(VBase4(0.5, 0.5, 0.5, 0.5))
self.ambientLightNP = render.attachNewNode(self.ambientLight)
render.setLight(self.ambientLightNP)
camera.setPos(0, -50, -50)
camera.lookAt(0, 0, 0)
card_np_factory = CardNodePathFactory(
parent=render,
dimensions=CardDimensions(),
textures_front_paths={47: "../resources/images/french_deck/47.png"},
textures_back_paths="../resources/images/french_deck/back.jpg"
)
card = card_np_factory.get_card_node_path(47)
card.setPos(-3, -15, -25)
card.lookAt(card, 0, 0.25, 1)
card2 = card_np_factory.get_card_node_path(47)
card2.setPos(3, -15, -25)
card2.lookAt(card2, 0, -0.25, -1)
self.accept("escape", sys.exit)
if __name__ == '__main__':
winst = World()
run()
To test the code, you need to replace the texture paths (textures_front_paths=…; textures_back_paths=…) to some images available on your computer.
Do I have to put the nodes that should receive different textures into different GeomNodes or even into different NodePaths? What else am I doing wrong here?