movement trails

To me it seems its too heavy on performance but i dont know how to make it faster.

import direct.directbase.DirectStart
from direct.task import Task
from pandac.PandaModules import GeomVertexData,GeomVertexFormat,Geom
from pandac.PandaModules import GeomVertexWriter,GeomTristrips,GeomNode
from pandac.PandaModules import OmniBoundingVolume,TransparencyAttrib
from pandac.PandaModules import TextNode
from direct.showbase.DirectObject import DirectObject
import time


b1=loader.loadModel('smiley')
b1.reparentTo(render)
b1.setScale(0.5)
b1.flattenLight()
b1.setX(1)
spin=0

#text notes
text=TextNode('text')
textnode=aspect2d.attachNewNode(text)            
text.setAlign(TextNode.ALeft)
text.setText('1:Thickness fade\n2:Transparency fade\n3:Texture\n4:spin\n5:random color\n6:7:+/- time step')                        
textnode.setScale(0.1)
textnode.setPos(-1,0,0)

#texture
texture=loader.loadTexture('samples\Particles\water2.png')

class Trail():
    def __init__(self,parent,hi,len,col,step):
        self.parent=parent
        #trail len
        self.nr=len+1    
        self.col=col
        self.step=step
        self.do=time.time()
        
        #attrib
        self.tfade=False
        self.afade=False
        self.tex=False

        #vert data
        self.vdata = GeomVertexData('name',GeomVertexFormat.getV3c4t2(),Geom.UHDynamic)
        
        #writers
        self.vertex = GeomVertexWriter(self.vdata, 'vertex')        
        self.color = GeomVertexWriter(self.vdata, 'color') 
        self.texcoord=GeomVertexWriter(self.vdata,'texcoord') 
        
        #trail height step
        self.dim=1.0/(self.nr<<1)
                    
        for i in range(self.nr<<1):
            #alpha=1.0/(i+1)                    
            alpha=1
            self.color.addData4f(col[0],col[1],col[2],alpha)            
            self.color.addData4f(col[0],col[1],col[2],alpha)
            self.texcoord.addData2f(i*self.dim*2,1)            
            self.texcoord.addData2f(i*self.dim*2,0)            
  
        #create prim
        prim = GeomTristrips(Geom.UHDynamic)
        for i in range(self.nr<<1):
            prim.addVertex(i)
        prim.closePrimitive()

        #geom
        self.geom=Geom(self.vdata)
        self.geom.addPrimitive(prim)
        self.geom.doublesideInPlace()        
                 
        self.node=GeomNode('gnode')
        self.node.addGeom(self.geom)
        self.node.setBounds(OmniBoundingVolume())
        self.node.setFinal(True) 
        
        self.nodep = render.attachNewNode(self.node)        
        self.nodep.setTransparency(TransparencyAttrib.MDual)
        
        #pos root
        self.root=self.parent.attachNewNode('root')
                
        #p dif is height
        self.p1=self.root.attachNewNode('p1')
        self.p1.setZ(hi*0.5)
        
        self.p2=self.root.attachNewNode('p2')
        self.p2.setZ(-hi*0.5)
        
            
        #trail poses
        self.trpos=[]
        pos=[self.p1.getPos(),self.p2.getPos()]
        
        for i in range(self.nr):
            self.trpos.append((pos))

    def run(self):
        if time.time()-self.do>self.step:
            self.do=time.time()
                   
            self.trpos.pop()
        
            self.trpos.insert(0,[self.p1.getPos(render),self.p2.getPos(render)])
        else:
            self.trpos[0]=[self.p1.getPos(render),self.p2.getPos(render)]
            
        pos = GeomVertexWriter(self.vdata, 'vertex')
        for i in range(self.nr):
            
            npos=self.root.getPos(render)
            xr,yr,zr=self.root.getPos()
            x1,y1,z1=self.trpos[i][0]
            x2,y2,z2=self.trpos[i][1]
            xx=x1-x2
            yy=y1-y2
            zz=z1-z2

            xdim=self.dim*i*xx
            ydim=self.dim*i*yy
            zdim=self.dim*i*zz
            
            if not self.tfade:
                xdim=0
                ydim=0
                zdim=0
            
            pos.setData3f(x1-xdim,y1-ydim,z1-zdim)
            pos.setData3f(x2+xdim,y2+ydim,z2+zdim)

                    
                
           
          
def do(task):
    b1.setY(b1,0.3)
    b1.setP(b1.getP()+3)        
    b1.setH(b1.getH()+1.5)
    b1.setR(b1.getR()+spin)        
    tr.run()        
    return task.cont
    



