Realtime Mesh Water Waves(No animations required)

Sadly, it will not work on a sphere. It would appear that the script is trying to keep the faces of the mesh facing upward and will try to do the same to the sphere’s faces. Also it won’t receive the collisions for some reason…waves_sphere_edit.blend (940 KB)

The dampening should be possible through editing some of the calls to BPY but i’m not entirely sure which ones :confused:

Can you use a initial face normal rather then Z axis?

Thanks for the feedback.
After uploading I realised how unreadable the code was.
I’ve cleaned it up and have been working on speeding things up. It’s not there yet but things are improving.
Yes this can be done to a sphere and the waves can be dampened too.

Here’s the .blend

Attachments

POOL.blend (898 KB)


That’s really cool! Thanks for sharing!

My Pleasure.
Here’s a faster version that interlaces the updates (if you get my drift). This gives me as many as 7000 faces at 60fps on my modestly endowed computer.
Disappointingly, it doesn’t want to work in the standalone player. Still it’s been fun testdriving the little boats.


water.blend (1.39 MB)

Nice!
looks good!

what module does it use that is not available in standalone?

Thanks. it doesn’t use any modules as far as I know.

@Dommomomo - this is fantastic. Hopefully I’ll get some time to play with it over winter break.

The standalone player: bpy is imported in water.py line 2. Fortunately it’s not used, so deleting that line fixes it.

EDIT
Runs great on my baseline laptop. Totally gonna use this. Thanks for the resource!

Awesome thanks for sharing

set to tap

every 2

very smooth still, yet gets rid of much of the logic.

Thanks matey, I guess .bpy counts as a module then.

Hey, do you think you could make this process half the water each frame? like every other somehow?

kinda like this

Attachments

DivideLoad.blend (625 KB)

It works really great for a grass too.

Just thought of something…

Hitting a single face initiates processing the water,
Each face has a table of neighbors, this way

Frame 1 = only first face processed

Frame 2 = face 1s neighbors processed

Frame 3 face 1s neighbor’s neighbors are processed

This way, the cpu usage would be cut down significantly,

Same for grass… Etc, a wave of change could propagate from one edge of a scene, across the scene.

Hi Blue Print.
It already spreads the updating over 6 frames, alternating between updating positions and normals. I gave up trying to spread it out further as the flicker gets too obvious (at least on my computer).
As far as your suggestion goes, I wouldn’t know how to implement it. This is my first go at Blender and Python.
if it helps, the script works by checking the relative displacement between neighbouring vertices and applies equal and opposite forces based on that displacement. I wouldn’t know how to go about telling it to check only displaced verts without checking to see which ones were displaced. or something… Still, it might be fun trying.

Grass eh?, Interesting, have you got a picture?

very cool, i optimized the first code , adding the normals … check if is enought fast 8)
( without (very poors) tricks with the frequency 8) )






own = bge.logic.getCurrentController().owner








if "run" not in own.attrDict:
    import random
    import mathutils
    R = random.random
    
    speed = 1.           #Set wave speed
    height = 100.0        #Set Wave Force
    
    verts = {}
    mesh = own.meshes[0]
    




    verts_in_poly = []
    for polyID in range(0, mesh.numPolygons):
        p = mesh.getPolygon(polyID)
        a1 = mesh.getVertex(0,p.v1)
        a2 = mesh.getVertex(0,p.v2)
        a3 = mesh.getVertex(0,p.v3)
        verts_in_poly.append([a1,a2,a3])
    
    mathutils_geometry_normal = mathutils.geometry.normal
    def run_verts_in_poly():
        for verts in verts_in_poly:
            poly_n = mathutils_geometry_normal(*[v.XYZ for v in verts])
            for v in verts: 
                v.setNormal(poly_n)
    


    for vert in [mesh.getVertex(0, vn) for vn in range(mesh.getVertexArrayLength(0))]:
        c = tuple(vert.XYZ)
        if c not in verts:
            frame = random.uniform(1,3)
            verts[c] = [[], frame, c[2]]
        verts[c][0].append(vert)
    
    waves = [verts[k] for k in verts]
    waves[0][1] = 1.0001
    
    def calc_h(frame):
        if frame  <= 2.0:                               # If frame is less than half of animation
            h = (frame **2.0 - 3.0*frame) + 2.0         # Formula for parabolic coordinate plotting by frame num
        else:
            frame = frame -1.0                          # Reset frame by half its animation
            h = -((frame **2.0 - 3.0*frame) + 2.0)      # INVERTED Formula for parabolic coordinate plotting by frame num   
        h = h * height/100    
        return h
    
        
    def run():
        if not "control" in own:
            own["control"] = [waves[0][1],]
            own["frames"] = []
            own["index"] = 0
        
        if len(own["control"])>=2:
            if own["index"]+1 >= len(own["frames"]):
                own["index"] = 0
            else:
                own["index"]+=1
            
            for vs in own["frames"][own["index"]]:
                fp,fn,p,n=vs
                fp(p)
                fn(n)
            return
        print(waves[0][1])
        if len(own["control"])==1:
            if waves[0][1] < own["control"][0]:
                own["control"].append(waves[0][1])
        elif len(own["control"])==2:
            if waves[0][1] > own["control"][0]:
                own["control"].append(waves[0][1])
                return
            
        for wave in waves:
            wave[1] += speed/height                            
            if wave[1] >= 3.0:                               
                wave[1] = 1.0  
            
            frame = wave[1]
            h = calc_h(frame)
            for vert in wave[0]:
                vert.z = wave[2] + h
        run_verts_in_poly()
        frameverts = []
        for wave in waves:
            for vert in wave[0]:
                frameverts.append([vert.setXYZ,vert.setNormal,vert.getXYZ(),vert.getNormal()])
        own["frames"].append(frameverts)


    own["run"] = run
own["run"]()



Not really, I just made a quick example with some grass planes. To give best results though you must rotate the grass sideways, give it apply rotation and then rotate it back to normal - that way the grass moves sideways, pretty good looking and realistic.