Realtime Mesh Water Waves(No animations required)

Hey guys,

I made this water wave script, that works really well. The reason why it doesn’t lag is because ive stored all the vertexes in a library that makes it easy for blender to access.

Its just one script, with no properties, no animations, nothing!.

Just connect it to an always sensor set to true and attach it to the script and VOILA water! :slight_smile:

Heres the blend file.

Wave Script3.blend (552 KB)

NOTE: Ive changed the script a bit so that you can modify the speed of the animation, height of waves, and now you can pretty much apply the script to anything. Cheers :slight_smile: XD

2 Likes

Corey you are a genius, thanks alot!

HAHAHAH! I wish I was XD, I updated the script, you can play with the settings now.

yea… this is AWESOME corey! Thank you for commenting your code so well also, really appreciate that.

This is pretty cool, will it work with on a sphere ocean or just planes? I want to try use it in my project here.

This is amazing! just wondering say i wanted to use this for a river/stream would i be able to give the script a direction?
Also what license are you releasing it under?
Great work though, very cool!

Capt.A, I did a quick test with my sphere setup and can confirm the vertex displacement occurs along the z-axis relative to it’s previous height… not the vertex position relative to the centre of the mesh. See line 61:

new = [x, y, value[2] + h]#Store New coordinate for vertex relative to default height

It’s very good, but it doesn’t recalculate the vertex normals, so shading could be a problem.
I think it is possible to do a calculation to give correct normals to each vert, but it will slow down the script somewhat.

It seems like I will have to figure a way to cause the displacement along the planets gravity vector for it to work in my project. Can you think of any reason why it wouldn’t work as is with Martin’s water shader?

Hahah thank you! Umm It would be really difficult for me to figure out how to do that but, if you want the water to move in a direction, you can modify the UV placement of the texture in game with another python script. That would create the illusion that it was moving in a direction.

Lol its a resource just use it, change it, IDC modify it. Its yours now

add this line at the end of the script
own.reinstancePhysicsMesh()

this will recalculate the mesh normals

ReinstancePhysicsMesh will refresh the physical mesh, but it won’t do anything about the visual normals of the object.


The image above shows your script at the top. This is in GLSL mode with a sun type lamp shining on the mesh. You can see that there is no shading, the plane is still being shaded as a flat plane, all the normals are straight up i.e. (0.0,0.0,1.0).
In the bottom image I’ve recalculated the normals using mathutils.geometry.normal() and vert.setNormal()

Here’s a quick running version of the script I used:

            
import mathutils

mesh = own.meshes[0]

number = mesh.numPolygons

for polyID in range(0,number):
    p = mesh.getPolygon(polyID)
    
    a1=mesh.getVertex(0,p.v1)
    b1=a1.getXYZ()
    a2=mesh.getVertex(0,p.v2)
    b2=a2.getXYZ()
    a3=mesh.getVertex(0,p.v3)
    b3=a3.getXYZ()
                        
    poly_n = mathutils.geometry.normal(b1,b2,b3)
    
    v_list = [a1,a2,a3]
       
    for v in v_list:    
                
        v.setNormal(poly_n)       

Just apend this to the end of your script to get it working. You should be able to intergrate it in to your script more cleanly and get a better frame rate.

For my own project I’m using a more complex script that adds all the normal data from the shared polys of a vertex to a dictionary and averages them. It gives a slightly cleaner finish, but is too slow for a frame by frame application. I use it once at the start when i generate my random terrain.

Of course you don’t need this for your games, you could work with a shadeless plane, perhaps shading the verts based on height of the wave, or just relying on the distortion of the texture to give the apearance of natural waves.

Hi.

I know this thread is a month old, but I recently downloaded this file and I have to say, this is a pretty good way of making waves in a water mesh.

The only thing for me is that it takes up quite a few logic cycles when you start doing the normal recalculation as well, so I modified the script to utilize a classic optimization technique that uses timer properties to execute script parts once every N frames instead of every frame (the edited code also cleans up what I perceive as excessive use of whitespace)


import mathutils
gl = bge.logic#Import Logic as gl

cont = gl.getCurrentController()#Get current controller
own = cont.owner#Get owner
mesh = own.meshes[0]
number = mesh.numPolygons

speed = 1*3#Set wave speed
height = 50#Set Wave Force

