Use information already gathered to displace vertices

in this code I build a matrix (tile heights)
then I create all the tiles - I need to connect up all the tiles vertex to create a terrain.

I need to store a vertex displacement list for each tile so when it is spawned, or it’s physics is spawned, I can deform them.

I create a list right now for each tile (neighbor tiles) (PL=[])

I am having trouble understanding how to loop through the data I have and create this ‘vertex displacement list’
then the tiles will be removed,

LOD adds the tiles back + applies the ‘Vertex displacement list’ to the newly added mesh as needed.

The part I need help with, is what to do after

#Get all neighbors for each tile        
        for object in store:
            x = object[4]
            y = object[5]
            print(x)
            print(y)
            PL = []
            if x>=1:
                T1 = own['Matrix'][x-1][y]
                PL.append([T1,x-1,y])
                if y<=98:
                    T5 = own['Matrix']
                    PL.append([T5,x-1,y+1])
            if x<=98:        
                T2 = own['Matrix'][x+1][y]
                PL.append([T2,x+1,y])
                if y<=1:
                    T3 = own['Matrix'][x+1][y+1]
                    PL.append([T3,x+1,y+1])
                elif y>=1:    
                    T4 = own['Matrix'][x+1][y-1]
                    PL.append([T4,x+1,y-1])
            if y>=1:
                T6 = own['Matrix'][x][y-1]
                PL.append([T6,x,y-1])
            

so T1 - T6 = neighbor tiles that may or may not exist.
[Neighbor,X_location in matrix,Y_location in matrix]

Height = own[‘Matrix’][X][Y]*.5

#Full script


import bgeimport random
import mathutils
def main():


    cont = bge.logic.getCurrentController()
    own = cont.owner


    if 'Matrix' not in own:
        #Generate noise in a 100x100 array
        own['Actor']=own.scene.objects['Actor']
        random.seed('Blank')
        own['Matrix'] = [[0]*100 for i in range(100)]
        for x in range(100):
            for y in range(100):
                own['Matrix'][x][y]=random.random()
        minx = own.worldPosition.x-50
        miny = own.worldPosition.y-50
        index=0
        store =[]
        #create game objects for each point and store position and other info in a list
        for x in range(100):
            X=minx+x
            for y in range(100):
                Y =miny+y
                added = own.scene.addObject('Tile',own,0)
                added.worldPosition = (X,Y,own['Matrix'][int(x)][int(y)]*.5)
                
                index+=1
                store.append([added, added.worldPosition.copy(),added['Type'],0,int(x),int(y)])
                
        
        
        #Get all neighbors for each tile
        
        for object in store:
            x = object[4]
            y = object[5]
            print(x)
            print(y)
            PL = []
            if x>=1:
                T1 = own['Matrix'][x-1][y]
                PL.append([T1,x-1,y])
                if y<=98:
                    T5 = own['Matrix']
                    PL.append([T5,x-1,y+1])
            if x<=98:        
                T2 = own['Matrix'][x+1][y]
                PL.append([T2,x+1,y])
                if y<=1:
                    T3 = own['Matrix'][x+1][y+1]
                    PL.append([T3,x+1,y+1])
                elif y>=1:    
                    T4 = own['Matrix'][x+1][y-1]
                    PL.append([T4,x+1,y-1])
            if y>=1:
                T6 = own['Matrix'][x][y-1]
                PL.append([T6,x,y-1])
            
    
        
        #BuildKDTree
        
        size = len(store)
        kd = mathutils.kdtree.KDTree(size)


        for i in range(size):
            kd.insert(store[i][1], i)


        kd.balance()
        own['KD']=kd
        own['Store']=store   
                
    else:
        
        #Use KDTRee for Object LOD and PhysicsLOD
        SClose=[]
        close =[]
        kd = own['KD']
        # Find points within a radius of the 3d cursor
        
        co_find = own['Actor'].worldPosition
        for (co, index, dist) in kd.find_range(co_find,5):
            SClose.append(index)
            if own['Store'][index][0].invalid:
                
                
                strn=own['Store'][index][2]+"_Physics"
                own['Store'][index][0]=own.scene.addObject(own['Store'][index][2],own,0)
                added=own.scene.addObject(strn,own,0)
                own['Store'][index][0]['Phys']=added
                
                own['Store'][index][0].worldPosition=own['Store'][index][1]
                own['Store'][index][0]['Phys'].worldPosition=own['Store'][index][1]
                own['Store'][index][3]=1    
                own['added']+=1
                
            elif own['Store'][index][3]==1:
                
                if 'Phys' not in own['Store'][index][0]:
                    print(own['Store'][index][0].getPropertyNames())
                    strn=own['Store'][index][2]+"_Physics"
                    own['Store'][index][0]['Phys']=own.scene.addObject(strn,own,0)
                    own['Store'][index][0]['Phys'].worldPosition=own['Store'][index][1]
                    #own['added']+=1
                        
        for (co, index, dist) in kd.find_range(co_find,10):
            if index not in SClose:
                close.append(index)
                if own['Store'][index][0].invalid:
                   
                    own['Store'][index][0]=own.scene.addObject(own['Store'][index][2],own,0)
                    own['Store'][index][0].worldPosition=own['Store'][index][1]
                    own['Store'][index][3]=1
                else:
                    if 'Phys' in own['Store'][index][0]:
                        own['Store'][index][0]['Phys'].endObject()
                        del own['Store'][index][0]['Phys']
                        print(index)
                        
                   
        for (co, index, dist) in kd.find_range(co_find, 12):
            if index not in close and index not in SClose:
                if own['Store'][index][3]==1:
                    try:
                        own['Store'][index][0].endObject()
                        own['Store'][index][3]=0
                    except:
                        n=False
                    
                      
                
                                
                    
        
        
        
