A Couple of Questions Concerning KX_VetexProxy

Context: I’m trying to figure out how I can create organic-ish ice crystals (if you’ve seen How to Train Your Dragon 2, the Bewilderbeast’s blast is what I’m trying to more-or-less replicate) in realtime.

Here’s my script:

import bge, mathutils.noise as noise

NBasis = noise.types.VORONOI_F2F1 #In my testing, the "Voronoi F2-F1" type was the best for spikey crystals.
#  Available Noise types are:
#    BLENDER
#    CELLNOISE (Great for geometric, cubical crystals, such as Cobalt)
#    NEWPERLIN
#    STDPERLIN
#    VORONOI_CRACKLE
#    VORONOI_F2F1
#    VORONOI_F1
#    VORONOI_F2
#    VORONOI_F3
#    VORONOI_F4

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

for msh in own.meshes: #Copied (with slight adjustments) directly from the API Reference
   for m_index in range(len(msh.materials)):
      for v_index in range(msh.getVertexArrayLength(m_index)):
         vtx = msh.getVertex(m_index, v_index)
         
         #Todo: consider angle-of-incidence and (maybe) speed-of-incidence (and maybe normal angle? Might not be required; not sure); use this info to add X/Y displacement to the Normal Z-displacement for more realism
         #  For now, just do Z-Disp.
         nrml = vtx.getNormal()
         pos = vtx.getXYZ()
         
         #Now, do the magic!  In theory, we now just add the Noise value to the Normal Z vector, apply the adjusted vector to the mesh, and it works.   ...Yeah right.
         val = noise.noise(pos, NBasis) #Get the noise value at the vertex
         val *= 3; val = abs(val) #Process the value; this will need to do more in the future
         nrml.z += val
         #pos.z += val
         #vtx.z += val #Apply the value
         
         vtx.setNormal(nrml)
         #vtx.setXYZ(pos)

My concept is pretty simple: Use mathutils.noise.noise() to displace each vertex in a source mesh along Normal Z (basically, a realtime Displace modifier). What I currently have kinda works, but it has a couple of problems:
Firstly, in order to get more random, organic-looking crystals, I need to use each vertex’s Global position, and KX_VertexProxy.getXYZ() and KX_VertexProxy.XYZ are both the vert’s Local position. So the first question is, how can I get the World position of each KX_VertexProxy? I read a couple of threads on this already, but they were from 8-9 years ago, and I really didn’t understand them. :confused:
Secondly, I actually have no clue how to displace a vertex along its Normal. My script (as you could probably tell) only displaces them along Object Z, which isn’t what I want. So the second question is, how do I do that?

Thanks in advance for any help you can give me!

Local position is relative to object origin; [0,0,0] to a vertex is the origin of the object. So wrld=own.worldPosition.copy()+pos.

We call it “normal” rather than “direction in 3D space” and maybe that’s why it sounds so mystical. It’s not. Say you try this:

from bge.logic import getCurrentController
from mathutils import Vector

own=getCurrentController().owner
direction=Vector([0,1,0]); speed=2.5;
own.worldPosition+=(direction*speed)

That moves you 2.5 units(meters) on the Y.

So replace “direction” with “normal”.
In your case, pos+=(nrml*val).

With that formula and some randomness, I got this:

Would take a little more tweaking on my part to make it look like ice, but eh, hopefully you get the idea.

isnt the local to world have a orientation too? (copy() isnt needed since a new vector is created when doing vector math)

obj.worldPosition+(obj.worldOrientation*pos) (replace * with @ for 2.8+)

getting local is similar

obj.worldOrientation.inverted()*(pos-obj.worldPosition) (i think thats it)

If it is scaled:

own.worldTransform * pos

should be enough to cover position, orientation and scale.

Yeah, (own.worldTransform*( Vector([pos[0:3], 1])).xyz does the thing if you want to keep that. But this is python, for real-time deform I wouldn’t bother.

Thank you! This was very helpful, and comes much closer, but still doesn’t seem quite right.

It appears that the displacement isn’t just happening along Normal Z. Did I just forget something in my script?

         nrml = vtx.getNormal()
         pos = vtx.getXYZ()
         wPos = own.worldPosition + pos
                  
         #Now, do the magic!  In theory, we now just add the Noise value to the Normal Z vector, apply the adjusted vector to the mesh, and it works.   ...Yeah right.
         val = noise.noise(wPos, NBasis) #Get the noise value at the vertex
         #val *= 3#; 
         val = abs(val) #Process the value; this will need to do more in the future
         
         nrml.z += val
         pos += (nrml*val)
         #vtx.z += val #Apply the value
         
         vtx.setNormal(nrml)
         vtx.setXYZ(pos)

Remove this line.

Alright, that seems to be correct. Thanks! I suppose the resultant holes in the mesh are just due to the way vertices are handled by the BGE and there’s not much I can do about them?

Edit: Okay, I can just fix those by setting the mesh to smooth-shaded. Thanks again for all the help, Liebranca! :grinning:

this is an important lesson. edges are split when sent to the gpu, so vertex counts might be much higher than one would think. even smooth shaded things sometimes!

in order for verts to stay joined, they need to be smooth, and have identical vertex data, such as uv coords, color, etc.

this isnt just an (up)bge thing, its how geometry is handled in general.

1 Like

Good to know. :+1: