Codesnippets > 3D-snippets

Simple ocean / water surface / waves

(1/2) > >>

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 ---// 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],      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&#39;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"

ENDSUB // CREATEWATERTEXTURE

Modified to include changes from Hemlos

Hemlos:
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 ---// 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],      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&#39;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"

ENDSUB // CREATEWATERTEXTURE

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: