How to spawn Object on vertices: BGE

Basically I need the code for terrain generator but also the same methode on particle system like smoke trails or explosion.
Sadly I never found the right way which is supposedly using object data or bmesh or something, but not quite works on older version.
The only thing that works is using ray function which is very heavy on BGE.
However though there is something that I just dont know yet on object spawn management in my game, still studying the data assets though as external game management, just like working library load and free in BGE without any issues :pensive:

import bge
from bge import logic as GL
from bge import render
import random


import math, mathutils
from mathutils import Vector, noise

scene = GL.getCurrentScene()


def init(cont):
    
    own = cont.owner
    
    if own['states']=='made_lists':
        
        if not "rock_list" in own:
            own['close_list'] = []
            own['full_list']  = []
            
            own['states']='gen_mount'
    
    if own['states']=='gen_mount':
        check_mount(cont)
        
    elif own['states']=='gen_biom':
        check_bioms(cont)


def check_mount(cont):
    own = cont.owner
    tap = cont.sensors['tap'] 
          
    camera = scene.objects['Player']
    
    if tap.positive:
        
        mesh = own.meshes[0]
        
        for v in range(mesh.getVertexArrayLength(0)):
            vert = mesh.getVertex(0, v)
            pos = vert.getXYZ()
            
            dist = camera.getDistanceTo(pos)
            
            
            if dist>100.000 and not pos in own['close_list']: 
                
                rand_v = random.randint(0, 2000)
                
                if rand_v in range(0, 7):
                    if not pos in own['full_list']:
                        own['full_list'].insert(0, pos)
                        vert.color = [0.000000, 1.000000, 0.000000, 1.000000] 
                        
                        mount = scene.addObject("large_mounts", own, 0)
                        mount.worldPosition = pos
                        
                        vect = mount.getVectTo(own)
                        mount.lookAt(-vect[2], 2, 1.0)
                        
                        rand_rot = random.triangular(-180, 180)
                        mount.applyRotation((0.0, 0.0, rand_rot), True)
                        
                
                elif rand_v in range(8, 2000):
                    vert.color = [1.000000, 0.000000, 0.000000, 1.000000] 
                    pass
            
            elif dist<=100.000 and not pos in own['close_list']:
                vert.color = [1.000000, 0.000000, 0.000000, 1.000000]     
            
            own['states']='gen_biom'    

def check_bioms(cont):

    own = cont.owner
    tap = cont.sensors['tap'] 
          
    camera = scene.active_camera
    
    list_rock  = ["red_desert_rock4_kit1", "red_desert_stown5_kit1", "red_desert_medium_stown2_kit3"]
    list_stown = ["red_desert_stown6_kit2", "red_desert_stown8_kit2", "red_desert_light_stown6", 
                  "red_desert_light_stown7", "red_desert_light_stown7", "red_desert_light_stown7"] 
    
    
    if tap.positive:
        
        mesh = own.meshes[0]
        
        for v in range(mesh.getVertexArrayLength(0)):
            vert = mesh.getVertex(0, v)
            pos = vert.getXYZ()
            
            dist = camera.getDistanceTo(pos)
            
            
            if dist<50.000 and not pos in own['close_list']:    
                
                rand_v = random.randint(0, 100)
                
                if dist<5.0:
                    if not pos in own['full_list']:
                        own['full_list'].insert(0, pos)
                        vert.color = [0.000000, 0.000000, 1.000000, 1.000000] 
                
                
                elif dist>5.0:
                    
                    if rand_v in range(0, 60) and not pos in own['full_list']:
                        own['full_list'].insert(0, pos)
                        vert.color = [1.000000, 0.000000, 0.000000, 1.000000]
                        
                        if not pos in own['close_list']:
                            own['close_list'].insert(0, pos)
                            
                            obj = scene.addObject(random.choice(list_rock), own, 0)
                            obj.worldPosition = pos
                    
                            rand_rot = random.triangular(-180, 180)
                            rand_scl = random.triangular(0.4, 1.5)
                            rand_mov = random.triangular(0.3, 0.5)
                    
                            obj.applyRotation((0.0, 0.0, rand_rot), True)   
                            obj.scaling = [rand_scl, rand_scl, rand_scl]
                            obj.applyMovement((0.0, 0.0, -rand_mov), True)
                    
                            vect = obj.getVectTo(pos)
                            obj.lookAt(vect[2], 2, 2.0)
                            
                
                    elif rand_v in range(61, 100) and not pos in own['full_list']:
                        own['full_list'].insert(0, pos)
                        vert.color = [0.000000, 0.000000, 1.000000, 1.000000]    
            
            
        