class Input(DirectObject):
    def __init__(self):
        self.accept('1',self.logic,[1])
        self.accept('2',self.logic,[2])
        self.accept('3',self.logic,[3])
        self.accept('4',self.logic,[4])
        self.accept('5',self.logic,[5])
        self.accept('6',self.logic,[6])
        self.accept('7',self.logic,[7])
        
    def logic(self,tag):
        if tag==1:
            if tr.tfade:
                tr.tfade=False
            else:
                tr.tfade=True

        elif tag==2:
            if tr.afade:
                tr.afade=False
            else:
                tr.afade=True
                
        
            tr.color = GeomVertexWriter(tr.vdata, 'color') 
            col=tr.col             
            for i in range(tr.nr<<1):
                if tr.afade:                        
                    alpha=1.0/(i+1)                 
                else:
                    alpha=1
                
                tr.color.addData4f(col[0],col[1],col[2],alpha)            
                tr.color.addData4f(col[0],col[1],col[2],alpha)                

        elif tag==3:
            if tr.tex:
                tr.tex=False
                tr.nodep.clearTexture()
            else:
                tr.tex=True
                tr.nodep.setTexture(texture)
                
        elif tag==4:
            global spin
            if spin==10:
                spin=0
            else:
                spin=10
                
            

        elif tag==5:
            tr.color = GeomVertexWriter(tr.vdata, 'color') 
            tr.col=tr.root.getPos(render)           
            col=tr.col
            
            for i in range(tr.nr<<1):
                if tr.afade:                        
                    alpha=1.0/(i+1)                 
                else:
                    alpha=1
                
                tr.color.addData4f(col[0],col[1],col[2],alpha)            
                tr.color.addData4f(col[0],col[1],col[2],alpha)                
            
        elif tag==6 or tag==7:
            if tag==6:
                tr.step+=0.01
            if tag==7 and tr.step>0.01:
                tr.step-=0.01
                
            global text
            text.setText('1:Thickness fade\n2:Transparency fade\n3:Texture\n4:spin\n5:random color\n6:7:+/- time step:'+str(tr.step)+'ms,'+str(1.0/tr.step)+'fps')                                    


#(parent,height,nr of segments,(color),time step)
#NB. time step does not improve performance, it just allows to make longer trails with less verts
tr=Trail(b1,1,20,(1,1,0),0.03)


i=Input()    

taskMgr.add(do,'do')

base.cam.setPos(0,-40,3)
base.setFrameRateMeter(True)
run()

why do you need water2 when you are not using it?

GeomVertexWriter are slow because they recreate the gome every frame!
use GeomVertex(re)Writer to rewrite a single geom or use MeshDrawer the faster package.

I found it very fast and very well coded
the only place where to squeeze a frame or two is maybe to turn a couple of multiplies in adds changing this:

            xdim=self.dim*i*xx
            ydim=self.dim*i*yy
            zdim=self.dim*i*zz

to this

        d=self.dim
        dt=-d
        for i in range(self.nr):
            ...
            dt+=d
            xdim=dt*xx
            ydim=dt*yy
            zdim=dt*zz

in the .run() loop but I can’t see how better could be done.

EDIT
unless you’re a panda3d veteran like treeform :wink:

I am not sure what I did wrong but you should get the idea after running this code:


import direct.directbase.DirectStart
from direct.task import Task
from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
import time


b1=loader.loadModel('smiley')
b1.reparentTo(render)
b1.setScale(0.5)
b1.flattenLight()
b1.setX(1)
spin=0


maxParticles = 1000
by = 8

        


#text notes
text=TextNode('text')
textnode=aspect2d.attachNewNode(text)           
text.setAlign(TextNode.ALeft)
text.setText('1:Thickness fade\n2:Transparency fade\n3:Texture\n4:spin\n5:random color\n6:7:+/- time step')                       
textnode.setScale(0.1)
textnode.setPos(-1,0,0)

#texture
texture=loader.loadTexture('water.jpg')

