Water Simulation

20 May, 2014

Main Image

This application simulates water waves on a rectangular plane. This is done by deforming the vertices of the rectangular plane, made up of several polygons. The change in height is calculated and then compared to the previous iteration as well as the surrounding vertices.

Heightfield approximation is used with a simple wave equation. Simple contributing average is used for calculating vertex/face normals.

The following code demonstrates the deformation of the vertices on the plane:-

void WaterPlane::UpdateSimulation()
{
    const auto epsilon = 0.001f
    textureOffset.x += epsilon;
    textureOffset.y += epsilon;

    //initialize values with magic numbers
    //these numbers have been chosen since they
    //give the clearest results

    const auto deltaGrid = 1.0f;
    m_time = 0.16f;

    //Can be optimized out
    D3DXVECTOR3 originalPlane[SIZE][SIZE];

    const auto limit = (deltaGrid) / (waveSpeed * 1.4142f);

    if (m_time < 0.0f || m_time > limit)
        return;

    for (int i = 0; i < SIZE; ++i)
    {
        for (int j = 0; j < SIZE; ++j)
        {
            originalPlane[i][j] = m_plane[i][j];
        }
    }

    // Calculate prediction
    for (int i = 0; i < SIZE; ++i)
    {
        for (int j = 0; j < SIZE; ++j)
        {
            auto left = i - 1;
            auto right = i + 1;
            auto up = j - 1;
            auto down = j + 1;

            if (left < 0)
                left = 0;

            if (right > SIZE - 1)
                right = SIZE - 1;

            if (up < 0)
                up = 0;

            if (down > SIZE - 1)
                down = SIZE - 1;

            const auto prefix = ((m_time * m_time) * (waveSpeed *     
                waveSpeed)) / (deltaGrid * deltaGrid);

            auto xComponent = originalPlane[left][j].y - (2.0f * 
                originalPlane[i][j].y) + originalPlane[right][j].y;

            auto yComponent = originalPlane[i][up].y - (2.0f *  
                originalPlane[i][j].y) + originalPlane[i][down].y;

            auto zComponent = (2.0f * originalPlane[i][j].y) -  
                m_previousPlane[i][j].y;

            auto prediction = (prefix * xComponent) + (prefix * 
                yComponent) + zComponent;

            m_plane[i][j].y = pow(2.71f, -decay * m_time) *   
                prediction;
        }
    }

    // Get previous plane
    for (int i = 0; i < SIZE; ++i)
        for (int j = 0; j < SIZE; ++j)
            m_previousPlane[i][j] = originalPlane[i][j];

    CalculateNormals();
}