Ogre Compatible HeightMap

From Ogre Wiki

Jump to: navigation, search

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)


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;
};
Personal tools
administration