I’ve got my heightMapLoad function working the way I want it side from how fast it is. As of now it takes around 30 seconds to load a 2048x2048 greyscale image. Any tips as to how I could improve the speed of this loader…
bool loadHeightMap(const char* fileName, std::vector<float>& heightMap,
std::vector<unsigned int>& index, float tileSize, float maxHeight)
{
//Load image with SDL
SDL_Surface* img = SDL_LoadBMP(fileName);
if(!img)
{
std::cout<<"Failed to load " << fileName << "." << std::endl;
return false;
}
//Store height values in heights vector.
std::vector<float> tmp;
std::vector<std::vector<float>> heights;
for(int i = 0; i < img->h; i++)
{
tmp.clear();
for(int j = 0; j < img->w; j++)
{
Uint32 pixel = ((Uint32*)img->pixels)[i * img->pitch / 4 + j];
Uint8 r, g, b;
SDL_GetRGB(pixel, img->format, &r, &g, &b);
tmp.push_back((float)r/255.0);
}
heights.push_back(tmp);
}
//Fill the vertex data and index data.
for(int z = 0; z < img->h; z++)
{
for(int x = 0; x < img->w; x++)
{
heightMap.push_back((float)x * tileSize);
heightMap.push_back((float)heights[x][z] * maxHeight);
heightMap.push_back((float)z * tileSize);
heightMap.push_back((float)heights[x][z]);
heightMap.push_back((float)heights[x][z]);
heightMap.push_back((float)heights[x][z]);
if(x < img->w-1 && z < img->h-1)
{
int top = z * img->w + x;
int bottom = (z+1)*img->w + x;
index.push_back(top);
index.push_back(bottom);
index.push_back(top+1);
index.push_back(top+1);
index.push_back(bottom);
index.push_back(bottom+1);
}
}
}
SDL_FreeSurface(img);
if(heights.empty())
{
std::cout<< "Failed to get height data." << std::endl;
return false;
}
if(index.empty())
{
std::cout<< "Failed get index data." << std::endl;
return false;
}
if(heightMap.empty())
{
std::cout<< "Failed to get vertex data." << std::endl;
return false;
}
return true;
}
edit: A small question that’s been bugging me I just haven’t search much for an answer yet… Is there some kind of default max view distance in opengl? I know when I load this 2048x2048 heightmap I cannot see the whole thing as the view is cut out at a far distance.
[ol]
[li] Force SDL to provide the data in a known format so that you can omit the SDL_GetRGB() calls and access the data directly.
[/li][li] Generate the vertices as you read the pixels rather than using two sets of loops.
[/li][li] Use the .reserve() method to pre-allocate the vector’s storage.
[/li][/ol]
The maximum distance is set by the last argument (zFar or farVal) to a gluPerspective(), glOrtho() or glFrustum() call.
Setting this value too low will clip geometry which should be visible. Setting it to high (or, for a perspective projection, setting the near-plane distance too low) will reduce the precision of the depth buffer.
Ah yes I had ment to combine them loop and forgot before I posted this. I will look into the .reserve. I’ve never used it but seems simple enough… Although after I combine the loop it will only use the vectors I pass by reference. Would that stillbe a good idea?
I’m got it down to about 15 seconds to fully load the height map. Any other ideas how I could possibly improve it’s speed?
On a side note the speed seemed to be the same using SDL_GetRGB or the method I use now to just access the red color from the format. I tried with and without the SDL_LockSurface(img); with no change in speed.
edit: I changed this again. I wanted to get rid of all the type casting and I changed r/255.0 so it’s only done once.
bool loadHeightMap(const char* fileName, std::vector<float>& heightMap,
std::vector<unsigned int>& index, float tileSize, float maxHeight)
{
//Load image with SDL
SDL_Surface* img = SDL_LoadBMP(fileName);
if(!img)
{
std::cout<<"Failed to load " << fileName << "." << std::endl;
return false;
}
heightMap.reserve((img->h * img->w) * 6); //This is only 6 because I'm adding color into the vector.
index.reserve(((img->h-1) * (img->w-1)) * 6);
SDL_PixelFormat* fmt = img->format;
Uint32 temp, pixel;
float r;
SDL_LockSurface(img);
for(int z = 0; z < img->h; z++)
{
for(int x = 0; x < img->w; x++)
{
pixel = ((Uint32*)img->pixels)[z * img->pitch / 4 + x];
temp = pixel & fmt->Rmask;
temp = temp >> fmt->Rshift;
temp = temp << fmt->Rloss;
r = (Uint8)temp/255.0;
/*Uint32 pixel = ((Uint32*)img->pixels)[z * img->pitch / 4 + x];
Uint8 r, g, b;
SDL_GetRGB(pixel, img->format, &r, &g, &b);*/
heightMap.push_back(x * tileSize);
heightMap.push_back(r * maxHeight);
heightMap.push_back(z * tileSize);
heightMap.push_back(r);
heightMap.push_back(r);
heightMap.push_back(r);
if(x < img->w-1 && z < img->h-1)
{
int top = z * img->w + x;
int bottom = (z+1)*img->w + x;
index.push_back(top);
index.push_back(bottom);
index.push_back(top+1);
index.push_back(top+1);
index.push_back(bottom);
index.push_back(bottom+1);
}
}
}
SDL_FreeSurface(img);
if(index.empty())
{
std::cout<< "Failed get index data." << std::endl;
return false;
}
if(heightMap.empty())
{
std::cout<< "Failed to get vertex data." << std::endl;
return false;
}
return true;
}
Is that a debug or release build? There can be a lot of difference between the two when using templates. But even without optimisations, 15 seconds seems ridiculously slow.
Try to force SDL to provide a specific format so that you can avoid the shift/mask operations. You should be able to just read every third or fourth byte directly. Or skip SDL altogether and just read an 8-bpp BMP (or PGM) file directly.
Another possibility is to replace the .reserve() call with .resize() and copy directly into heightMap[n++] rather than using .push_back().
Edit: The loaders takes about 2 seconds to load in release mode, I guess that’s ok.
I’ve got several of the functions made in a vector class but what’s giving me more trouble is something a lot more simple… -.-
I need to fill vec3 objects with triangles so I figured it’d be easy to use the heightMap vertex data + my index to recreate it. I wish I could think of a way to do this as I fill the vertex but it seems full triangles are a must.
This just doesn’t work right at all… still in the process of trying to figure it out though.
void heightmap::computeNormals()
{
vector3d vec;
for(int i = 0; i < index.size()-3; i++)
{
std::cout<< heightMap[index[i]] << " " << heightMap[index[i+1]] << " " << heightMap[index[i+2]] << "
";
//I'd just fill the vec3 with all the points in order so I could normalize them...
}
}
vector3d vector3d::crossProduct(const vector3d& vec2)
{
return (vector3d(y*vec2.z-z*vec2.y, x*vec2.z-z*vec2.x, x*vec2.y-y*vec2.x));
}
void vector3d::normalize()
{
float len = length();
if(len != 0)
{
x /= len;
y /= len;
z /= len;
}
}