main()



system working so far-

What you could use is a dictionary. Dictionary keys can be tuples, so get the xyz value of the vert and store that as a tuple:

{(15, 12, 11):vertex}

You can use the mathutils.Vector.to_tuple() function to convert them directly, or do it manually.
If you find that 1 blender unit is not enough precision (two or more verts are too near each other) you can multiply the values by 100 or 1000 to get more possibilities. Don’t forget to divide them later when looking them up.

This will help you to find the verts you need more easily without having to loop through the list each time.
You could write a manager class with some functions for setting and getting the verts using just a vector input to make things simpler for yourself:

store_vert = VertexManager.add_vert(my_vector, my_vertex)
...

the_vert_i_want = VertexManager.get_vert(my_vector) 

Using classes like this makes it easier to write code because when you come to write your level builder script you can use the methods of the class instead of having to write new functions each time. It’s like how you use game_object.alignAxisToVect() or game_object.applyImpulse().

oops wrong video**

yeah that makes sense, I could use a kdtree to store all of the verts and then only use it 1 time per vert per tile, and store a changelist for each tile in the object Kdtree data

Ok here is what I have so far-
it’s working, but not the way I intended

generate kdtree of all vertex world positions with Z value chopped out

value = objects[0].worldPosition+objects[0].worldOrientation*vertex.XYZ.copy()
#chop off Z value and transform to world space
value.z=0

find all vertex that share a XY location within .05 units

get average of the height stored in each object for all verts that share an XY locations

set all verts that share an XY locations height to calculated average
(I got the world position of each vertex then set Z to the height desired - then transformed back to local then set)

I am doing something wrong though, because the objects vertex don’t all line up
(there should be no gaps)


import bge
import random
import mathutils
from mathutils import Vector
def main():


    cont = bge.logic.getCurrentController()
    own = cont.owner


    if 'Matrix' not in own:
        #Generate noise in a 100x100 array
        own['Actor']=own.scene.objects['Actor']
        random.seed('Blank')
        own['Matrix'] = [[0]*10 for i in range(10)]
        for x in range(10):
            for y in range(10):
                own['Matrix'][x][y]=random.random()
        minx = own.worldPosition.x-5
        miny = own.worldPosition.y-5
        index=0
        store =[]
        #create game objects for each point and store position and other info in a list
        for x in range(10):
            X=minx+x
            for y in range(10):
                Y =miny+y
                added = own.scene.addObject('Tile',own,0)
                added.worldPosition = (X,Y,own['Matrix'][int(x)][int(y)]*.5)
                added['H']=own['Matrix'][int(x)][int(y)]*.5
                index+=1
                store.append([added, added.worldPosition.copy(),added['Type'],0,int(x),int(y)])
                
        
        
       
        #gather data about each vertex
        listz = []
        index5=0        
        for objects in store:
            for mesh in objects[0].meshes:
               for m_index in range(len(mesh.materials)):
                  for v_index in range(mesh.getVertexArrayLength(m_index)):
                     vertex = mesh.getVertex(m_index, v_index)
                     value = objects[0].worldPosition+objects[0].worldOrientation*vertex.XYZ.copy()
                     #chop off Z value and transform to world space
                     value.z=0
                     listz.append([vertex,value,objects[0],index5])
            index5+=1         
        kd2 = mathutils.kdtree.KDTree(len(listz))
        index2=0
        
        
        
        #build kd tree of all vertex with Z value chopped off in world space
        for vert in listz:
            kd2.insert(vert[1],index2)
            index2+=1
        kd2.balance()    
                        
        CL = []
        index3=0
        
        #Get all vertex that are near each others XY 
        for vert in listz:
            grab=[]
            
            for (co, index, dist) in kd2.find_range(vert[1],.05):
                grab.append(index)
                
            #Store list of vertex that share XY in vertex data list   
            listz[index3].append(grab)
            index3+=1
            
            
        
        
        for vert in listz:
            
            
            H=0.0
            count=0
            #get height for all vertex that share a XY cord
            for neighbors in vert[4]:
                H += listz[neighbors][2]['H']
                count+=1
            if count>0:    
                #Average height
                H = H/count
                
                
            #Set height
            for neighbors in vert[4]:
                #transform vertex to world
                xyz = listz[neighbors][2].worldPosition+listz[neighbors][2].worldOrientation*(listz[neighbors][0].XYZ)
                print('pos was '+str(xyz))
                #set height
                xyz.z =H
                
                #localize back to objects
                xyz = xyz - listz[neighbors][2].worldPosition
                
                xyz = listz[neighbors][2].worldOrientation.inverted()*xyz
                #set position
                listz[neighbors][0].XYZ = xyz
                
                #generate info to print
                place = listz[neighbors][2].worldPosition+listz[neighbors][2].worldOrientation*(listz[neighbors][0].XYZ)
                print('pos is '+str(place))
                
                #I am setting the vertex position, but its not correct
                
                
                
                
                
                
                    
                
                
       
                    
                      
                
                                
                    
        
        
        
