Node setup for sprites rendering

Hi,

the code snippet below is intented to simplify sprite rendering by allowing you to use screen coordinates in calls to NodePath.setPos() and NodePath.setScale() instead of render[2d] and aspect2d coordinates.

The idea is to first call create_sprites_node_setup and use the returned nodepath as the parent to all your sprite nodes. Then you can use screen coordinates with your sprite nodes.

from pandac.PandaModules import Vec3
from pandac.PandaModules import Point3
from pandac.PandaModules import TextureStage
from pandac.PandaModules import NodePath
from pandac.PandaModules import CardMaker
from direct.interval.LerpInterval import LerpPosInterval
import direct.directbase.DirectStart

def create_sprites_node_setup(screenWidth, screenHeight, parent = render2d):
	
	aspect_ratio = parent.getScale()[0]	
	
	screenOrigin = parent.attachNewNode('screen_origin')
	screenNode = screenOrigin.attachNewNode('screen_node')

	screenOrigin.setPos(-1.0/aspect_ratio, 0.0, 1.0)
	screenOrigin.setScale(2.0, 1.0, -2.0)

	screenNode.setPos(0, 0, 0)
	
	screenNode.setScale(1.0/(aspect_ratio*screenWidth), 1.0, 1.0/screenHeight)
	screenNode.setTexScale(TextureStage.getDefault(), 1.0, -1.0)
	
	# test some points	
#	points = [(0,0), (screenWidth, 0), (screenWidth, screenHeight), (screenWidth/2.0, screenHeight/2.0), (0, screenHeight)]
#	for pt in points:
#		print '%s -> %s' % (pt, str(parent.getRelativePoint(screenNode, Vec3(pt[0], 0, pt[1]))))
	
	return screenNode


def create_sprite(filename, x, z, sx, sz, transparent = 1):
	cm = CardMaker('spritesMaker')
	cm.setFrame(-0.5, 0.5, -0.5, 0.5)
	sprite = cm.generate()	

	tex = loader.loadTexture(filename)

	spriteNP = NodePath(sprite)	

	spriteNP.setTexture(tex)
	spriteNP.setPos(x, 0, z)
	spriteNP.setScale(sx, 1.0, sz)
	spriteNP.setTransparency(transparent)
	return spriteNP
		
	
# use it like this
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

sprites_root = create_sprites_node_setup(SCREEN_WIDTH, SCREEN_HEIGHT, aspect2d)
flower_sprite = create_sprite('flower.png', SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 128, 128, 1)
flower_sprite.reparentTo(sprites_root)

horizontalMove = LerpPosInterval(flower_sprite, 9.0, Point3(SCREEN_WIDTH, 0, SCREEN_HEIGHT / 2), Point3(0, 0, SCREEN_HEIGHT / 2))
horizontalMove.loop()
	
run()

As you can see the coordinates to LerpPosInterval are given in screen space.

In case you want to run the code, you can right click on the sprite image below and save it:

It has simplified my sprite handling code which was initially mixed with coordinates converions. :slight_smile:

Regards

Yeah i also use a similar method. For 2d stuff i think you really got to do the pixel perfect normal screen space coordinates.

hey, just want to tell i used your script for start making a simple gui and really has helped a lot.

chek it here if you feel like.
discourse.panda3d.org/viewtopic.php?p=44860

I coudn’t manage to create text inside the screen root node or inside the sprite nodes, is there any way you can do that? Thanks!

thanks!! this is really usefull, it should be on the sample applications and the doc.

hello,

I have exaxtly the same problem as “snaptothegrid”, I did not succeed to attach text node to the sprites root. Any idea ?

I draw cardmarker with original size of texture with a little code:

def create_sprite(filename, x, z, screenWidth, screenHeight, transparent=1):    
    tex = loader.loadTexture(filename)   
    cm = CardMaker('spritesMaker')
    sprite = NodePath(cm.generate())    
    sprite.setTexture(tex)
    
    #Scale and position
    sx = float(tex.getXSize()) / screenWidth
    sz = float(tex.getYSize()) / screenHeight
    sprite.setScale(sx, 1.0, sz)
    sprite.setPos(x, 0.0, z) 
    sprite.setTransparency(transparent)
    return sprite