void ImageHeightDataSource::fillHeightData( GeometryClipmapLevel& level, const Rectangle2i& targetRect ) const
	{
		// check validity of the input data:
		assert( targetRect.getWidth() > 0 );
		assert( targetRect.getHeight() > 0 );

		// todo: clip data to the existing data: (saves per pixel clipping)		

		// iterate over the target pixels and fill them with the image height data:
		Pnt2i samplePos;

		for( int y = targetRect.y0; y < targetRect.y1; ++y )
		{
			float* targetPtr = &level.heightmap.samples[ y * level.heightmap.size ];

			for( int x = targetRect.x0; x < targetRect.x1; ++x )
			{
				// todo: make this incremental:
				samplePos = level.blockPosToSamplePos( Pnt2i( x, y ) );

				targetPtr[ x ] = heightScale_ * getHeightSample( samplePos );					
			}			
		}
	}
void CpuClipmapRenderer::onBuildVertices( GeometryClipmapLevel& level, const GeometryClipmapLevel* coarserLevel, const Rectangle2i& blockRect )
{
    const int levelSampleCount = level.heightmap.size;

    TerrainLevelRenderData& levelRenderData = levels_[ level.index ];

    // loop over the changed vertices:
    Pnt2i samplePos;
    Pnt2f worldPos;

    const Pnt2i worldSampleCount = geoClipmaps_->getWorldSampleCount();

    for( int y = blockRect._y0; y < blockRect._y1; ++y )
    {
        const int index = y * levelSampleCount + blockRect._x0;
        const float* samplePtr = &level.heightmap.samples[ index ];
        OpenGLTerrainVertex* vertexPtr = &levelRenderData.vertices[ index ];

        for( int x = blockRect._x0; x < blockRect._x1; ++x )
        {
            // todo: make this incremental
            samplePos = level.blockPosToSamplePos( Pnt2i( x, y ) );

            vertexPtr->pos[ 0 ] = samplePos[ 0 ];
            vertexPtr->pos[ 1 ] = *samplePtr;
            vertexPtr->pos[ 2 ] = samplePos[ 1 ];
            vertexPtr->pos[ 3 ] = *samplePtr;

            // todo: correct uv coordinates:
            vertexPtr->uv.setValues( float(samplePos[ 0 ]) /
                                     float(worldSampleCount[ 0 ]),
                                     float(samplePos[ 1 ]) /
                                     float(worldSampleCount[ 1 ]) );
            //vertexPtr->uv.setValues( float( x ) / float( levelSampleCount - 1 ), float( y ) / float( levelSampleCount - 1 ) );

            samplePtr++;
            vertexPtr++;
        }
    }

    if( coarserLevel )
    {
        // todo: fill in the interpolated positions of the coarser level:
        const int coarserLevelSpacing = coarserLevel->sampleSpacing;
#ifdef OSG_DEBUG
        const int coarserLevelSampleCount = coarserLevel->heightmap.size;
#endif

        // todo: make this faster
        for( int y = blockRect._y0; y < blockRect._y1; ++y )
        {
            const int index = y * levelSampleCount + blockRect._x0;
            OpenGLTerrainVertex* vertexPtr = &levelRenderData.vertices[ index ];

            for( int x = blockRect._x0; x < blockRect._x1; ++x, ++vertexPtr )
            {
                // todo: make this incremental
                samplePos = level.blockPosToSamplePos( Pnt2i( x, y ) );
                Pnt2i blendWeights = componentModulo( samplePos, coarserLevelSpacing );

                if( blendWeights[ 0 ] == 0 && blendWeights[ 1 ] == 0 )
                {
                    continue;
                }

                Pnt2i coarserSamplePos = samplePos - blendWeights;

                Pnt2i coarserSamplePos10 = componentAdd( coarserSamplePos, Pnt2i( coarserLevelSpacing, 0 ) );
                Pnt2i coarserSamplePos01 = componentAdd( coarserSamplePos, Pnt2i( 0, coarserLevelSpacing ) );

                // todo: clip on a higher level (not per pixel)
                if( !coarserLevel->containsSample( coarserSamplePos ) ||
                        !coarserLevel->containsSample( coarserSamplePos10 ) ||
                        !coarserLevel->containsSample( coarserSamplePos01 ) )
                {
                    // no data}
                    continue;
                }

                Pnt2i coarserBlockPos = coarserLevel->samplePosToBlockPos( coarserSamplePos );

#ifdef OSG_DEBUG
                assert( coarserBlockPos[ 0 ] < coarserLevelSampleCount && coarserBlockPos[ 1 ] < coarserLevelSampleCount );
#endif

                Pnt2i coarserBlockPos10 = coarserLevel->samplePosToBlockPos( coarserSamplePos10 );
                Pnt2i coarserBlockPos01 = coarserLevel->samplePosToBlockPos( coarserSamplePos01 );

                if( ( blendWeights[ 0 ] != 0 ) && ( blendWeights[ 1 ] != 0 ) )
                {
                    // todo: is this case important?
                }
                else if( blendWeights[ 0 ] != 0 )
                {
                    const float coarserSample00 = coarserLevel->getBlockSample( coarserBlockPos );
                    const float coarserSample10 = coarserLevel->getBlockSample( coarserBlockPos10 );

                    vertexPtr->pos[ 3 ] = ( coarserSample00 + coarserSample10 ) / 2.0f;
                }
                else if( blendWeights[ 1 ] != 0 )
                {
                    const float coarserSample00 = coarserLevel->getBlockSample( coarserBlockPos );
                    const float coarserSample01 = coarserLevel->getBlockSample( coarserBlockPos01 );

                    vertexPtr->pos[ 3 ] = ( coarserSample00 + coarserSample01 ) / 2.0f;
                }
            }
        }
    }

    if( levelRenderData.vertexBuffer.isValid() )
    {
        // todo: upload only the changed data rect:
//        const int vertexCount = levelRenderData.vertices.size();

        //std::cerr << "Uploading Vbo Data " << blockRect << std::endl;

        const int vboLineLength = sizeof( OpenGLTerrainVertex ) * blockRect.getWidth();
        for( int y = blockRect._y0; y < blockRect._y1; ++y )
        {
            const int index = y * levelSampleCount + blockRect._x0;
            const int vboOffset = index * sizeof( OpenGLTerrainVertex );

            levelRenderData.vertexBuffer.uploadData( &levelRenderData.vertices[ index ], vboOffset, vboLineLength );
        }

        // testing: upload the complete vbo:
        //std::cerr << "Uploading VBO Data" << std::endl;
        //levelRenderData.vertexBuffer.uploadData( &levelRenderData.vertices[ 0 ], 0, sizeof( OpenGLTerrainVertex ) * vertexCount );
        //checkVboConsistency( levelRenderData, levelSampleCount );
        levelRenderData.vertexBuffer.deactivate();
    }
}