if own['Tic2'] <= 0:
    if "Vertex" not in own.attrDict:#Run Once
    
        import random#Import Random
    
        own.attrDict["Vertex"] = {}#Create Vertex Dict
        own.attrDict["Frame"] = 1#Create Frame Variable at frame 1
    
        for i in range(mesh.getVertexArrayLength(0)):#For each vertex index
            
            vertex = mesh.getVertex(0, i)#Get vertex By index
            x = vertex.XYZ[0]#get Vertex X Coordinate
            y = vertex.XYZ[1]#get Vertex Y Coordinate
            frame = random.randint(100,300)/100.0#Get a random frame from 1.0 - 3.0
            
            if str([x, y]) not in own.attrDict["Vertex"]:#If Coordinate not in vertex library
                own.attrDict["Vertex"][str([x, y])] = [[vertex],frame, vertex.XYZ[2]]#Add Coordinate Vertex with random frame number, store vertex default height
                
            else:
                own.attrDict["Vertex"][str([x, y])][0].append(vertex)#Add secondary coordinate to vertrex list NOTE: There can be two vertex in exactly one point
                
    for coor in own.attrDict["Vertex"]:#For Corrdinate in vertex library
        value = own.attrDict["Vertex"][str(coor)]#Create variable for ease of access
        
        for vertex in value[0]:#For vertex in vertex list
            frame = value[1]#Get frame
        
            if frame  <= 2:#If frame is less than half of animation
                h = (frame **2 - 3*frame) + 2#Formula for parabolic coordinate plotting by frame num
            else:#Else
                frame = frame -1#Reset frame by half its animation
                h = -((frame **2 - 3*frame) + 2)#INVERTED Formula for parabolic coordinate plotting by frame num   
                
            h = h * height/100#Modify height of waves
            value[1] += speed/100#Add for the next frame by speed
            x = vertex.XYZ[0]#Get vertex X coor
            y = vertex.XYZ[1]#Get vertex Y coor
            new = [x, y, value[2] + h]#Store New coordinate for vertex relative to default height
            vertex.XYZ = new#Apply New coordinate for vertex
        
            if value[1] >= 3:#If end of frame
                value[1] = 1#Restart frame
            
        own['Tic2'] = 3
else:
    own['Tic2'] -= 1
    
if own['Tic'] <= 0:
    for polyID in range(0,number):
        p = mesh.getPolygon(polyID)
        
        a1=mesh.getVertex(0,p.v1)
        b1=a1.getXYZ()
        a2=mesh.getVertex(0,p.v2)
        b2=a2.getXYZ()
        a3=mesh.getVertex(0,p.v3)
        b3=a3.getXYZ()
                            
        poly_n = mathutils.geometry.normal(b1,b2,b3)
        
        v_list = [a1,a2,a3]
           
        for v in v_list:    
                    
            v.setNormal(poly_n)
    own['Tic'] = 5
else:
    own['Tic'] -= 1

To make it work, take the water plane and add two integer properties (Tic and Tic2), what these properties do is basically reduce the update frequency of the vertex movement from 60 times to 20 times per second and the normal recalculation reduced to 12 times per second. On my machine, this meant a drop in the number of milliseconds for the logic from 8.5 to 3 (around a 60 percent drop). Unless the vertices are moving really fast, you will simply not notice that the logic isn’t being run on every frame.

Also notice that there’s a multiplier for the wave speed, this is so we can compensate for the more infrequent updating because we would otherwise see the waves moving a bit slower.

Ive experimented with the BGE lag and it doesn’t matter how many frames per second you may change it to. The script will always absolutely need to calculate the same amount of vertices at one frame. So you should probably try to change the number of vertices calculations, not how many frames delay it run.

I’ve been working with using smaller patches of terrain instead of one big mesh. Recalculating normals allows all the patches to look like they are joined, there are no visible seams, however because only a few patches are in the view fulstrum at any one time only part of the terrain gets rendered greatly reducing the drain on redering resources.

If you did the same thing with water, spliting it in to patches and then calculating waves for a single patch (as long as it’s tile-able) you could choose to only update the verts for patches which are in the view fulstrum (there’s actually some code to check that). This would greatly reduce the required calulations.

It would work well with this code I think as the waves are uniform, so they should be able to be rewritten to tile quite easily.

I don’t have time to do that myself right now, but maybe someone else would like to look at it. You’ll have to be more thorough with calculating normals as adjacent patches will share vert locations so to make the patches seamless you’ll have to add all the normals from each poly which surrounds a vertex and then divide the normal by the number of polys. You’ll also have to consider that some polys have three verts while others may have 4 (I don’t know why exactly but it’s somethig that came up in my own experiements with terrain).

Ya I don’t know why theres more than 1 vert, even if they are merged., this is why I call up each vertex by vertex position or coordinate. Each point has its own library of vertexes.

Just want to say that I really appreciate this thead.
I’ve used several of the ideas from here as well as some of my own and come up with this.


Attachments

waves.blend (849 KB)

Thats absolutely amazing Dommomomo! It’s a shame its so harsh on the logic processing though. :frowning:
I’m currently researching how to run bpy in a separate instance on a local server to allow for pseudo-threading. I will be using this as a benchmark for once I get things working. :slight_smile:

Nice!!

Can you do this to a sphere?

Can you increase the “dampening” ?