main()





Could use a bit of help if you have the time, thanks!

Attachments

terrainTest2.blend (476 KB)

Ok - if I add a object to the scene, and it shares a mesh with another, and I edit one mesh… it probably edits the other?

do I have to libNew for each mesh?

oK I have GFX working, but libNew does not work on physics meshes for some reasons I don’t understand.

Maybe this will help.I did a search.

no, my issue is that I can generate a unique graphics mesh using libnew,
but even after assigning the new physicsMesh and graphics mesh by using replace mesh, reinstancePhysics() does not update the physics shape,

Why can’t you just use premade pieces for the ground?Are you trying to make diggable ground?

so I can store a massive world as a seed,

then texture using vertex colors as a mix system for 3 terrain tile materials,

then place instances of grass and trees and rocks,

then generate roads using a algorythm and store them as a seed

then generate lots adjacent to each road near intersections, and randomly elsewhere, then I will place seeds for buildings exteriors I enjoy in lots, some buildings will have randomly generated dungeons to explore, with changemaps unique to each dungeon where I can hand place props, and overide maze configurations.

then I will walk around inside the game, and place object instances related to the story and waypoints and gates and then pickle those changes.

no matter where you are ploped down in the terrain, it should be able to generate that area from the seed.

this should make the game small in size, but huge in scale and speed.

I ran in to this problem myself before.
No, libnew cant create new physics mesh, only visual mesh.
The way around it is to preplace unique ground object tiles on the active layer to the upper size you require (100×100 for example) in blender then when bge starts you can get them from own.scene.objects (again using a vector to tuple keyed dictionary) and modify them. You can delete any extra ones outside the size you want for that level. You can even use a single large mesh rather than many individual ones. I’ve found similar performance during testing.

I will be generating the meshes, then deviating them, then turning physics off for them, then setting them all invisible,

as they come in range they will be made visible and as they get closer they will get physics.

this makes the 1 huge mesh thing impossible, (and it would kill performance)

I can add the 100’s of tiles before hand and just loop through them I suppose, it’s just silly between libNew or reinstancePhysicsMesh() that we can’t generate a new physics mesh :spin:

Yeah, it’s an engine limitation.
BTW I think physics already pretty much ignores checks on distant objects by doing a bounding box check before any more complex collision detection. Or it should at any rate…

What you’re trying to do is probably what I’ve already done on BGECore. This problem you have now I had it too before. I left you a workarround here: https://github.com/UPBGE/blender/issues/211

for some reason facebook compression killed the videos quality but the good news is it is working!

upbge reinstancePhysicsMesh() has the ability to generate new meshes :smiley:

Very good work.I hope you can finish your wrectified now.But i think there is going to be more things you need help figuring out.We’ll we will be waiting.

Thanks LostScience :smiley:

got normals working now as well :smiley:

Lets see the protagonist walking over the terrain.

I notic your rasterizer and gpu latency are quite high. I had this same problem when using a lot of objects with nodes. I couldn’t track down the problem but it soured me to the idea of using lots of small tiles.

I am using 50x50 tiles that are 4*4 verts and each one is a unique mesh,
right now I am zoomed out so you can see all possible tiles, but when I look forward (like a over the shoulder 3rd person)
I get about 4ms-6ms rasterizer when not screencasting in HD.


when static draw call batching hits in 0.1.2 I expect it to be around 2ms (just the terrain tiles)

buildings instances made of kit tiles another 2ms

grass + trees = another 2ms

road another 2ms

enemy units =???

and that is everything,
the dungeons won’t be able to see outside* (almost like mega man legends)