class Trail:
    def __init__(self,parent,hi,len,col,step):
        self.parent=parent
        #trail len
        self.nr=len+1   
        self.col=col
        self.step=step
        self.do=time.time()
       
        #attrib
        self.tfade=False
        self.afade=False
        self.tex=False
    
        #trail height step
        self.dim=1.0/(self.nr<<1) 
        
       

       
        #pos root
        self.root=self.parent.attachNewNode('root')
               
        #p dif is height
        self.p1=self.root.attachNewNode('p1')
        self.p1.setZ(hi*0.5)
       
        self.p2=self.root.attachNewNode('p2')
        self.p2.setZ(-hi*0.5)
       
           
        self.generator = MeshDrawer()
        self.generator.setBudget(maxParticles)
        self.generator.setPlateSize(by)
        generatorNode = self.generator.getRoot()
        generatorNode.reparentTo(self.root)
        generatorNode.setDepthWrite(False)
        generatorNode.setTransparency(True)
        generatorNode.setTwoSided(True)
        generatorNode.setBin("fixed",0)
        generatorNode.setLightOff(True)
           
        #trail poses
        self.trpos=[]
        pos=[self.p1.getPos(),self.p2.getPos()]
       
        for i in range(self.nr):
            self.trpos.append((pos))

    def run(self):
        if time.time()-self.do>self.step:
            self.do=time.time()
                   
            self.trpos.pop()
       
            self.trpos.insert(0,[self.p1.getPos(render),self.p2.getPos(render)])
        else:
            self.trpos[0]=[self.p1.getPos(render),self.p2.getPos(render)]
           
        self.generator.begin(base.cam,render)
        
        prev1 = None
        prev2 = None
        
        for i in range(self.nr):
           
            npos=self.root.getPos(render)
            xr,yr,zr=self.root.getPos()
            x1,y1,z1=self.trpos[i][0]
            x2,y2,z2=self.trpos[i][1]
            xx=x1-x2
            yy=y1-y2
            zz=z1-z2

            xdim=self.dim*i*xx
            ydim=self.dim*i*yy
            zdim=self.dim*i*zz
           
            if not self.tfade:
                xdim=0
                ydim=0
                zdim=0

            now1 = Vec3(x1-xdim,y1-ydim,z1-zdim)
            now2 = Vec3(x2+xdim,y2+ydim,z2+zdim)
        
            if prev1 != None:

                self.generator.tri(
                    now1,Vec4(1,1,1,1),Vec2(0,0),
                    now2,Vec4(1,1,1,1),Vec2(0,0),
                    prev1,Vec4(1,1,1,1),Vec2(0,0),
                    )
                
                self.generator.tri(
                    prev1,Vec4(1,1,1,1),Vec2(0,0),
                    prev2,Vec4(1,1,1,1),Vec2(0,0),
                    now2,Vec4(1,1,1,1),Vec2(0,0),
                    )
            
            prev1 = now1
            prev2 = now2
            
        self.generator.end() 
                   
               
           
         
def do(task):
    b1.setY(b1,0.3)
    b1.setP(b1.getP()+3)       
    b1.setH(b1.getH()+1.5)
    b1.setR(b1.getR()+spin)       
    tr.run()       
    return task.cont
   



class Input(DirectObject):
    def __init__(self):
        self.accept('1',self.logic,[1])
        self.accept('2',self.logic,[2])
        self.accept('3',self.logic,[3])
        self.accept('4',self.logic,[4])
        self.accept('5',self.logic,[5])
        self.accept('6',self.logic,[6])
        self.accept('7',self.logic,[7])
       
    def logic(self,tag):
        if tag==1:
            if tr.tfade:
                tr.tfade=False
            else:
                tr.tfade=True

#        elif tag==2:
#            if tr.afade:
#                tr.afade=False
#            else:
#                tr.afade=True
#               
#       
#            tr.color = GeomVertexWriter(tr.vdata, 'color')
#            col=tr.col             
#            for i in range(tr.nr<<1):
#                if tr.afade:                       
#                    alpha=1.0/(i+1)                 
#                else:
#                    alpha=1
#               
#                tr.color.addData4f(col[0],col[1],col[2],alpha)           
#                tr.color.addData4f(col[0],col[1],col[2],alpha)               
#
#        elif tag==3:
#            if tr.tex:
#                tr.tex=False
#                tr.nodep.clearTexture()
#            else:
#                tr.tex=True
#                tr.nodep.setTexture(texture)
#               
#        elif tag==4:
#            global spin
#            if spin==10:
#                spin=0
#            else:
#                spin=10
#               
#           
#
#        elif tag==5:
#            tr.color = GeomVertexWriter(tr.vdata, 'color')
#            tr.col=tr.root.getPos(render)           
#            col=tr.col
#           
#            for i in range(tr.nr<<1):
#                if tr.afade:                       
#                    alpha=1.0/(i+1)                 
#                else:
#                    alpha=1
#               
#                tr.color.addData4f(col[0],col[1],col[2],alpha)           
#                tr.color.addData4f(col[0],col[1],col[2],alpha)               
#           
#        elif tag==6 or tag==7:
#            if tag==6:
#                tr.step+=0.01
#            if tag==7 and tr.step>0.01:
#                tr.step-=0.01
#               
#            global text
#            text.setText('1:Thickness fade\n2:Transparency fade\n3:Texture\n4:spin\n5:random color\n6:7:+/- time step:'+str(tr.step)+'ms,'+str(1.0/tr.step)+'fps')                                   


#(parent,height,nr of segments,(color),time step)
#NB. time step does not improve performance, it just allows to make longer trails with less verts
tr=Trail(b1,1,20,(1,1,0),0.03)


i=Input()   

taskMgr.add(do,'do')

base.cam.setPos(0,-40,3)
base.setFrameRateMeter(True)
run()