def check_rocks(cont):
    
    own = cont.owner
    tap = cont.sensors['tap']
    
    planet = scene.objects['Player']
    spawn  = scene.objects['spawner']
    
    list_stown = ["red_desert_stown3_kit3", "red_desert_stown5_kit3", "red_desert_stown4_kit1",
                  "red_desert_stown1_kit1", "red_desert_stown2_kit1", "red_desert_stown3_kit1",
                  "red_desert_stown4_kit3"] 
                  
    
    if tap.positive:
        for i in scene.objects:
            if "rock_states" in i and i['rock_states']!='gen_stown' and own.getDistanceTo(i)<=35.000:
                
                i['rock_states']='gen_stown'
                
                spawn.worldPosition    = i.worldPosition
                spawn.worldOrientation = i.worldOrientation
                
                spawn.applyMovement((0.0, 0.0, 5.0), True)
                    
                '''s = planet.worldPosition
                e = i.worldPosition
                    
                r = planet.rayCast(e,s, 0, "", xray=True)
                h = r[0]
                p = r[1]
                n = r[2]
                    
                if h:
                    render.drawLine(s, p, [1.0, 0.0, 0.0])   '''
                    
                
                spawn['count'] = random.randint(10,25)
                
                while spawn['count']>0:
                    spawn['count']-=1
                    
                    randX = random.triangular(-30, 30)
                    randY = random.triangular(-30, 30)
                    
                    pos = [randX, randY]
                    s = spawn.worldPosition
                    e = spawn.worldPosition + spawn.getAxisVect([randX, randY, -15])
                    
                    r = planet.rayCast(e,s, 0, "", xray=False)
                    h = r[0]
                    p = r[1]
                    n = r[2]
                    
                    if h:
                        if "planet" in h:
                            obj = scene.addObject(random.choice(list_stown), own, 0)
                            obj.worldPosition = p
                    
                            rand_rot = random.triangular(-180, 180)
                            rand_scl = random.triangular(0.1, 0.3)
                            rand_mov = random.triangular(0.28, 0.35)
                        
                            obj.applyRotation((0.0, 0.0, rand_rot), True)   
                            obj.scaling = [rand_scl, rand_scl, rand_scl]
                            obj.applyMovement((0.0, 0.0, -rand_mov), True)
                    
                            
                            obj.lookAt(n, 2, 2.0)
                         



this is my code for generating objects in vertex positions - it uses the creation of lists and random ranges for the density of placement - first the positions of mountains and large rocks are determined, which then create smaller objects using the position of these objects as a starting position - you only need to replace in the lists of stones and rock the names of objects that you want to create at the position vertices - I attached two images, this is how objects will be created as in the image with a scatter - this works on planes and spheres

wow this is too much of a code, what I want is the simplicity of spawing vector, based on 3.0 + on this bpy operation/editing bellow that works fine

Even tho this is just makes things out simpler, eevee has most render on modifier processes such as Dupli vert and Particles.

So I just looking the vertex coordinates ( co ), It would be even better to add all verts as a lists

So what did I missing? should I use bpy import as a context? Sorry Im so noob on coding :pensive:

But now I’ll try that code for now :exploding_head:

you have not traversed all the vertices of the object in the v in range(mesh) loop.getVertexArrayLength(0)):
vert = mesh.GetVertex(0, v)
pos = vert.getXYZ() accordingly, you did not get the vertices and cannot access their positions in UPBGE. The method for getting vertices is based on iteration cycles - so go back to my script - first just get the vertices and add them to the list - be careful and do not add more than 50-60 to the list thousands of vertex positions otherwise your computer will freeze for about 3-4 minutes - when you have a list of vertices, you can also search for positions through cycles or binary trees - for example, find the ones closest to the player or in a certain zone from the player - for item in own[‘vert_pos_list’]: if player.getDistanceTo(item)<=15.000: #you logics - spawn objects or paint vertex for create custom mask for material

I first create lists for the object - if not “pos_list” in own: own[‘pos_list’] = then you can write the resulting position in them - if not pos in own[‘pos_list’]: own[‘pos_list’].insert(0,pos) - to do this through the bpy module, you have to follow the documentation for the module and the documentation for working with vertices - as far as I remember, even through bpy, you have to use the for loop to get all the vertices

So I just add getXYZ () at the end of spawnpoint and it works on a single vertex
But, I wan’t more to the entire vertex as well.

So this seems like particle spawning or dupli vert modifiers then, which is cool :thinking:

1 Like

