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.
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?
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.
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)
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?
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.