This is a function which loads an image and creates a 3d plane proportional to the image. yresolution argument allows you to set the “pixel-perfect” resolution of the image, which means in that resolution one pixel in your image should match one pixel on your screen.

So this function can save you from using egg-texture-cards and if you have used Blender, it works like “2d cutout image importer” script.

This is how I believe OnScreenImage should have been implemented.

def loadImageAsPlane(filepath, yresolution = 600):
	Load image as 3d plane
	filepath -- image file path
	yresolution -- pixel-perfect width resolution
	tex = loader.loadTexture(filepath)
	cm = CardMaker(filepath + ' card')
	cm.setFrame(-tex.getOrigFileXSize(), tex.getOrigFileXSize(), -tex.getOrigFileYSize(), tex.getOrigFileYSize())
	card = NodePath(cm.generate())
	card.setScale(card.getScale()/ yresolution)
	card.flattenLight() # apply scale
	return card

Example use:

image = loadImageAsPlane('maps/circle.png')

Note that if you are using non power-of-two images (for your GUI), you will need to enable non power-of-two textures, or tell Panda to scale or pad them to the next bigger power-of-two, not the next smaller which is the default.

Put these lines in your Config.prc:

textures-auto-power-2 1
textures-power-2 up 

This will tell Panda to try to enable non power-of-two images and if your GPU doesn’t support them, it will then scale those images up, not down.

Updated the code.

Cool. Simple, clean and very useful. Thanks.

Still, you shouldn’t use this in real, commercial products, as loading non-2^n textures can cause problems with some graphics cards (mainly older ones, i believe).

I’m glad it is useful.

I don’t think this is right. We are porting a game to Panda3d engine right now and the GUI graphics are in random res.
Before I was making games I was into modding and pretty much all the games I came across had non power-of-two images for GUI. Of course I’ve also came across games which had a black borders added to the right and bottom, this were games which didn’t officially support modding, so the devs probably decided to have it like that and not let the game engine add the borders itself each time.

You are right that some GPUs don’t support non power-of-two images, but most games let the artist not worry about that and the game engine does the job of scaling or adding blank borders.
If you think about it, it makes sense. The artist doesn’t waste time adding borders to each of his images and doesn’t waste time creating a plane and generating the right UV coords.

Note that I’m talking about GUI images, everyone makes their textures in power-of-two res.

Panda also supports this, but only scaling the images, not adding borders. I think it would be better if it could also do it the other way, as even when you scale the images up to the next power-of-two, it might still appear just a little bit blurry than the original on some GPUs.

BTW, if you fear that telling Panda to scale each GUI image like this might be slow, then don’t worry. We have a PSD image with 50 layers which takes 0.4 seconds to load with our Python/C++ loader and render on a new notebook.

For the record, the 1.8.0 version of Panda supports a new mode:

textures-power-2 pad

which will add black borders as needed to frame the texture within a larger power-two texture. It is then up to your code to apply texture.getTexScale() where needed. This does, of course, prevent using repeated textures.

Also, I should clarify a minor misunderstanding in the above. These lines:

textures-power-2 none
textures-power-2 up

don’t tell Panda to try non-power-of-2, then fall back to scaling up. Instead, only the first line is read. If you want to try non-power-of-2 first then fall back, use these lines:

textures-auto-power-2 1
textures-power-2 up


Oh, nice that it’s already added. :slight_smile:

I’m not sure if I understand what you mean. Do you mean using the texture’s x,y resolution when creating the quad? I think getOrigFileXSize() and getOrigFileYSize() will get the resolution of the image file before it was resized by Panda, right?

Thanks, I updated my post.

I’ll add the info about pad mode to this page if you don’t mind
panda3d.org/manual/index.php … xture_Size

The point is that in “pad” mode, you have to adjust the UV’s–the texture coordinates are no longer 0 … 1 in both axes to cover the whole texture. Instead, it’s 0 … (something) in X, and 0 … (something) in Y, because of the extra pad regions.

You can compute the (something) by using the ratio getOrigFileXSize() / getXSize(), if you know the texture was padded and not scaled. You can also use (getXSize() - getPadXSize()) / getXSize(). But it might be easier to use getTexScale(), which pre-computes this ratio for you, and is correct in all cases–whether the texture is scaled, padded, or non-power-of-2.

Sure, thanks! You might want to mention that this textures-power-2 mode is new as of 1.8.0. Also, I think we should de-emphasize the use of getPadXSize/getPadYSize(), and recommend instead the simpler getTexScale().


Maybe I need some coffee, I don’t see why you need to adjust anything.
I just tried that mode in my game and all the non power-of-2 GUI stuff is right.
If the UV is not in range (0,1) anymore and the (0.1) range will only cover your actual image pixels, that means only the needed part of the texture will cover your plane or whatever. Isn’t that what we want?
EDIT: OK, I just tried with egg-texture-cards, not my function and I see what you mean. I don’t understand.

In “pad” mode, the useful pixels of the image are embedded within a larger texture. Only the lower-left corner of the texture contains the actual content we want to display.

For instance, suppose we load a 640x480 image into a 1024x512 texture. As far as the graphics card is concerned, we are feeding it a 1024x512 texture image, it just so happens that the only part of this image we want to see is the lower-left 640x480 pixels.

But if we apply the whole texture to a card, we will see the whole texture, including the black part we don’t care about. In order to avoid rendering the black region, we have to reduce the UV’s, so instead of going from 0 … 1 on the X axis, we go from 0 … (640/1024), or 0 … 0.625. Similarly on the Y axis.

Obviously, this isn’t necessary if we had simply scaled the texture up to 1024x512 (or down to 512x256), because in that case the graphics card receives the entire texture over its entire region, so when we apply the whole texture to a card, we are seeing exactly the part we meant to see (all of it).


OK I updated the manual page.

I see what you mean now. For some reason I assumed Panda3d would silently modify the UVs in this mode. But that doesn’t make much sense.

If anyone is curious, that mode shouldn’t affect this function. So if you want to use non power-of-two only for GUI images and load them with a function like this, you shouldn’t have to worry. For every other case if you don’t want to modify your texture scale manually, you can just go with “textures-power-2 up”.