In-game vertex indices (and count) do not necessarily match the ones of the bpy mesh data. The mesh is optimized for rendering, almost always resulting in the splitting of vertices due to several factors:
Materials: a vertex that is shared by two or more faces with different materials will be split into two or more vertices;
Normals: vertices can have only one normal, if a vertex is shared by faces with different normals, like in a flat shaded model, it will be split into as many normals as the faces it belongs have;
UVs: same as with normals, only one coordinate per vertex, per uv layer. A single vertex that has two positions in a uv layer will be split. Basically, they are split by uv seams;
Colors: again, one color per vertex. Two faces with different vertex colors on a shared vertex will result in said vertex being split.

Recently I made this script to analyze the mesh in game, because of reasons:

# bge-mesh-analyze.py

from bge import logic

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

mesh = own.meshes[0]

verticesPos = {}
totalVertices = 0

for matID, material in enumerate(mesh.materials):
    vertex_count = mesh.getVertexArrayLength(matID)
    
    totalVertices += vertex_count
    
    materialName = str(material)
    print(materialName.ljust(25), "vertices:", vertex_count)
    
    for vertex_index in range(vertex_count):
        vertex = mesh.getVertex(matID, vertex_index)
        
        if not verticesPos.get(tuple(vertex.XYZ)):
            verticesPos[tuple(vertex.XYZ)] = vertex_index

print("total vertices:", totalVertices)
print("unique vertices positions:", len(verticesPos))

It prints the vertex count per materials. Regarding unique positions, two real vertices (not split by optimization) with the same position will show as unique, naturally.

1 Like

Im not sure tupple is quite accurate tho…

However Finally I was complete the journey so far :sweat_smile:
After checking the 2.79 index

from bge import logic

cont = logic.getCurrentController()
object = cont.owner
scene = logic.getCurrentScene()

for mesh in object.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)
         # Do something with vertex here...
         # ... eg: color the vertex red.
         #vertex.color = [1.0, 0.0, 0.0, 1.0]
         #spawnpoint = object.meshes[0].getVertex(0,0).getXYZ()
         #spawnpoint = vertex.getXYZ()
         spawn = scene.addObject('Cube')
         spawn.worldPosition = vertex.getXYZ()

Should I add this to resources? :stuck_out_tongue:
Here is the sample

Add object to Vertex coordinate Sample.blend (691.1 KB)

Owh, the next stage is quite chalenging on this terrain project, which is the deletion of the terrain Objects because I messed up from this solitary object methode :sob:

1 Like

Upbge 3x uses bpy mesh

import bge 
KX_object = bge.logic.getCurrentController().owner 
Mesh = KX_object.blenderObject
Vert = Mesh.data.verices[index]

for vert in Mesh.data.vertices:
    world = KX_object.worldTransform @ vert.co

you can delete objects at a distance from the active camera - although I don’t do that - I create an invisible level of detail for objects and disable physics for them at a long distance, and with a large number of objects it works fine - deleting and re-adding objects can load a weak computer and manipulating objects in my project is more important - I delete only objects that have completed their logical cycle - for example, an asteroid from which minerals were extracted, a destroyed ship - to make it look logically justified, I do a complete cleaning of planets and objects for them if the player leaves some system and I need to rebuild everything :wink:

Thats what Im doing in 1st place, and still to figure it out.
So I can add setParent() instantly on added objects, so its easy to delete…for now
Some games use lists on the assests that perhaps convert the list into actual object render.
Even the landscape is listed, so loading text is very easy at the beginning then the next stage is rendering, also the link operation takes place during this process.
Is like chart sytem or pool of an assets and that is outside the game mechanic.

yes, this is a pool of objects - for optimization, chunks of maps have their own lists, or arrays of objects to avoid overflow - and then it is easier to manipulate such a list as objects - search and operations are performed faster - also if you divide the terrain in BGE/UPBGE/Range into parts of about 100-180 thousand polygons per part of the terrain you can it is very fast to generate a landscape and control objects just as quickly, unlike a large map of several million polygons, there is an interesting feature of BGE if the terrain is flat initially and it is generated during the game, then the start of the game is faster, unlike the map created in advance

What flat terrain? I already test the height map but not work in BGE Standalone player, I still figure it out on that particular issues.

apparently, these are difficulties of translation through Google - if you take a flat plane divided several times and generate a landscape through the code or vertex keys, then the game will start much faster at the start - unlike a pre-created landscape - elevation maps will most likely not work if you do not use scripts for shifting mesh vertices with physics recalculation - also modifiers do not they work in an offline player, they must be applied or written to the vertex key in order to play animations, I will give you links to a working option