Simple ocean / water surface / waves

Previous topic - Next topic

FutureCow

After searching the web I found it very difficult to find a basic simulation of the oceans  surface / waves - there's plenty of complex fluid simulations, but what I wanted was effectively just a 3D sine wave. After a bit more searching for 3D sine wave algorithms and a bit of experimentation I've ended up with a modified series of a hyperbolic paraboloid (also known as a saddle surface - http://en.wikipedia.org/wiki/Paraboloid).

Forgive my code if there's a lot better / more efficient way to create/animate the 3D surface, I'm extremely new to 3D coding.

Changes to the code you may want to make :

* You can get slightly different wave shapes by changing the "-" in the formula to either a "+" or a "*"
* The higher the wave frequency variable the shorter the period of the sine wave shape.  Suggested figures 18,36,72. If you change to other numbers you will have to change the AnimationFrameMax variable as the start and end frames wont line up any more.
* The lower the SizeFactor variable the higher the amplitude of the sine wave.
* Comment out the "DRAWRECT" lines creating the water "texture" and use the LOADSPRITE instead to make it look a lot prettier.
* use some "X_MOVEMENT" and "X_DRAWOBJ" commands to increase the size of the ocean

I haven't gotten around to working out how to change the direction the waves are moving, but I'll leave that as an exercise for the reader  :P
Code (glbasic) Select
// Project: Ocean Surface
// Start: Monday, November 23, 2009
// IDE Version: 7.177
// Written by Shane Hockings

CONSTANT WaveAnimationFrameMax%=20                        // How many frames before we return to the start frame
CONSTANT GridSizeX%=40
CONSTANT GridSizeZ%=40
CONSTANT SizeFactor%=3                              // A lower number = higher sine wave amplitude
CONSTANT WaveFrequency%=36                        // Higher number = shorter sine wave period

GLOBAL HeightMap#[]                              // The array storing the heights for each square in our water surface
DIM HeightMap#[GridSizeX%][GridSizeZ%]

GLOBAL WaterTexture                              // Sprite for the texture
GLOBAL GlobalTimer


// ------------------------------------------------------------- //
// ---  MAIN  ---
// ------------------------------------------------------------- //
WaterTexture=GENSPRITE()                  // Reserve the sprite for the water texture
GOSUB CreateWaterTexture                  // Create/load the water texture

WHILE TRUE
    X_MAKE3D 1, 600, 45                     // Make 3D window
    X_CAMERA 0,10,-10, 10, 0, 10            // Set up the camera
    X_AMBIENT_LT 0, RGB(255,255,255)


   //timer = 100 ms
    UpdateGrid(200)                     // Create the waves

   
   X_SETTEXTURE WaterTexture,-1            // Give them texture
    X_DRAWOBJ 1,0   

//need this for right time, must be with showscreen
GlobalTimer=GETTIMER()
SHOWSCREEN                           // Display them on the screen
WEND


// ------------------------------------------------------------- //
// ---  UPDATEGRID  ---
// ------------------------------------------------------------- //
FUNCTION UpdateGrid: Timer_ms
   // These values are defined LOCAL:
// Counter%
   STATIC WaveTimer
   STATIC WaveAnimationFrame
   WaveTimer=WaveTimer+GlobalTimer
   IF WaveTimer>=Timer_ms THEN WaveTimer = 0
   IF WaveTimer<>0 THEN RETURN 0 //not ready to render yet=0

    WaveAnimationFrame=WaveAnimationFrame+1                 // Due to earlier return, this only executes when the timer >= Timer_ms
    IF WaveAnimationFrame=WaveAnimationFrameMax% THEN WaveAnimationFrame=0            // Reset animation

    LOCAL X, Z

    FOR Z=0 TO GridSizeZ-1                                             // For each grid location
        FOR X=0 TO GridSizeX-1
            HeightMap[X][Z]=(SIN((X+WaveAnimationFrame)*36)-SIN((Z+WaveAnimationFrame)*36))/SizeFactor
                                                                  // Formula for saddle surface : Z = x^2 - y^2
                                                                    // X and Z are points on a sine curve
        NEXT
    NEXT

    X_OBJSTART 1                                                   // Create 3D object number 1
    FOR Z=0 TO GridSizeZ-2
        X_OBJNEWGROUP                                                // Create a new group of polygons every row
        FOR X=0 TO (GridSizeX/2)-1                                       // As a trangle is drawn between column [X] and column [X+1] (ie. 2 columns)
                                                                 // we only need to loop through half of them
      X_OBJADDVERTEX   X,   HeightMap[X][Z+1],  Z+1,  0,0, RGB(0,0,255)         // Create quad
        X_OBJADDVERTEX   X,   HeightMap[X][Z],      Z,  0,1, RGB(0,0,255)
        X_OBJADDVERTEX   X+1, HeightMap[X+1][Z+1], Z+1, 1,0, RGB(0,0,255)
        X_OBJADDVERTEX   X+1, HeightMap[X+1][Z],    Z,  1,1, RGB(0,0,255)
        NEXT
    NEXT
    X_OBJEND                                                      // Finished 3D object number 1
   
RETURN 1 //an object was built =1

ENDFUNCTION // UPDATEGRID



// ------------------------------------------------------------- //
// ---  CREATEWATERTEXTURE  ---
// ------------------------------------------------------------- //
SUB CreateWaterTexture:
   
   // Either create a texture that's a white box with blue outline
   
   DRAWRECT 0,0,32,32,RGB(0,0,255)                        // Create a filled blue box
   DRAWRECT 1,1,30,30,RGB(255,255,255)                     // Create a filled white box just inside it
   GRABSPRITE WaterTexture,0,0,32,32                     // Grab the boxes into sprite "WaterTexture"

   // OR load a texture instead
   
   //LOADSPRITE "Water.png", WaterTexture                  // Load a texture for the water
ENDSUB // CREATEWATERTEXTURE



Modified to include changes from Hemlos

Hemlos

#1
AnimationFramea
caused error...needs to be AnimationFrame

That is really cool stuff man.

:good:

I altered your code so that the program can run full speed.
The difference is, i stored gettimer() value to time the ocean.
To use gettimer, you must have the gettimer right before showscreen,
AND sleep 1, MUST be right after showscreen.
If you dont take these steps, then the ocean will "jitter", as gettimer has some bug if you dont sleep 1 millisecond after showscreen.
I use the gettimer value inside the update function.
A second parameter to the function is added to set the milliseconds per frame.
This allows you to set the time for only the ocean, while the whole program can run max speed.


Code (glbasic) Select
// Project: Ocean Surface
// Start: Monday, November 23, 2009
// IDE Version: 7.177
// Written by Shane Hockings


GLOBAL GridSizeX%=40
GLOBAL GridSizeZ%=40
GLOBAL SizeFactor%=3 // A lower number = higher sine wave amplitude
GLOBAL WaveFrequency%=36 // Higher number = shorter sine wave period
GLOBAL AnimationFrameMax%=20 // How many frames before we return to the start frame

GLOBAL HeightMap#[] // The array storing the heights for each square in our water surface
DIM HeightMap#[GridSizeX%][GridSizeZ%]

GLOBAL WaterTexture // Sprite for the texture



// ------------------------------------------------------------- //
// ---  MAIN  ---
// ------------------------------------------------------------- //
LOCAL AnimationFrame = -1 // Initialise wave animation frame counter

WaterTexture=GENSPRITE() // Reserve the sprite for the water texture
GOSUB CreateWaterTexture // Create/load the water texture

WHILE TRUE
    AnimationFrame=AnimationFrame+1
    IF AnimationFrame=AnimationFrameMax% THEN AnimationFrame=0 // Reset animation

    X_MAKE3D 1, 600, 45 // Make 3D window
    X_CAMERA 0,10,-10, 10, 0, 10 // Set up the camera
    X_AMBIENT_LT 0, RGB(255,255,255)


//timer = 100 ms
    UpdateGrid(AnimationFrame, 200) // Create the waves


X_SETTEXTURE WaterTexture,-1 // Give them texture
    X_DRAWOBJ 1,0

//need this for right time, must be with showscreen
gTIMER=GETTIMER()
    SHOWSCREEN // Display them on the screen
   
    //i removed this:  SLEEP 200
    // must have sleep 1 for gettimer to work
   
    SLEEP 1 // Pace the animation
WEND


// ------------------------------------------------------------- //
// ---  UPDATEGRID  ---
// ------------------------------------------------------------- //
FUNCTION UpdateGrid: AnimationFrame, Timer_ms
// These values are defined LOCAL:
// Counter%
STATIC timer
timer=timer+gTIMER
IF timer>=Timer_ms THEN timer = 0
IF timer<>0 THEN RETURN 0 //not ready to render yet=0

    LOCAL X, Z

    FOR Z=0 TO GridSizeZ-1 // For each grid location
        FOR X=0 TO GridSizeX-1
            HeightMap[X][Z]=(SIN((X+AnimationFrame)*36)-SIN((Z+AnimationFrame)*36))/SizeFactor
            // Formula for saddle surface : Z = x^2 - y^2
            // X and Z are points on a sine curve
        NEXT
    NEXT

    X_OBJSTART 1 // Create 3D object number 1
    FOR Z=0 TO GridSizeZ-2
        X_OBJNEWGROUP // Create a new group of polygons every row
        FOR X=0 TO (GridSizeX/2)-1 // As a trangle is drawn between column [X] and column [X+1] (ie. 2 columns)
        // we only need to loop through half of them
X_OBJADDVERTEX   X,   HeightMap[X][Z+1],  Z+1,  0,0, RGB(0,0,255) // Create quad
        X_OBJADDVERTEX   X,   HeightMap[X][Z],      Z,  0,1, RGB(0,0,255)
        X_OBJADDVERTEX   X+1, HeightMap[X+1][Z+1], Z+1, 1,0, RGB(0,0,255)
        X_OBJADDVERTEX   X+1, HeightMap[X+1][Z],    Z,  1,1, RGB(0,0,255)
        NEXT
    NEXT
    X_OBJEND // Finished 3D object number 1
   
RETURN 1 //an object was built =1

ENDFUNCTION // UPDATEGRID



// ------------------------------------------------------------- //
// ---  CREATEWATERTEXTURE  ---
// ------------------------------------------------------------- //
SUB CreateWaterTexture:

// Either create a texture that's a white box with blue outline

DRAWRECT 0,0,32,32,RGB(0,0,255) // Create a filled blue box
DRAWRECT 1,1,30,30,RGB(255,255,255) // Create a filled white box just inside it
GRABSPRITE WaterTexture,0,0,32,32 // Grab the boxes into sprite "WaterTexture"

// OR load a texture instead

//LOADSPRITE "Water.png", WaterTexture // Load a texture for the water
ENDSUB // CREATEWATERTEXTURE
Bing ChatGpt is pretty smart :O

FutureCow

Awesome, thanks Hemlos. I changed the variable name in the post to make the code more readable - I obviously didn't try compiling it again after the change  :-[
I like your changes too - good work!

Hemlos

I added a grid function, in that other thread you started.
Bing ChatGpt is pretty smart :O

FutureCow

#4
Cheers buddy! I fixed that typo in the original post too just in case someone copies it rather than yours.

FutureCow

Hemlos, I've updated my code to reflect your changes and fix a small bug yours had. You were updating the animation frame once every game loop, but only drawing whatever frame was current after the timer delay (which meant frames were getting missed). I've fixed it so AnimationFrame only advances when the timer delay has passed (ie. when timer=0 from your code)

Hemlos

#6
Ahh cool, glad you caught that, it is your baby afterall :)
A shot in the dark here.....the twitching error i noticed was due to this which i missed.

Great work man, it works really good.




I assume we can remove SLEEP 1.....which is good because it will go from 60 fps up to 4000 fps.

When i said sleep 1 makes it run full speed, i was wrong, i noticed an error with glbasic and this.

So lets try to see what happens without it....
Bing ChatGpt is pretty smart :O

Hemlos

#7
Success, i got it to run full speed now....3300 FPS woo!
REMOVE from your main code SLEEP 1.

Also dont forget to move your globals and local global from the main code, into the function as locals instead.

GLOBAL AnimationFrameMax%=20               
LOCAL AnimationFrame% = -1

These should be added to the function STATIC
STATIC AnimationFrameMax,AnimationFrame
Bing ChatGpt is pretty smart :O

Qedo

I am trying the Ocean Surface and am getting only 4 FPS (in the best condition).
I read in the topic of 3300 FPS!!!!
Is there something wrong on my computer or 3300 is exhausted?
I miss something?
Someone can try?
Thank you