Trying to scale card uv's so video playing is always the correct aspect ratio

Hi, im trying to get a card playing a video to keep the video at the correct aspect ratio by padding the top and bottom of the screen, so far i’ve tried normalising the resolution to no avail

So, let me first check that I understand you correctly: Is it that you have a full-screen card–and which thus has an arbitrary aspect-ratio–on which you want to play a video, but without that video being stretched?

If so, then I think that you should be able to achieve this by applying a texture-scale to the card. Now, the exact scaling I’m not confident of–I think that what should work is to compare the aspect ratio of the video to the aspect ratio of the window, and to scale either horizontally or vertically in accordance with that comparison (And likely in proportion to it, I think).

To apply a texture-scale to the node, you can call one of the variants of the “setTexScale” method. The parameters vary, but one example might look something like this:

myCard.setTexScale(textureStage, uScale, vScale)

Where “textureStage” is the relevant TextureStage for the texture being scaled (quite possibly just “TextureStage.getDefault()”), and uScale and vScale are the relevant scaling factors.

1 Like

yeah, i’m trying to find the scaling factors, since they are scaled between 0 and 1 I believe

Exactly.

I think that the relevant values should be obtained by comparing the ratios of window-dimension to image-dimension (i.e. window-height / image-height and window-width / image-width), and scaling by the ratio that is furthest from 1:1 the related dimension–but I don’t have time right now to test that. (In addition, note that I’m not sure that I have those ratios the right way around–it might be image-dimension / window-dimension; I’m not sure.)

[edit]

Okay, I took some time to play around with it, and the following code does more or less what you want, I think. (Although granted that I haven’t tested it with a non-square texture.) It likely calls for some alteration to fit your specific purposes, but it should hopefully convey the general idea.

Note that it loads an arbitrary texture–just replace the one referenced in the code with one that you have on hand.

from direct.showbase.ShowBase import ShowBase

from panda3d.core import NodePath, CardMaker, TextureStage
from direct.showbase.DirectObject import DirectObject

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        # Make a full-screen card
        cardMaker = CardMaker("card maker")
        cardMaker.setFrameFullscreenQuad()
        self.card = NodePath(cardMaker.generate())
        self.card.reparentTo(render2d)

        # Load a texture, and apply it to the above-made card
        self.tex = loader.loadTexture("normals.png")
        self.card.setTexture(self.tex)

        # Gain access to window-events so that we can adjust the
        # rendering when the window is resized.
        #
        # There may be a better way of doing it than by
        # creating a separate DirectObject, but for the
        # purposes of demonstration, at least, it should do.
        self.windowDirectObject = DirectObject()
        self.windowDirectObject.accept("window-event", self.windowUpdated)

        # Call the "windowUpdated" method to set the initial scaling
        self.windowUpdated(None)

    def windowUpdated(self, graphicsWindow):
        # Determine the ratios of window-size to texture-size
        # in each of the x- and y- dimensions 
        xRatio = self.win.getXSize() / self.tex.getXSize()
        yRatio = self.win.getYSize() / self.tex.getYSize()
        
        # Determine which ratio was the larger--i.e. in which
        # dimension the texture most differs from the window--
        # and adjust that, keeping the other dimension at
        # a scale of 1
        if xRatio > yRatio:
            self.card.setTexScale(TextureStage.getDefault(), xRatio / yRatio, 1)
        else:
            self.card.setTexScale(TextureStage.getDefault(), 1, yRatio / xRatio)


app = Game()
app.run()