A helping hand for bedroom coders throughout the land.
Battle of Trafalgar for Windows Phone 7 - Part 3, Creating an ocean effect

With simple BasicEffect rendering of the ships with a skybox in the background, the next thing we need is the sea.

My first approach was only ever intended as a placeholder, but it actually worked surprisingly well. Hopefully most of you will be aware of the Reach DualTextureEffect - I have blogged about this previously here, but in the context of baking light maps into your level scenery models. However this effect is also really useful for masking the repeating patterns that are normally an obvious sideffect of tiling textures across a terrain. With this in mind I created a really simple mesh in XSI Mod Tool with the usual repeating tileable 'sea' texture. Then I added a second set of texture coords that spanned the entire model.

Being a slightly lazy cove, I reused the same sea texture image for both, and the result in-situ looks like this:

http://www.youtube.com/v/3NiWKxe7pp8 <p><a href="http://www.youtube.com/v/3NiWKxe7pp8">http://www.youtube.com/v/3NiWKxe7pp8</a></p>

I quite liked this, but it is a bit static. Once again I can apply a shader technique, but instead on the CPU using the game drawable, to create a bit of animation by tweaking the properties of the vertices between draw calls. There are 3 steps to this:

  • Gain access the vertex data from the model
  • Cycle through the vertices during update and alter the vertex property values.
  • Set the vertex data back into the model

The first part is dealt with in this code snippet:

public override void Initialize()
{
    // Must initialise my base class first in order to load the source model
    base.Initialize();
 
    // This class only supports a single mesh with one mesh part
    System.Diagnostics.Debug.Assert(m_model.Meshes.Count == 1);
    System.Diagnostics.Debug.Assert(m_model.Meshes[0].MeshParts.Count == 1);
 
    ModelMesh mesh = m_model.Meshes[0];
    ModelMeshPart part = mesh.MeshParts[0];
 
    // Work out how many floats in this model's vertices
    int data_size = part.VertexBuffer.VertexDeclaration.VertexStride;
    m_floats_per_vertex = data_size / sizeof(float);
 
    // Cache the vertex count & total number of floats as these will
    // needed repeatedly in the update cycle
    m_vertex_count = part.VertexBuffer.VertexCount;
    m_total_number_of_floats = m_vertex_count * m_floats_per_vertex;
 
    // Allocate internal cache of vertex data
    m_vbData = new float[m_total_number_of_floats];
 
    // Fill the cache from the part's vertexbuffer
    part.VertexBuffer.GetData<float>(m_vbData);
}

and the second part here:

public override void Update(GameTime gameTime)
{
    float wave_offset;
 
    // Iterate through the cached vertices 
    for (int i = 0; i < m_total_number_of_floats; i += m_floats_per_vertex)
    {
        // Calculate the wave offset using the vertex X position & the time
        wave_offset = (float)(Math.Sin(m_vbData[i] + gameTime.TotalGameTime.TotalSeconds));
 
        // Offset the X texture coords for both textures, 
        // but in opposite directions & with different scaling
        m_vbData[i + 7] -= wave_offset * 0.0002f;
        m_vbData[i + 8] -= wave_offset * 0.0001f;
        m_vbData[i + 9] += wave_offset * 0.00001f;
    }
    base.Update(gameTime);
}

Setting the data is done like this:

public override void Draw(GameTime gameTime)
{
    // Set the vertex data into the MeshPart before
    // rendering
    ModelMesh mesh = m_model.Meshes[0];
    ModelMeshPart part = mesh.MeshParts[0];
    part.VertexBuffer.SetData<float>(m_vbData);
 
    base.Draw(gameTime);
}

The result is subtle sway of the ocean textures and can be seen here:

Finally, if you can tweak the vertex texture coordinates, why not the vertex positions, and reflect the sky for good measure? Charles Humphrey (aka NemoKrad) provided the initial code for this following too.

The nice thing about the DrawableGameComponent architecture is that each element is largely decoupled so I can pick and choose the elements I want to try by simply adding or removing them from the components collection. At the moment I'm using the tex coord animated version, but who knows...


Posted Thu, Mar 17 2011 3:30 PM by Edward Powell