Page 1 of 3
Generating terrain from a plain c++ array
Posted: Sat Apr 17, 2010 3:48 pm
by Alastriona
Hello,
I've been messing around with procedural terrain generation (using fractals) the last few weeks. My algortihm basicly creates a heightmap in a plain (2D) array of chars.
The array looks like this:
Code: Select all
char[height*width] terrain = new char[height*width];
Now I have code that will save this to an image. Which will allow me to load it as a terrain scene node into irrlicht.
The problem I'm having with this is that its kind of overkill to save to an image and then load it into Irrlicht. What I would like is to use my 2D array directly to provide height values for Irrlicht to use. Since my array is basicly representing a 8-bit grayscale image I'm pretty sure this should be possible.
My questions are:
- Has anyone ever done something similar, and is this code freely available?
- If not, do you guys think it would be hard to implement into Irrlicht?
P.S. I hope the description of my situation is sufficient. I didn't wanna end up with a huge wall of text. Feel free to ask for clarification if needed.
Posted: Sat Apr 17, 2010 5:49 pm
by bonsalty
You can get the mesh from ITerrainSceneNode and modify it. To bad there is no initialiser for the size. To bad: if a texture is used for heightmap the mesh might have a different size
. For instance if your patchsize is 65, your terrain will have the the size of n*64. This is a headache, cos you have to interpolate one point of your grid to more then one points.
What I do is generating a grayscale image. I found, that irrlicht handles r,g,b channels with different weights. Actually you can have ~2500 levels instead of the grayscale version, which supports 255. So I simple interpolate heights to colors. Then I resize the image to n*patchsize. Then I make an Iimage of it in memory and load it.
Huh, a long story indeed. It was often repeated to me: "You can simple make your own terrain of triangles in a custom node. " But whats the point of that? If you have grids larger then 500*500 the whole project will slow down. Thats why IterrainSceneNode is great. Another prise Im dealing with is how to remove the node properly.
Posted: Sat Apr 17, 2010 6:49 pm
by Alastriona
Thanks for the response bonsalty. I think I get most of what you mean. I'm guessing this means that I will have to overload the loadHeightMap() function to take an Iimage and then construct an IterrainSceneNode manually and call my overloaded loadheightmap function?
Browsing through documentation and source I don't see how I could generate a Heightmap from an Iimage without modifying the source. Is this correct?
Posted: Sat Apr 17, 2010 7:21 pm
by Alastriona
Alastriona wrote:Thanks for the response bonsalty. I think I get most of what you mean. I'm guessing this means that I will have to overload the loadHeightMap() function to take an Iimage and then construct an IterrainSceneNode manually and call my overloaded loadheightmap function?
Browsing through documentation and source I don't see how I could generate a Heightmap from an Iimage without modifying the source. Is this correct?
Never mind all that.Not really familliar with the whole API yet. I just found ISceneManager::addTerrainMesh().
If I am correct this should do what I want if I provide it with an IImage.
Is there any way to create an empty CImage instance that I can them fill myself with my data?
EDIT: Found it: IVideoDriver::createImage()
Posted: Sat Apr 17, 2010 8:39 pm
by bonsalty
Code: Select all
terrain = smgr->addTerrainSceneNode(device->getFileSystem()->createMemoryReadFile ((void*)lpBits, size, "heightmap",true),
NULL, // parent node
-1, // node id
core::vector3df(0, 0, 0), // position
core::vector3df(0, rotZ, 0), // rotation
core::vector3df(rescaleX,rescaleZ,rescaleY), // scale
video::SColor ( 255, 255, 255, 255 ), // vertexColor
3, // maxLOD
scene::ETPS_65, // patchSize
0 // smoothFactor
);
This works for memory bitmaps.
You have to pass the lpbits and size of the jpg or png or bmp file.
Actually Im using the freeimage api. Freeimage can load almost every graphic file. It also has a feature of getting the bits.
Other option: you can also make a hbitmap or cbitmap and get the byte another suck.
Third option: You make an FTP and I try to send you my lib. To bad it works only with mfc.
Posted: Sat Apr 17, 2010 8:51 pm
by bonsalty
And heightmap to image:
Code: Select all
void ConvertToColor(double z,double skala,int &r,int &g,int &b){
// 2500 levels possible!
const double color_skala=250.0;
double rgb=(z/skala)*color_skala;
int floor_rgb=floor(rgb);
double rest=rgb -floor_rgb;
r=floor_rgb;
g=floor_rgb;
b=floor_rgb;
if (rest>=0.95){r++;g++;b++;}
else if (rest>=0.86){r=r+3;}//0.9
else if (rest>=0.76){g++; b=b+2;}//0.81
else if (rest>=0.66){g++; b++;}//0.7
else if (rest>=0.57){r=r+2;}//0.6
else if (rest>=0.47){r++; b=b+2;}//0.52
else if (rest>=0.38){r++; b++;}//0.41
else if (rest>=0.27){r++;}//0.3
else if (rest>=0.17){b=b+2;}//0.22
else if (rest>=0.06){b++;}//0.11
}
int CreateMap(CMyImage & myImage ,HeightMap *myArray, double min ,double max){
int size=max(myArray->Width(),myArray->Height());
myImage.New(size,size);
int R,G,B;
for (int x=0;x< size; x++)for (int y=0;y< size; y++){
ConvertToColor(myArray->value(x,y)-min,max-min,R,G,B);
myImage.Canvas->SetPixel(x,y,RGB(R,G,B));
}
return 0;
}
This makes a bitmap from a heightmap, simple...
int CreateMap(CMyImage & myImage ,HeightMap *myArray, double min ,double max)
Replace HeightMap with a 2D vector or class, whatever. CMyImage is my custom image loader , you can replace it with the CDC-device context.
Min is the lowest point of the terrain, max is the highest.
Posted: Sat Apr 17, 2010 9:42 pm
by hybrid
Better create a memory read file from your array and use the RAW loader of terrain scene node. You can directly load any number array with ints or floats.
Posted: Sat Apr 17, 2010 9:42 pm
by vitek
bonsalty wrote:Code: Select all
terrain = smgr->addTerrainSceneNode(device->getFileSystem()->createMemoryReadFile ((void*)lpBits, size, "heightmap",true),
NULL, // parent node
-1, // node id
core::vector3df(0, 0, 0), // position
core::vector3df(0, rotZ, 0), // rotation
core::vector3df(rescaleX,rescaleZ,rescaleY), // scale
video::SColor ( 255, 255, 255, 255 ), // vertexColor
3, // maxLOD
scene::ETPS_65, // patchSize
0 // smoothFactor
);
This works for memory bitmaps.
This code will result in a memory leak. You need to remember to
drop() the pointer to the
IReadFile returned by
createMemoryReadFile().
Travis
Posted: Sat Apr 17, 2010 10:19 pm
by Alastriona
hybrid wrote:Better create a memory read file from your array and use the RAW loader of terrain scene node. You can directly load any number array with ints or floats.
Alright ill try this.
Posted: Sat Apr 17, 2010 10:48 pm
by Alastriona
How do I use the RAW loader? I can't seem to create an empty instance of ITerrainSceneNode*? I need an instance of the ITerrainSceneNode to call the raw loader right?
P.S. I'm really tired, maybe I'm missing something.
Posted: Sun Apr 18, 2010 9:59 am
by hybrid
Just create the terrain scene node with an empty heightmap. Then call the RAW load method on it.
Posted: Sun Apr 18, 2010 12:28 pm
by bonsalty
Can you load vectors containing floats with the raw loader?
For instance:
Code: Select all
vector<double > lpBits;
for (int x=0;x<map->Width();x++)for (int
y=0;y<map->Height();y++){
lpBits.push_back(map->value(x,y));
}
size=lpBits.size();
io::IReadFile * file=device->getFileSystem()->createMemoryReadFile ((void *) &lpBits[0], size, "heightmap",true);
terrain = params->smgr->addTerrainSceneNode(NULL);
terrain->loadHeightMapRAW(file);
wont work. Acces violation, Im not familier with casting
Posted: Sun Apr 18, 2010 1:21 pm
by Alastriona
I'm totaly stuck on this. What I currently am trying (assume terrainArray is initialized):
Code: Select all
int terrainArray[257*257];
io::IReadFile* file = device->getFileSystem()->createMemoryReadFile(&terrainArray, 257*257*sizeof(int), "heightmap");
scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode(
NULL,
0,
-1,
core::vector3df(0.f, 0.f, 0.f),
core::vector3df(0.f, 0.f, 0.f),
core::vector3df(40.f, 4.4f, 40.f),
video::SColor ( 0, 0, 0, 255 ),
5,
scene::ETPS_17,
4,
true
);
terrain->loadHeightMapRAW(file,32,true);
Perhaps someone could give me a code sample of how this should be done?
Posted: Sun Apr 18, 2010 1:26 pm
by hybrid
Both code frags should basically work. Use floats instead of doubles in the first code. And don't forget to fill the array of ints in the second version.
Posted: Sun Apr 18, 2010 2:19 pm
by Alastriona
hybrid wrote:Both code frags should basically work. Use floats instead of doubles in the first code. And don't forget to fill the array of ints in the second version.
I am still getting access violations, but that must be somewhere else in my code then. Strangely when I remove the rawloader line it works fine. I can't reproduce it in debug mode either.