media player sample

Hi guys,
Im going through the ‘beginner’ level sample called ‘Media Player’ and trying to understand it. While Ive a lot of it, I have quite a few questions. I have not yet fully understood the system of inheritance in Panda3d and I might understand it better if I can clear up these doubts. This may be a lot of questions in one post and I hope I dont annoy people… If you wish I can compile your answers together and create an FAQ for this example in order to make a return contribution to the project …

  1. I notice that the mediafile is read twice.
    First time using:
    assert self.tex.read(MEDIAFILE), “Failed to load video!”

and second time using
self.sound=loader.loadSfx(MEDIAFILE)

My understanding is that self.tex.read(MEDIAFILE) reads only the video, whereas loader.loadSfx(MEDIAFILE) reads only the audio… Am I correct here? It appears videos are considered textures by Panda3d?

  1. In the statement
    self.sound = loader.loadSfx(MEDIAFILE)
    does self.sound automatically become an AudioSound object? I see the AudioSound object is used to test if the sound is playing later in the code using the condition
    if (self.sound.status() == AudioSound.PLAYING):

  2. I am quite confused by this block of code :

Set up a fullscreen card to set the video texture on.

cm = CardMaker(“My Fullscreen Card”);
cm.setFrameFullscreenQuad()
cm.setUvRange(self.tex)
card = NodePath(cm.generate())
card.reparentTo(render2d)
card.setTexture(self.tex)
card.setTexScale(TextureStage.getDefault(), self.tex.getTexScale())
self.sound=loader.loadSfx(MEDIAFILE)

(a) At first glance I thought the command setFrameFullScreenQuad() would have something to do with making the video fullscreen. But it does not play as a full screen but rather in a window. So I looked up the documentation, which says: “Sets the card to (-1,1,-1,1), which is appropriate if you plan to parent it to render2d and use it as a fullscreen quad.”
What is a fullscreen quad? This video is not appearing as full screen to me…

(b) I then looked up setUvRange(Texture const tex) in the documentation.
It says “Sets the range of UV’s that will be applied to the vertices appropriately to show the non-pad region of the texture.”
What is a UV ? Is it a coordinate system like X and Y coord? Does passing the texture as the argument give me the X and Y coordinates of the video?

© In the statement
card = NodePath(cm.generate())

I understand that cm.generate() makes a node out of the cardmaker object cm. We do this because everything in Panda3d needs to be made a node to render it?

We then use NodePath() function and pass our node to it. What is the NodePath that is created? I assumed by default the node would become a child of ‘render2d’. But in the next statement we explicitly say
card.reparentTo(render2d)
So what was the parent in the first place?

(d) Why do we reparentTo(render2d) ? I thought everything in Panda3d had to be reparentTo(render) ? Is render2d a child of render?

(e) card.setTexture(self.tex)

Here card is a nodepath… why do we sassign the texture to the nodepath? I though conceptually we would assign the texture to the cardmaker cm, or to its node cm.generate(). Assigning texture to a nodepath seems unintuitive to me at first sight… In Panda3d do you assign textures to nodepaths rather than to nodes. Or have I misunderstood this?

(f) In the carousel sample the attachNewNode() function was used to create new nodes rather than the generate() method. So I thoguht to myself why not use that method. I tried to reparent cm to render2d using the following lines instead

self.new_cm_node = render2d.attachNewNode(“cmnode”)
cm.reparentTo(self.new_cm_node)

However this failed saying “Cardmaker object has no attribute reparentTo”
While I understand that you cannot use reparentTo if the method does not exist for the object and therefore this will not work, was my basic logic correct here? As in attaching a node to render2d and then trying to assign cm to it? Or is my understanding of the inheritence structure completely wrong?

Id appreciate any help here from you folks. And I hope I don’t come across as a complete idiot asking so many questions :slight_smile:

Your understanding is correct.

The object returned by loader.loadSfx is (I think) either None (if it failed loading) or an AudioSound (or any of its derivatives).

Its a bit confusing terminology indeed. By a fullscreen quad, a quad that covers the entire window is meant here.

The UV coordinates of the vertices indicate how the texture is laid out on the geometry. A UV coordinate of (0, 0) indicates that the upperleft corner of the texture should be ‘pinned’ to that vertex, a UV coordinate of (1, 1) means the lowerright corner.

A CardMaker, as the name says, makes a quad. It is not a quad itself. So, with the CardMaker, you configure the card settings first and then you call generate() to make an actual card out of those settings.

A NodePath is a handle to the node. You need it to do high-level operations (actually, you can apply those operations on the underlying node too, but its a lot more work).

No, if you create a node, its standalone, it has no parent, until you reparent it.
Theres nothing magical about “render2d” and “render”, they are just standalone nodes (without parent) as well, with the only difference that theres a camera rendering them.

Nope. render2d is the 2d scene graph, for 2d (usually GUI) elements that are superimposed over the 3d scene.

A CardMaker itself only knows about the concept of geometry, not about the render state’s attributes (such as textures).

Calling NodePath.setTexture is just a high-level method to modify (or create) the TextureAttrib that is attached to the underlying node. Its just a faster way.
If it didn’t exist, you’d have to get the underlying node, get (or create) a new TextureAttrib, create a TextureStage, create a new TextureAttrib that is a copy of the orginal but with the new TextureStage<>Texture pair, and insert it back into the node.

Once again, a CardMaker itself is not a node, so you cannot insert it into the scenegraph. It only has the ability to create a node containing card geometry.

You can indeed use attachNewNode to create a new NodePath pointing to an existing node, or to create a new dummy PandaNode. The correct equivalent of your code would be:

self.new_cm_node_path = render2d.attachNewNode(cm.generate())

attachNewNode is just a shortcut. Maybe this will help understanding what it does:

class NodePath:
  def attachNewNode(self, node_or_name):
    np = NodePath(node_or_name)
    np.reparentTo(self)
    return np

(This is not the actual implementation - just to make clear what it does.)

Almost, you just had to know that a CardMaker is not a node or NodePath itself, even though it has the ability to generate a node. This would have been correct too:

self.new_cm_np = render2d.attachNewNode("cmnode")
NodePath(cm.generate()).reparentTo(self.new_cm_np)

Or, if you don’t want to mess with creating a NodePath, use the lowerlevel addChild interface:

self.new_cm_np = render2d.attachNewNode("cmnode")
self.new_cm_np.node().addChild(cm.generate())

Note that both would have created a dummy node called “cmnode” between the card and render2d.

No problem. These Panda3D fundamentals are not always trivial to understand. :slight_smile:

Thanks rdb. This makes everything much clearer