Ogre Compatible HeightMap         Get height of terrain at (x,z) positions on a server without firing up Ogre
Print

I've noticed a few posts around about people using Ogre on their headless server platforms with a null renderer because they need heightmap support that is compatible with ogre. I was guilty of this as well, but I finally did some research and created a heightmap system that works the same as Ogre's. Well, not exactly the same, but it supports the feature that I needed, namely getHeightAt(x,z). This is not necessarily a drop in replacement, but it should get anyone interested in such a thing a real head start.

Note: The current Ogre version also has the method Ogre::TerrainSceneManager::getHeightAt(x,y)(external link)

According to my tests the following code works "identically" to the Ogre 1.4.5.

#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>
#include <iomanip>
 
class HeightMap
{
public:
    HeightMap(int width, float widthScale, float heightScale,
             const std::string& rawFilename, bool is16bit = true)
        : m_Width(width), m_WidthScale(widthScale), m_HeightScale(heightScale)
    {
        std::ifstream ifs;
 
        // open the file (should add some error checking here)
        ifs.open(rawFilename.c_str(), std::ios::binary);
 
        // create the new vertex array
        m_Vertices = new float[m_Width * m_Width];
 
        unsigned short tmp = 0;
        float invScale = 1.0f / 65535.0f;
 
        // loop through and populate the vertices
        for(int y = 0; y < m_Width; ++y)
        {
            for(int x = 0; x < m_Width; ++x)
            {
                // get first byte
                tmp = ifs.get();
                // if its 16 bit, get the second
                if(is16bit)
                    tmp += ifs.get() << 8;
 
                // add the current value to the correct position
                m_Vertices[y*m_Width + x] = tmp * invScale;
            }
        }
 
        ifs.close();
    }//end HeightMap()
 
    ~HeightMap() 
    {
        delete[] m_Vertices;
    }//end ~HeightMap
 
    /**
     * Print the height map to standard output, useful for debugging
     */
    void printHeightMap()
    {
        // now print out the heightmap
        for(int y = 0; y < m_Width; ++y)
        {
            for(int x = 0; x < m_Width; ++x)
            {
                std::cout << std::setw(2) << 
                    static_cast<int>(m_Vertices[y*m_Width + x] * heightScale)<< ",";
            }
            std::cout << std::endl;
        }
    }//end printHeightMap
 
    /**
     * This function is merely a modified form of the function
     * of the same name in TerrainRenderable.
     */
    float getHeightAt(float x, float z)
    {
        float start_x = 0;
        float start_y = indexHeight(0,0);
        float start_z = 0;
        float end_x = m_Width*m_WidthScale/4;
        float end_y = indexHeight(m_Width,m_Width);
        float end_z = m_Width*m_WidthScale/4;
 
        float x_pct = ( x - start_x ) / ( end_x - start_x );
        float z_pct = ( z - start_z ) / ( end_z - start_z );
 
        float x_pt = x_pct * ( float ) ( m_Width);
        float z_pt = z_pct * ( float ) ( m_Width);
 
        int x_index = ( int ) x_pt;
        int z_index = ( int ) z_pt;
 
        // If we got to the far right / bottom edge, move one back
        if (x_index == m_Width)
        {
            --x_index;
            x_pct = 1.0f;
        }
        else
        {
            // get remainder
            x_pct = x_pt - x_index;
        }
        if (z_index == m_Width)
        {
            --z_index;
            z_pct = 1.0f;
        }
        else
        {
            z_pct = z_pt - z_index;
        }
 
        //bilinear interpolate to find the height.
        float t1 = indexHeight( x_index, z_index);
        float t2 = indexHeight( x_index + 1, z_index);
        float b1 = indexHeight( x_index, z_index + 1);
        float b2 = indexHeight( x_index + 1, z_index + 1);
 
        float midpoint = (b1 + t2) / 2.0;
 
        if (x_pct + z_pct <= 1) {
            b2 = midpoint + (midpoint - t1);
        } else {
            t1 = midpoint + (midpoint - b2);
        }
 
        float t = ( t1 * ( 1 - x_pct ) ) + ( t2 * ( x_pct ) );
        float b = ( b1 * ( 1 - x_pct ) ) + ( b2 * ( x_pct ) );
 
        float h = ( t * ( 1 - z_pct ) ) + ( b * ( z_pct ) );
 
        return h * m_HeightScale;
    }//end getHeightAt
 
private:
    /**
     * Simple function for pulling out a value from the array
     */
    inline float indexHeight(int x, int y)
    {
        return m_Vertices[m_Width * y + x];
    }//end indexHeight
 
private:
    float* m_Vertices;
    int m_Width;
    float m_WidthScale;
    float m_HeightScale;
};

Contributors to this page: jacmoe171980 points  and Beauty13675 points  .
Page last modified on Sunday 03 of January, 2010 00:33:04 UTC by jacmoe171980 points .


The content on this page is licensed under the terms of the Creative Commons Attribution-ShareAlike License.
As an exception, any source code contributed within the content is released into the Public Domain.