Issue Renumbering vertex-indices

Hi to all,

I’ve run into a small issue when deleting vertices from panda3d’s vertex table with the following process:

  • Create 2 geoms and fill them up with the relevant primitive and vertex data.

  • Add these 2 geoms to a single geomNode.

  • Create the nodepath from the geomNode.

  • Attempt to remove the 4 primitives [numbers: 1,2,3,4] from the first geom as well as delete the vertex data they reference in the relevant vertex table.

  • Since data was removed from the relevant vertex table, renumber the remaining referenced vertex data in the remaining primitives within both geoms in the entire geomNode.

  • Here is the code performing the above-mentioned process, just copy paste and run to test it:

from direct.showbase.ShowBase import ShowBase
from panda3d.core import *
import copy
class run_me(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        self.DrawBox()
        
    def myNormalize(self,myVec):
       myVec.normalize()
       return myVec    
        
    
    def DrawBox(self):
        #administrative stuff...
        array = GeomVertexArrayFormat()
        array.addColumn(InternalName.make('vertex'), 3,Geom.NTFloat32, Geom.CPoint)
        array.addColumn(InternalName.make('texcoord'), 2,Geom.NTFloat32, Geom.CTexcoord)
        array.addColumn(InternalName.make('normal'), 3,Geom.NTFloat32, Geom.CNormal)
        array.addColumn(InternalName.make('color'), 4,Geom.NTFloat32, Geom.CColor)
        format = GeomVertexFormat()
        format.addArray(array)
        format = GeomVertexFormat.registerFormat(format)
        self.vdata = GeomVertexData('name', format, Geom.UHStatic)
        self.vertex = GeomVertexWriter(self.vdata, 'vertex')
        self.normal = GeomVertexWriter(self.vdata, 'normal')
        self.color = GeomVertexWriter(self.vdata, 'color')
        self.texcoord = GeomVertexWriter(self.vdata, 'texcoord')
        counter=0
        faces=[]
        
        #start Geom 1's data:
        p1=LVecBase3f(0, 0, 0)
        p2=LVecBase3f(60, 0, 0)
        p3=LVecBase3f(0, 256, 0)
        p4=LVecBase3f(60, 256, 0)
        faces.append([p1,p2,p3,p4])

        p5=LVecBase3f(60, 0, 0)
        p6=LVecBase3f(72, 0, 0)
        p7=LVecBase3f(60, 144, 0)
        p8=LVecBase3f(72, 144, 0)
        faces.append([p5,p6,p7,p8])

        p9=LVecBase3f(72, 0, 0)
        p10=LVecBase3f(84, 0, 0)
        p11=LVecBase3f(72, 144, 0)
        p12=LVecBase3f(84, 144, 0)
        faces.append([p9,p10,p11,p12])

        p13=LVecBase3f(60, 160, 0)
        p14=LVecBase3f(72, 160, 0)
        p15=LVecBase3f(60, 256, 0)
        p16=LVecBase3f(72, 256, 0)
        faces.append([p13,p14,p15,p16])

        p17=LVecBase3f(72, 160, 0)
        p18=LVecBase3f(84, 160, 0)
        p19=LVecBase3f(72, 256, 0)
        p20=LVecBase3f(84, 256, 0)
        faces.append([p17,p18,p19,p20])

        p21=LVecBase3f(84, 0, 0)
        p22=LVecBase3f(256, 0, 0)
        p23=LVecBase3f(84, 256, 0)
        p24=LVecBase3f(256, 256, 0)
        faces.append([p21,p22,p23,p24])
        
        
        #start Geom 2's data:
        p25=LVecBase3f(60, 160, 0)
        p26=LVecBase3f(60, 144, 0)
        p27=LVecBase3f(64, 156, -4)
        p28=LVecBase3f(64, 148, -4)
        faces.append([p25,p26,p27,p28])

        p29=LVecBase3f(64, 156, -4)
        p30=LVecBase3f(80, 156, -4)
        p31=LVecBase3f(60, 160, 0)
        p32=LVecBase3f(84, 160, 0)
        faces.append([p29,p30,p31,p32])

        p33=LVecBase3f(64, 148, -4)
        p34=LVecBase3f(80, 148, -4)
        p35=LVecBase3f(60, 144, 0)
        p36=LVecBase3f(84, 144, 0)
        faces.append([p33,p34,p35,p36])

        p37=LVecBase3f(84, 160, 0)
        p38=LVecBase3f(84, 144, 0)
        p39=LVecBase3f(80, 156, -4)
        p40=LVecBase3f(80, 148, -4)
        faces.append([p37,p38,p39,p40])

        p41=LVecBase3f(80, 156, -4)
        p42=LVecBase3f(76, 156, -4)
        p43=LVecBase3f(80, 148, -4)
        p44=LVecBase3f(76, 148, -4)
        faces.append([p41,p42,p43,p44])

        p45=LVecBase3f(64, 148, -4)
        p46=LVecBase3f(76, 148, -4)
        p47=LVecBase3f(64, 156, -4)
        p48=LVecBase3f(76, 156, -4)
        faces.append([p45,p46,p47,p48])

        #draw both geoms:
        node = GeomNode("GeomBox")
        numverz=0
        geom1=Geom(self.vdata)
        geom2=Geom(self.vdata)
        for array2 in faces:
            prim = GeomTriangles(Geom.UHStatic)
            self.draw_faces(prim,array2,counter)
            prim.closePrimitive()
            if(numverz>=6):
                geom2.addPrimitive(prim)
            else:
                geom1.addPrimitive(prim)
            counter+=4
            numverz+=1
        node.addGeom(geom1)
        node.addGeom(geom2)
        self.nodePathaqq = render.attachNewNode(node)
        self.nodePathaqq.setTransparency(TransparencyAttrib.MAlpha,1)
        self.nodePathaqq.setTwoSided(True)
        pandafile = Filename.fromOsSpecific("world_textures/desert.png")
        myTexture1=loader.loadTexture(pandafile)
        self.nodePathaqq.setAttrib(ShadeModelAttrib.make(ShadeModelAttrib.MSmooth))
        newTS = TextureStage('1')
        self.nodePathaqq.setTexture(newTS,myTexture1)
        
        #now, attempt to remove the four first primitives(faces)
        #from the first geom:
        gnode=self.nodePathaqq.node()
        geom=gnode.modifyGeom(0)
        gotVertices=[]
        #first, store the vertices that these four primitives
        #reference in the vertex table:
        p1=geom.modifyPrimitive(1)
        p2=geom.modifyPrimitive(2)
        p3=geom.modifyPrimitive(3)
        p4=geom.modifyPrimitive(4)
        for indd in p1.getVertexList():
            if(gotVertices.count(indd)==0):
                gotVertices.append(indd)
        
        for indd in p2.getVertexList():
            if(gotVertices.count(indd)==0):
                gotVertices.append(indd)
                
        for indd in p3.getVertexList():
            if(gotVertices.count(indd)==0):
                gotVertices.append(indd)
        
        for indd in p4.getVertexList():
            if(gotVertices.count(indd)==0):
                gotVertices.append(indd)
        #then, remove the four primitives themselves:
        geom.removePrimitive(1)
        geom.removePrimitive(1)
        geom.removePrimitive(1)
        geom.removePrimitive(1)
        bufferDat=[]
        bufferDat=copy.copy(gotVertices)
        
        #show the primitive and vertex data for the second geom
        #before deleting the relevant vertices:
        vdata = geom.modifyVertexData()
        vertexReader = GeomVertexReader(vdata, 'vertex')
        print "Pre-delete geom-data:\n"
        geom2=gnode.modifyGeom(1)
        for primz in xrange(geom2.getNumPrimitives()):
            primitive=geom2.modifyPrimitive(primz)
            print "Prim ",primz,": \n"
            seen=[]
            for vnm in primitive.getVertexList():
                   vertexReader.setRow(vnm)
                   v1 = vertexReader.getData3f()
                   if(seen.count(v1)==0):
                       print "V: ",vnm,v1
                       seen.append(v1)
            del seen[:]
        
        
        #remove the vertices from the vertex-table:
        vertexArray=vdata.modifyArray(0)
        vertexArrayModder=GeomVertexWriter(vertexArray,0)
        vertexArrayHandle=vertexArray.modifyHandle()
        vertexArrayFormat=vertexArrayHandle.getArrayFormat()
        length=vertexArrayFormat.getStride()
        for indiii in xrange(len(gotVertices)):
            vert=gotVertices[indiii]
            start=vert
            vertexArrayHandle.setSubdata(start*length, length, '')
            #renumber:
            for indiii2 in xrange(len(gotVertices)):
                if(gotVertices[indiii2]>vert):
                   gotVertices[indiii2]-=1
        
        #renumber the relevant data in the primitive tables:
        for i in range(2):
            #get the current geom:
            geom = gnode.modifyGeom(i)
            #go through the primitives in this geom:
            indoo=0
            lenz=geom.getNumPrimitives()
            while indoo<lenz:
                primitive=geom.modifyPrimitive(indoo)
                vertexArrayData=primitive.modifyVertices()
                primitiveArrayModder=GeomVertexWriter(vertexArrayData,0)
                primitiveArrayReader=GeomVertexReader(vertexArrayData,0)
                while not primitiveArrayReader.isAtEnd():
                    rowz=primitiveArrayReader.getData1i()
                    numValz=0
                    numValz=rowz
                    numgret=0
                    for indiii2 in xrange(len(bufferDat)):
                        if(numValz>bufferDat[indiii2]):
                           numgret+=1
                    numValz-=numgret
                    primitiveArrayModder.setData1i(numValz)        
                indoo+=1
        
        #show the data for the second geom after the deletion and
        #renumbering process is done:
        vertexReader = GeomVertexReader(vdata, 'vertex')
        print "Post-delete geom-data:\n"
        geom2=gnode.modifyGeom(1)
        for primz in xrange(geom2.getNumPrimitives()):
            primitive=geom2.modifyPrimitive(primz)
            print "Prim ",primz,": \n"
            seen=[]
            for vnm in primitive.getVertexList():
                   vertexReader.setRow(vnm)
                   v1 = vertexReader.getData3f()
                   if(seen.count(v1)==0):
                       print "V: ",vnm,v1
                       seen.append(v1)
            del seen[:]
    
    
    def draw_faces(self,prim_dat,array_sent,numbr):
            #1.vertex:
            k1=1
            self.vertex.addData3f(array_sent[0].x,array_sent[0].y, array_sent[0].z)
            self.normal.addData3f(self.myNormalize(Vec3(2*array_sent[0].x-1,2*array_sent[0].y-1,2*array_sent[0].z-1)))
            randi=0.8
            self.color.addData4f(k1, k1, k1, 1)
            self.texcoord.addData2f(1, 0)
            
            #2.vertex:
            k2=1
            self.vertex.addData3f(array_sent[1].x,array_sent[1].y, array_sent[1].z)
            self.normal.addData3f(self.myNormalize(Vec3(2*array_sent[1].x-1,2*array_sent[1].y-1,2*array_sent[1].z-1)))
            self.color.addData4f(k2, k2, k2, 1)
            self.texcoord.addData2f(1, 1)
            
            #3.vertex:
            k3=1
            self.vertex.addData3f(array_sent[2].x,array_sent[2].y, array_sent[2].z)
            self.normal.addData3f(self.myNormalize(Vec3(2*array_sent[2].x-1,2*array_sent[2].y-1,2*array_sent[2].z-1)))
            self.color.addData4f(k3, k3, k3, 1)
            self.texcoord.addData2f(0, 1)
            
            #4.vertex:
            k4=1
            self.vertex.addData3f(array_sent[3].x,array_sent[3].y, array_sent[3].z)
            self.normal.addData3f(self.myNormalize(Vec3(2*array_sent[3].x-1,2*array_sent[3].y-1,2*array_sent[3].z-1)))
            self.color.addData4f(k4, k4, k4, 1)
            self.texcoord.addData2f(0, 0)        
            
            #add to primitive:
            one=numbr
            two=numbr+1
            three=numbr+2
            four=numbr+3
            prim_dat.addVertices(one, two, three)
            prim_dat.addVertices(two, three, four)
            
            
w = run_me()
w.run()

Note that the first geom in the above case is just a flat plane with six different primitives that make six different faces.
The second geom is a trough made of six different primitives that make six different faces as well.

The problem arises with the second geom after the entire process is done. It’s data is rendered haphazardly, whereas querying the geom for the primitive and vertex-data before and after deletion shows that the data itself remains the same in terms of vertex-values, i.e. LVecBase3f data. However, if I only renumber the data from the first geom, no problem arises. Only when attempting to renumber data from both geoms does this issue arise. Both geoms reference data from the same vertex-table. Some screenshots:

Removing the primitives and vertices and renumbering data from only the first geom:[it visually works fine]


Removing the primitives and vertices and renumbering data from both geoms:[it visually does not work fine, geom 2’s data is rendered haphazardly]


What could be the issue? Did I miss something in my code? If any clarification is needed in terms of the question, please ask.

Thank you in advance.

Hi again :wink: .

It seems that when you want to modify GeomVertexData shared by multiple Geoms, a call to modifyVertexData makes the data unique, so it is no longer shared (meh, rdb can probably explain it better :stuck_out_tongue: ).

So replace this:

        #show the primitive and vertex data for the second geom
        #before deleting the relevant vertices:
        vdata = geom.modifyVertexData()

with this:

        #show the primitive and vertex data for the second geom
        #before deleting the relevant vertices:
        vdata = self.vdata

Also note that instead of updating the row indices in a nested for-loop:

        for indiii in xrange(len(gotVertices)):
            vert=gotVertices[indiii]
            start=vert
            vertexArrayHandle.setSubdata(start*length, length, '')
            #renumber:
            for indiii2 in xrange(len(gotVertices)):
                if(gotVertices[indiii2]>vert):
                   gotVertices[indiii2]-=1

it is much simpler to just delete the subdata in reverse sorted index order:

        for start in sorted(gotVertices, reverse=True):
            vertexArrayHandle.setSubdata(start*length, length, '')

Hope this helps :slight_smile: .

Hello again Sensei!

Thanks for the help and you’re right, it is easier to do things in reverse index order. I tried it and it worked and I understand what you say about created unique copies of geomVertexData, but that brings up another problem. What if there was no ‘self.vdata’ as in the above example? What if someone wanted to, in another function, modify a nodePath’s geometry like above? How would I access geomVertexData that is common to all the geoms in the geomNode of the nodePath? For instance:
[Again, just copy-paste and run]

from direct.showbase.ShowBase import ShowBase
from panda3d.core import *
import copy
class run_me(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        self.DrawBox()
        self.removeFaces()
        
    def myNormalize(self,myVec):
       myVec.normalize()
       return myVec    
        
    
    def removeFaces(self):
        #now, attempt to remove the four first primitives(faces)
        #from the first geom:
        gnode=self.nodePathaqq.node()
        geom=gnode.modifyGeom(0)
        gotVertices=[]
        #first, store the vertices that these four primitives
        #reference in the vertex table:
        p1=geom.modifyPrimitive(1)
        p2=geom.modifyPrimitive(2)
        p3=geom.modifyPrimitive(3)
        p4=geom.modifyPrimitive(4)
        for indd in p1.getVertexList():
            if(gotVertices.count(indd)==0):
                gotVertices.append(indd)
        
        for indd in p2.getVertexList():
            if(gotVertices.count(indd)==0):
                gotVertices.append(indd)
                
        for indd in p3.getVertexList():
            if(gotVertices.count(indd)==0):
                gotVertices.append(indd)
        
        for indd in p4.getVertexList():
            if(gotVertices.count(indd)==0):
                gotVertices.append(indd)
        #then, remove the four primitives themselves:
        geom.removePrimitive(1)
        geom.removePrimitive(1)
        geom.removePrimitive(1)
        geom.removePrimitive(1)
        bufferDat=[]
        bufferDat=copy.copy(gotVertices)
        

        #show the primitive and vertex data for the second geom
        #before deleting the relevant vertices:
        #vdata = self.vdata
        vdata=geom.modifyVertexData()
        vertexReader = GeomVertexReader(vdata, 'vertex')
        print "Pre-delete geom-data:\n"
        geom2=gnode.modifyGeom(1)
        for primz in xrange(geom2.getNumPrimitives()):
            primitive=geom2.modifyPrimitive(primz)
            print "Prim ",primz,": \n"
            seen=[]
            for vnm in primitive.getVertexList():
                   vertexReader.setRow(vnm)
                   v1 = vertexReader.getData3f()
                   if(seen.count(v1)==0):
                       print "V: ",vnm,v1
                       seen.append(v1)
            del seen[:]
        
        
        #remove the vertices from the vertex-table:
        
        vertexArray=GeomVertexArrayData(vdata.getArray(0))
        vertexArrayHandle=vertexArray.modifyHandle()
        vertexArrayFormat=vertexArrayHandle.getArrayFormat()
        length=vertexArrayFormat.getStride()

        for start in sorted(gotVertices, reverse=True):
            vertexArrayHandle.setSubdata(start*length, length, '')

        vdata.setArray(0, vertexArray)
        #renumber the relevant data in the primitive tables:
        for i in range(2):
            #get the current geom:
            geom = gnode.modifyGeom(i)
            #go through the primitives in this geom:
            indoo=0
            lenz=geom.getNumPrimitives()
            while indoo<lenz:
                primitive=geom.modifyPrimitive(indoo)
                vertexArrayData=primitive.modifyVertices()
                primitiveArrayModder=GeomVertexWriter(vertexArrayData,0)
                primitiveArrayReader=GeomVertexReader(vertexArrayData,0)
                while not primitiveArrayReader.isAtEnd():
                    rowz=primitiveArrayReader.getData1i()
                    numValz=0
                    numValz=rowz
                    numgret=0
                    for indiii2 in xrange(len(bufferDat)):
                        if(numValz>bufferDat[indiii2]):
                           numgret+=1
                    numValz-=numgret
                    primitiveArrayModder.setData1i(numValz)        
                indoo+=1
        
        #show the data for the second geom after the deletion and
        #renumbering process is done:
        vertexReader = GeomVertexReader(vdata, 'vertex')
        print "Post-delete geom-data:\n"
        geom2=gnode.modifyGeom(1)
        for primz in xrange(geom2.getNumPrimitives()):
            primitive=geom2.modifyPrimitive(primz)
            print "Prim ",primz,": \n"
            seen=[]
            for vnm in primitive.getVertexList():
                   vertexReader.setRow(vnm)
                   v1 = vertexReader.getData3f()
                   if(seen.count(v1)==0):
                       print "V: ",vnm,v1
                       seen.append(v1)
            del seen[:]        
        
    def DrawBox(self):
        #administrative stuff...
        array = GeomVertexArrayFormat()
        array.addColumn(InternalName.make('vertex'), 3,Geom.NTFloat32, Geom.CPoint)
        array.addColumn(InternalName.make('texcoord'), 2,Geom.NTFloat32, Geom.CTexcoord)
        array.addColumn(InternalName.make('normal'), 3,Geom.NTFloat32, Geom.CNormal)
        array.addColumn(InternalName.make('color'), 4,Geom.NTFloat32, Geom.CColor)
        format = GeomVertexFormat()
        format.addArray(array)
        format = GeomVertexFormat.registerFormat(format)
        vdata = GeomVertexData('name', format, Geom.UHStatic)
        self.vertex = GeomVertexWriter(vdata, 'vertex')
        self.normal = GeomVertexWriter(vdata, 'normal')
        self.color = GeomVertexWriter(vdata, 'color')
        self.texcoord = GeomVertexWriter(vdata, 'texcoord')
        counter=0
        faces=[]
        
        #start Geom 1's data:
        p1=LVecBase3f(0, 0, 0)
        p2=LVecBase3f(60, 0, 0)
        p3=LVecBase3f(0, 256, 0)
        p4=LVecBase3f(60, 256, 0)
        faces.append([p1,p2,p3,p4])

        p5=LVecBase3f(60, 0, 0)
        p6=LVecBase3f(72, 0, 0)
        p7=LVecBase3f(60, 144, 0)
        p8=LVecBase3f(72, 144, 0)
        faces.append([p5,p6,p7,p8])

        p9=LVecBase3f(72, 0, 0)
        p10=LVecBase3f(84, 0, 0)
        p11=LVecBase3f(72, 144, 0)
        p12=LVecBase3f(84, 144, 0)
        faces.append([p9,p10,p11,p12])

        p13=LVecBase3f(60, 160, 0)
        p14=LVecBase3f(72, 160, 0)
        p15=LVecBase3f(60, 256, 0)
        p16=LVecBase3f(72, 256, 0)
        faces.append([p13,p14,p15,p16])

        p17=LVecBase3f(72, 160, 0)
        p18=LVecBase3f(84, 160, 0)
        p19=LVecBase3f(72, 256, 0)
        p20=LVecBase3f(84, 256, 0)
        faces.append([p17,p18,p19,p20])

        p21=LVecBase3f(84, 0, 0)
        p22=LVecBase3f(256, 0, 0)
        p23=LVecBase3f(84, 256, 0)
        p24=LVecBase3f(256, 256, 0)
        faces.append([p21,p22,p23,p24])
        
        
        #start Geom 2's data:
        p25=LVecBase3f(60, 160, 0)
        p26=LVecBase3f(60, 144, 0)
        p27=LVecBase3f(64, 156, -4)
        p28=LVecBase3f(64, 148, -4)
        faces.append([p25,p26,p27,p28])

        p29=LVecBase3f(64, 156, -4)
        p30=LVecBase3f(80, 156, -4)
        p31=LVecBase3f(60, 160, 0)
        p32=LVecBase3f(84, 160, 0)
        faces.append([p29,p30,p31,p32])

        p33=LVecBase3f(64, 148, -4)
        p34=LVecBase3f(80, 148, -4)
        p35=LVecBase3f(60, 144, 0)
        p36=LVecBase3f(84, 144, 0)
        faces.append([p33,p34,p35,p36])

        p37=LVecBase3f(84, 160, 0)
        p38=LVecBase3f(84, 144, 0)
        p39=LVecBase3f(80, 156, -4)
        p40=LVecBase3f(80, 148, -4)
        faces.append([p37,p38,p39,p40])

        p41=LVecBase3f(80, 156, -4)
        p42=LVecBase3f(76, 156, -4)
        p43=LVecBase3f(80, 148, -4)
        p44=LVecBase3f(76, 148, -4)
        faces.append([p41,p42,p43,p44])

        p45=LVecBase3f(64, 148, -4)
        p46=LVecBase3f(76, 148, -4)
        p47=LVecBase3f(64, 156, -4)
        p48=LVecBase3f(76, 156, -4)
        faces.append([p45,p46,p47,p48])

        #draw both geoms:
        node = GeomNode("GeomBox")
        numverz=0
        geom1=Geom(vdata)
        geom2=Geom(vdata)
        for array2 in faces:
            prim = GeomTriangles(Geom.UHStatic)
            self.draw_faces(prim,array2,counter)
            prim.closePrimitive()
            if(numverz>=6):
                geom2.addPrimitive(prim)
            else:
                geom1.addPrimitive(prim)
            counter+=4
            numverz+=1
        node.addGeom(geom1)
        node.addGeom(geom2)
        self.nodePathaqq = render.attachNewNode(node)
        self.nodePathaqq.setTransparency(TransparencyAttrib.MAlpha,1)
        self.nodePathaqq.setTwoSided(True)
        pandafile = Filename.fromOsSpecific("world_textures/cotton_green.png")
        myTexture1=loader.loadTexture(pandafile)
        self.nodePathaqq.setAttrib(ShadeModelAttrib.make(ShadeModelAttrib.MSmooth))
        newTS = TextureStage('1')
        self.nodePathaqq.setTexture(newTS,myTexture1)
    
    
    def draw_faces(self,prim_dat,array_sent,numbr):
            #1.vertex:
            k1=1
            self.vertex.addData3f(array_sent[0].x,array_sent[0].y, array_sent[0].z)
            self.normal.addData3f(self.myNormalize(Vec3(2*array_sent[0].x-1,2*array_sent[0].y-1,2*array_sent[0].z-1)))
            randi=0.8
            self.color.addData4f(k1, k1, k1, 1)
            self.texcoord.addData2f(1, 0)
            
            #2.vertex:
            k2=1
            self.vertex.addData3f(array_sent[1].x,array_sent[1].y, array_sent[1].z)
            self.normal.addData3f(self.myNormalize(Vec3(2*array_sent[1].x-1,2*array_sent[1].y-1,2*array_sent[1].z-1)))
            self.color.addData4f(k2, k2, k2, 1)
            self.texcoord.addData2f(1, 1)
            
            #3.vertex:
            k3=1
            self.vertex.addData3f(array_sent[2].x,array_sent[2].y, array_sent[2].z)
            self.normal.addData3f(self.myNormalize(Vec3(2*array_sent[2].x-1,2*array_sent[2].y-1,2*array_sent[2].z-1)))
            self.color.addData4f(k3, k3, k3, 1)
            self.texcoord.addData2f(0, 1)
            
            #4.vertex:
            k4=1
            self.vertex.addData3f(array_sent[3].x,array_sent[3].y, array_sent[3].z)
            self.normal.addData3f(self.myNormalize(Vec3(2*array_sent[3].x-1,2*array_sent[3].y-1,2*array_sent[3].z-1)))
            self.color.addData4f(k4, k4, k4, 1)
            self.texcoord.addData2f(0, 0)        
            
            #add to primitive:
            one=numbr
            two=numbr+1
            three=numbr+2
            four=numbr+3
            prim_dat.addVertices(one, two, three)
            prim_dat.addVertices(two, three, four)
            
            
w = run_me()
w.run()

Same code, except the removing part is done in another function [self.removeFaces()] and there is no ‘self.vdata’, I again just use “vdata=geom.modifyVertexData()” because I don’t know any other way to access the geomVertexData that the geom is pointing to. So how would one go about it then? Again, the help is much appreciated! :smiley:

There’s no other way (that I know of) of modifying the data that allows it to remain shared, I’m afraid.
If it’s not possible to keep a reference to the original vertex data around, then you’ll have to modify the data of a single one of those geoms and replace the vertex data of every other geom with that modified data.

Anyway, I’ve modified your code with some additional optimizations (if you don’t mind, pandawan :wink: ):

from direct.showbase.ShowBase import ShowBase
from panda3d.core import *
import copy
class run_me(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        self.DrawBox()
        self.removeFaces()
       
    def myNormalize(self,myVec):
       myVec.normalize()
       return myVec   
       
   
    def removeFaces(self):
        #now, attempt to remove the four first primitives(faces)
        #from the first geom:
        gnode=self.nodePathaqq.node()
        geom=gnode.modifyGeom(0)
        gotVertices=set()
        #first, store the vertices that these four primitives
        #reference in the vertex table:
        p1=geom.modifyPrimitive(1)
        p2=geom.modifyPrimitive(2)
        p3=geom.modifyPrimitive(3)
        p4=geom.modifyPrimitive(4)
        prims = (p1, p2, p3, p4)
        gotVertices.update(*[p.getVertexList() for p in prims])
        #then, remove the four primitives themselves:
        geom.removePrimitive(1)
        geom.removePrimitive(1)
        geom.removePrimitive(1)
        geom.removePrimitive(1)
       

        #show the primitive and vertex data for the second geom
        #before deleting the relevant vertices:
        #vdata = self.vdata
        vdata=geom.modifyVertexData()
        vertexReader = GeomVertexReader(vdata, 'vertex')
        print "Pre-delete geom-data:\n"
        geom2=gnode.modifyGeom(1)
        for primz in xrange(geom2.getNumPrimitives()):
            primitive=geom2.modifyPrimitive(primz)
            print "Prim ",primz,": \n"
            seen=[]
            for vnm in primitive.getVertexList():
                   vertexReader.setRow(vnm)
                   v1 = vertexReader.getData3f()
                   if v1 not in seen:
                       print "V: ",vnm,v1
                       seen.append(v1)
       
       
        #remove the vertices from the vertex-table used by the first geom:
       
        # store all indices in a list; the indices referenced by the removed
        # primitives will be deleted from it, so it will correspond to the modified
        # vertex data; it can then be used to easily update the indices referenced
        # by the remaining primitives
        oldRowIndices=range(vdata.getNumRows())
        vertexArray=vdata.modifyArray(0)
        vertexArrayHandle=vertexArray.modifyHandle()
        vertexArrayFormat=vertexArrayHandle.getArrayFormat()
        length=vertexArrayFormat.getStride()

        for start in sorted(gotVertices, reverse=True):
            vertexArrayHandle.setSubdata(start*length, length, '')
            # delete the indices referenced by the removed primitives from the list,
            # so it reflects the changes made to the vertex data
            del oldRowIndices[start]

        vdata.setArray(0, vertexArray)

        #renumber the relevant data in the primitive tables:
        for i in range(2):
            #get the current geom:
            geom = gnode.modifyGeom(i)
            #go through the primitives in this geom:
            for indoo in xrange(geom.getNumPrimitives()):
                primitive=geom.modifyPrimitive(indoo)
                vertexArrayData=primitive.modifyVertices()
                primitiveArrayModder=GeomVertexWriter(vertexArrayData,0)
                primitiveArrayReader=GeomVertexReader(vertexArrayData,0)
                while not primitiveArrayReader.isAtEnd():
                    oldRowIndex=primitiveArrayReader.getData1i()
                    newRowIndex=oldRowIndices.index(oldRowIndex)
                    primitiveArrayModder.setData1i(newRowIndex)       

        #update the remaining geoms with the modified data:
        for i in range(1, gnode.getNumGeoms()):
            #get the current geom:
            geom = gnode.modifyGeom(i)
            # calling setVertexData checks if the data will correspond to the
            # indices referenced in the primitives, so those should already be
            # updated
            geom.setVertexData(vdata)
       
        #show the data for the second geom after the deletion and
        #renumbering process is done:
        vertexReader = GeomVertexReader(vdata, 'vertex')
        print "Post-delete geom-data:\n"
        geom2=gnode.modifyGeom(1)
        for primz in xrange(geom2.getNumPrimitives()):
            primitive=geom2.modifyPrimitive(primz)
            print "Prim ",primz,": \n"
            seen=[]
            for vnm in primitive.getVertexList():
                   vertexReader.setRow(vnm)
                   v1 = vertexReader.getData3f()
                   if v1 not in seen:
                       print "V: ",vnm,v1
                       seen.append(v1)
       
    def DrawBox(self):
        #administrative stuff...
        array = GeomVertexArrayFormat()
        array.addColumn(InternalName.make('vertex'), 3,Geom.NTFloat32, Geom.CPoint)
        array.addColumn(InternalName.make('texcoord'), 2,Geom.NTFloat32, Geom.CTexcoord)
        array.addColumn(InternalName.make('normal'), 3,Geom.NTFloat32, Geom.CNormal)
        array.addColumn(InternalName.make('color'), 4,Geom.NTFloat32, Geom.CColor)
        format = GeomVertexFormat()
        format.addArray(array)
        format = GeomVertexFormat.registerFormat(format)
        vdata = GeomVertexData('name', format, Geom.UHStatic)
        self.vertex = GeomVertexWriter(vdata, 'vertex')
        self.normal = GeomVertexWriter(vdata, 'normal')
        self.color = GeomVertexWriter(vdata, 'color')
        self.texcoord = GeomVertexWriter(vdata, 'texcoord')
        counter=0
        faces=[]
       
        #start Geom 1's data:
        p1=LVecBase3f(0, 0, 0)
        p2=LVecBase3f(60, 0, 0)
        p3=LVecBase3f(0, 256, 0)
        p4=LVecBase3f(60, 256, 0)
        faces.append([p1,p2,p3,p4])

        p5=LVecBase3f(60, 0, 0)
        p6=LVecBase3f(72, 0, 0)
        p7=LVecBase3f(60, 144, 0)
        p8=LVecBase3f(72, 144, 0)
        faces.append([p5,p6,p7,p8])

        p9=LVecBase3f(72, 0, 0)
        p10=LVecBase3f(84, 0, 0)
        p11=LVecBase3f(72, 144, 0)
        p12=LVecBase3f(84, 144, 0)
        faces.append([p9,p10,p11,p12])

        p13=LVecBase3f(60, 160, 0)
        p14=LVecBase3f(72, 160, 0)
        p15=LVecBase3f(60, 256, 0)
        p16=LVecBase3f(72, 256, 0)
        faces.append([p13,p14,p15,p16])

        p17=LVecBase3f(72, 160, 0)
        p18=LVecBase3f(84, 160, 0)
        p19=LVecBase3f(72, 256, 0)
        p20=LVecBase3f(84, 256, 0)
        faces.append([p17,p18,p19,p20])

        p21=LVecBase3f(84, 0, 0)
        p22=LVecBase3f(256, 0, 0)
        p23=LVecBase3f(84, 256, 0)
        p24=LVecBase3f(256, 256, 0)
        faces.append([p21,p22,p23,p24])
       
       
        #start Geom 2's data:
        p25=LVecBase3f(60, 160, 0)
        p26=LVecBase3f(60, 144, 0)
        p27=LVecBase3f(64, 156, -4)
        p28=LVecBase3f(64, 148, -4)
        faces.append([p25,p26,p27,p28])

        p29=LVecBase3f(64, 156, -4)
        p30=LVecBase3f(80, 156, -4)
        p31=LVecBase3f(60, 160, 0)
        p32=LVecBase3f(84, 160, 0)
        faces.append([p29,p30,p31,p32])

        p33=LVecBase3f(64, 148, -4)
        p34=LVecBase3f(80, 148, -4)
        p35=LVecBase3f(60, 144, 0)
        p36=LVecBase3f(84, 144, 0)
        faces.append([p33,p34,p35,p36])

        p37=LVecBase3f(84, 160, 0)
        p38=LVecBase3f(84, 144, 0)
        p39=LVecBase3f(80, 156, -4)
        p40=LVecBase3f(80, 148, -4)
        faces.append([p37,p38,p39,p40])

        p41=LVecBase3f(80, 156, -4)
        p42=LVecBase3f(76, 156, -4)
        p43=LVecBase3f(80, 148, -4)
        p44=LVecBase3f(76, 148, -4)
        faces.append([p41,p42,p43,p44])

        p45=LVecBase3f(64, 148, -4)
        p46=LVecBase3f(76, 148, -4)
        p47=LVecBase3f(64, 156, -4)
        p48=LVecBase3f(76, 156, -4)
        faces.append([p45,p46,p47,p48])

        #draw both geoms:
        node = GeomNode("GeomBox")
        numverz=0
        geom1=Geom(vdata)
        geom2=Geom(vdata)
        for array2 in faces:
            prim = GeomTriangles(Geom.UHStatic)
            self.draw_faces(prim,array2,counter)
            prim.closePrimitive()
            if(numverz>=6):
                geom2.addPrimitive(prim)
            else:
                geom1.addPrimitive(prim)
            counter+=4
            numverz+=1
        node.addGeom(geom1)
        node.addGeom(geom2)
        self.nodePathaqq = render.attachNewNode(node)
        self.nodePathaqq.setTransparency(TransparencyAttrib.MAlpha,1)
        self.nodePathaqq.setTwoSided(True)
        pandafile = Filename.fromOsSpecific("world_textures/cotton_green.png")
        myTexture1=loader.loadTexture(pandafile)
        self.nodePathaqq.setAttrib(ShadeModelAttrib.make(ShadeModelAttrib.MSmooth))
        newTS = TextureStage('1')
        self.nodePathaqq.setTexture(newTS,myTexture1)

   
    def draw_faces(self,prim_dat,array_sent,numbr):
            #1.vertex:
            k1=1
            self.vertex.addData3f(array_sent[0].x,array_sent[0].y, array_sent[0].z)
            self.normal.addData3f(self.myNormalize(Vec3(2*array_sent[0].x-1,2*array_sent[0].y-1,2*array_sent[0].z-1)))
            randi=0.8
            self.color.addData4f(k1, k1, k1, 1)
            self.texcoord.addData2f(1, 0)
           
            #2.vertex:
            k2=1
            self.vertex.addData3f(array_sent[1].x,array_sent[1].y, array_sent[1].z)
            self.normal.addData3f(self.myNormalize(Vec3(2*array_sent[1].x-1,2*array_sent[1].y-1,2*array_sent[1].z-1)))
            self.color.addData4f(k2, k2, k2, 1)
            self.texcoord.addData2f(1, 1)
           
            #3.vertex:
            k3=1
            self.vertex.addData3f(array_sent[2].x,array_sent[2].y, array_sent[2].z)
            self.normal.addData3f(self.myNormalize(Vec3(2*array_sent[2].x-1,2*array_sent[2].y-1,2*array_sent[2].z-1)))
            self.color.addData4f(k3, k3, k3, 1)
            self.texcoord.addData2f(0, 1)
           
            #4.vertex:
            k4=1
            self.vertex.addData3f(array_sent[3].x,array_sent[3].y, array_sent[3].z)
            self.normal.addData3f(self.myNormalize(Vec3(2*array_sent[3].x-1,2*array_sent[3].y-1,2*array_sent[3].z-1)))
            self.color.addData4f(k4, k4, k4, 1)
            self.texcoord.addData2f(0, 0)       
           
            #add to primitive:
            one=numbr
            two=numbr+1
            three=numbr+2
            four=numbr+3
            prim_dat.addVertices(one, two, three)
            prim_dat.addVertices(two, three, four)
           
           
w = run_me()
w.run()

It works splendidly, though to me at least, that was a rather subtle detail to catch, you know, that calling ‘modifyVertexData’ creates unique copies for the geoms that call it :open_mouth: , but as usual your esoteric eye-sight sees the unseen. I suppose I’ll just save a reference to the original vertex data in the larger project, thanks a lot, and good day to you!

Glad I could help :slight_smile: . But I made a mistake in my first reply about having to create a copy of the vertex array, so I’ve edited that post; I guess I got a bit confused there :unamused: .

Well, on this manual page I read the following:

I guess it was never considered that we might actually want to affect multiple nodes.

Is there in fact a particular reason why you are using the same GeomVertexData for the different Geoms? It is not necessary for adding them to the same GeomNode, after all.

And perhaps you could store vertex indices used by faces in tuples, instead of creating a GeomPrimitive for each face; you could then manipulate the array data of a single GeomTriangles, similar to how you manipulate the GeomVertexData, when removing a face.
Depending on how the vertex indices are ordered, you might even be able to use GeomPrimitive.offsetVertices.

Sorry for the relatively late reply as I had a rather busy weekend.

Well, I assumed that would slow things down as silly as that might sound. So giving each geom it’s own GeomVertexData won’t slow things down?

Those are interesting suggestions and I’ll get around to trying them out. Though since what I’m building is pretty big in size, a consequence of that is that I currently prioritize implementation over optimization. I’m presently interested in quickly and modestly implementing the ideas I have within the project and seeing them work as opposed to taking the time to implement them in an optimized manner. After majority of the work is done then I’d get around to fine-tuning the implementation. :smiley:

Although I haven’t done any benchmarks to test any differences in performance, it would kinda surprise me if unique vertex data would be noticeably slower than shared data. It does use up more memory, but I’m not sure how significant the impact would be. The only way to be sure is to check with PStats on a particularly heavy scene, I suppose.

Fair enough; good luck with your project :slight_smile: !

Thanks I appreciate it! :smiley:

It would use more memory, and Panda will waste time uploading the same data multiple times to the GPU. It’s only going to slow down noticeably if this is a bottleneck in your game (but your models would need to be pretty huge for this to be a bottleneck).

The Geom/GeomVertexData classes are designed as a copy-on-write class. This has the advantage of letting you copy parts of the scene graph around without incurring a cost for the extra copies. Of course, the downside is that trying to modify it will result in a copy.

However, GeomNode is not such a copy-on-write class. You could consider changing your model so that instead of many GeomNodes referencing the same Geom, you have a single GeomNode which is instanced to various places in your scene, and then modify the Geom associated with that GeomNode all you want.

I just now saw your message rdb.

Well within the actual game project, this is particularly used to generate terrain, hills, valleys, lakes, rivers etc. Since that is the case, it could be a bottleneck, as the terrain size is user-dependent, the user decides the size of the map to his liking.

That explains the additional copies each time “modifyVertexData” is called, thanks.

That’s actually how I’m doing things, many Geoms, one GeomNode which represents a ‘chunk/tile’ of the terrain. Then to create new ‘chunks/tiles’ as the player moves, I just instance the GeomNode to a different position and modify some of its Geoms. Of course I have to do this asynchronously but that is a story for another day!
Thanks for the additional clarifications rdb.