tcod-haskell-0.1.0.0: libtcod/samples/hmtool/hmtool.cpp
#include <stdarg.h>
#include <stdio.h>
#include <math.h>
#include "libtcod.hpp"
#include "gui/gui.hpp"
#include "operation.hpp"
TCODHeightMap *hm=NULL, *hmold=NULL;
TCODNoise *noise= NULL;
TCODRandom *rnd=NULL, *backupRnd=NULL;
TCODConsole *guicon=NULL;
bool greyscale=false;
bool slope=false;
bool normal=false;
bool isNormalized=true;
bool oldNormalized=true;
char msg[512]="";
float msgDelay=0.0f;
float hillRadius=0.1f;
float hillVariation=0.5f;
float addFbmDelta=0.0f;
float scaleFbmDelta=0.0f;
uint32_t seed=0xdeadbeef;
float sandHeight=0.12f;
float grassHeight=0.315f;
float snowHeight=0.785f;
char landMassTxt[128]="";
char minZTxt[128]="";
char maxZTxt[128]="";
char seedTxt[128]="";
float mapmin=0.0f,mapmax=0.0f;
float oldmapmin=0.0f,oldmapmax=0.0f;
// ui
ToolBar *params=NULL;
ToolBar *history=NULL;
ToolBar *colorMapGui=NULL;
float voronoiCoef[2] = {
-1.0f,1.0f
};
/* light 3x3 smoothing kernel :
1 2 1
2 20 2
1 2 1
*/
int smoothKernelSize=9;
int smoothKernelDx[9]={
-1,0,1,-1,0,1,-1,0,1
};
int smoothKernelDy[9]={
-1,-1,-1,0,0,0,1,1,1
};
float smoothKernelWeight[9]={
1,2,1,2,20,2,1,2,1
};
TCODColor mapGradient[256];
#define MAX_COLOR_KEY 10
// TCOD's land color map
static int keyIndex[MAX_COLOR_KEY] = {0,
(int)(sandHeight*255),
(int)(sandHeight*255)+4,
(int)(grassHeight*255),
(int)(grassHeight*255)+10,
(int)(snowHeight*255),
(int)(snowHeight*255)+10,
255
};
static TCODColor keyColor[MAX_COLOR_KEY]= {
TCODColor(0,0,50), // deep water
TCODColor(30,30,170), // water-sand transition
TCODColor(114,150,71),// sand
TCODColor(80,120,10),// sand-grass transition
TCODColor(17,109,7), // grass
TCODColor(120,220,120), // grass-snow transisiton
TCODColor(208,208,239), // snow
TCODColor(255,255,255)
};
static Image *keyImages[MAX_COLOR_KEY];
static int nbColorKeys=8;
void initColors() {
TCODColor::genMap(mapGradient,nbColorKeys,keyColor,keyIndex);
}
void render() {
static TCODHeightMap backup(HM_WIDTH,HM_HEIGHT);
isNormalized=true;
TCODConsole::root->setDefaultBackground(TCODColor::black);
TCODConsole::root->setDefaultForeground(TCODColor::white);
TCODConsole::root->clear();
backup.copy(hm);
mapmin=1E8f;
mapmax=-1E8f;
for (int i=0; i < HM_WIDTH*HM_HEIGHT; i++) {
if ( hm->values[i] < 0.0f || hm->values[i] > 1.0f ) {
isNormalized=false;
}
if ( hm->values[i] < mapmin ) mapmin=hm->values[i];
if ( hm->values[i] > mapmax ) mapmax=hm->values[i];
}
if (! isNormalized ) backup.normalize();
// render the TCODHeightMap
for (int x=0; x < HM_WIDTH; x ++ ) {
for (int y=0;y < HM_HEIGHT; y++ ) {
float z = backup.getValue(x,y);
uint8_t val=(uint8_t)(z*255);
if ( slope ) {
// render the slope map
z = CLAMP(0.0f,1.0f,hm->getSlope(x,y)*10.0f);
val = (uint8_t)(z*255);
TCODColor c(val,val,val);
TCODConsole::root->setCharBackground(x,y,c);
} else if ( greyscale ) {
// render the greyscale heightmap
TCODColor c(val,val,val);
TCODConsole::root->setCharBackground(x,y,c);
} else if ( normal) {
// render the normal map
float n[3];
hm->getNormal((float)x,(float)y,n,mapmin);
uint8_t r = (uint8_t)((n[0]*0.5f+0.5f)*255);
uint8_t g = (uint8_t)((n[1]*0.5f+0.5f)*255);
uint8_t b = (uint8_t)((n[2]*0.5f+0.5f)*255);
TCODColor c(r,g,b);
TCODConsole::root->setCharBackground(x,y,c);
} else {
// render the colored heightmap
TCODConsole::root->setCharBackground(x,y,mapGradient[val]);
}
}
}
sprintf(minZTxt,"min z : %.2f",mapmin);
sprintf(maxZTxt,"max z : %.2f",mapmax);
sprintf(seedTxt,"seed : %X",seed);
float landProportion=100.0f - 100.0f*backup.countCells(0.0f,sandHeight) / (hm->w*hm->h);
sprintf(landMassTxt,"landMass : %d %%%%",(int)landProportion);
if ( ! isNormalized ) TCODConsole::root->printEx(HM_WIDTH/2,HM_HEIGHT-1,TCOD_BKGND_NONE,TCOD_CENTER,"the map is not normalized !");
// message
msgDelay-=TCODSystem::getLastFrameLength();
if ( msg[0] != 0 && msgDelay > 0.0f ) {
int h=TCODConsole::root->printRectEx(HM_WIDTH/2,HM_HEIGHT/2+1,HM_WIDTH/2-2,0,TCOD_BKGND_NONE,TCOD_CENTER,msg);
TCODConsole::root->setDefaultBackground(TCODColor::lightBlue);
if (h > 0 ) TCODConsole::root->rect(HM_WIDTH/4,HM_HEIGHT/2,HM_WIDTH/2,h+2,false,TCOD_BKGND_SET);
TCODConsole::root->setDefaultBackground(TCODColor::black);
}
}
void message(float delay,const char *fmt,...) {
va_list ap;
msgDelay=delay;
va_start(ap,fmt);
vsprintf(msg,fmt,ap);
va_end(ap);
}
void backup() {
// save the heightmap & RNG states
for (int x=0; x < HM_WIDTH; x ++ ) {
for (int y=0;y < HM_HEIGHT; y++ ) {
hmold->setValue(x,y,hm->getValue(x,y));
}
}
if ( backupRnd ) delete backupRnd;
backupRnd=rnd->save();
oldNormalized=isNormalized;
oldmapmax=mapmax;
oldmapmin=mapmin;
}
void restore() {
// restore the previously saved heightmap & RNG states
for (int x=0; x < HM_WIDTH; x ++ ) {
for (int y=0;y < HM_HEIGHT; y++ ) {
hm->setValue(x,y,hmold->getValue(x,y));
}
}
rnd->restore(backupRnd);
isNormalized=oldNormalized;
mapmax=oldmapmax;
mapmin=oldmapmin;
}
void save() {
// TODO
message(2.0f,"Saved.");
}
void load() {
// TODO
}
void addHill(int nbHill, float baseRadius, float radiusVar, float height) {
for (int i=0; i< nbHill; i++ ) {
float hillMinRadius=baseRadius*(1.0f-radiusVar);
float hillMaxRadius=baseRadius*(1.0f+radiusVar);
float radius = rnd->getFloat(hillMinRadius, hillMaxRadius);
float theta = rnd->getFloat(0.0f, 6.283185f); // between 0 and 2Pi
float dist = rnd->getFloat(0.0f, (float)MIN(HM_WIDTH,HM_HEIGHT)/2 - radius);
int xh = (int) (HM_WIDTH/2 + cos(theta) * dist);
int yh = (int) (HM_HEIGHT/2 + sin(theta) * dist);
hm->addHill((float)xh,(float)yh,radius,height);
}
}
void clearCbk(Widget *w,void *data) {
hm->clear();Operation::clear();
history->clear();
params->clear();
params->setVisible(false);
}
void reseedCbk(Widget *w,void *data) {
seed=rnd->getInt(0x7FFFFFFF,0xFFFFFFFF);
Operation::reseed();
message(3.0f,"Switching to seed %X",seed);
}
void cancelCbk(Widget *w, void *data) {
Operation::cancel();
}
// operations buttons callbacks
void normalizeCbk(Widget *w,void *data) {
(new NormalizeOperation(0.0f,1.0f))->add();
}
void addFbmCbk(Widget *w,void *data) {
(new AddFbmOperation(1.0f,addFbmDelta,0.0f,6.0f,1.0f,0.5f))->add();
}
void scaleFbmCbk(Widget *w, void *data) {
(new ScaleFbmOperation(1.0f,addFbmDelta,0.0f,6.0f,1.0f,0.5f))->add();
}
void addHillCbk(Widget *w,void *data) {
(new AddHillOperation(25,10.0f,0.5f,(mapmax==mapmin ? 0.5f : (mapmax-mapmin)*0.1f)))->add();
}
void rainErosionCbk(Widget *w,void *data) {
(new RainErosionOperation(1000,0.05f,0.05f))->add();
}
void smoothCbk(Widget *w,void *data) {
(new SmoothOperation(mapmin,mapmax,2))->add();
}
void voronoiCbk(Widget *w,void *data) {
(new VoronoiOperation(100,2,voronoiCoef))->add();
}
void noiseLerpCbk(Widget *w,void *data) {
float v=(mapmax-mapmin)*0.5f;
if (v == 0.0f ) v=1.0f;
(new NoiseLerpOperation(0.0f,1.0f,addFbmDelta,0.0f,6.0f,v,v))->add();
}
void raiseLowerCbk(Widget *w, void *data) {
(new AddLevelOperation(0.0f))->add();
}
// In/Out buttons callbacks
void exportCCbk(Widget *w, void *data) {
const char *code=Operation::buildCode(Operation::C);
FILE *f=fopen("hm.c","wt");
fprintf(f,"%s",code);
fclose(f);
message(3.0f,"The code has been exported to ./hm.c");
}
void exportPyCbk(Widget *w, void *data) {
const char *code=Operation::buildCode(Operation::PY);
FILE *f=fopen("hm.py","wt");
fprintf(f,"%s",code);
fclose(f);
message(3.0f,"The code has been exported to ./hm.py");
}
void exportCppCbk(Widget *w, void *data) {
const char *code=Operation::buildCode(Operation::CPP);
FILE *f=fopen("hm.cpp","wt");
fprintf(f,"%s",code);
fclose(f);
message(3.0f,"The code has been exported to ./hm.cpp");
}
void exportBmpCbk(Widget *w, void *data) {
TCODImage img(HM_WIDTH,HM_HEIGHT);
for (int x=0; x < HM_WIDTH; x++ ) {
for (int y=0; y < HM_HEIGHT; y++ ) {
float z = hm->getValue(x,y);
uint8_t val=(uint8_t)(z*255);
if ( slope ) {
// render the slope map
z = CLAMP(0.0f,1.0f,hm->getSlope(x,y)*10.0f);
val = (uint8_t)(z*255);
TCODColor c(val,val,val);
img.putPixel(x,y,c);
} else if ( greyscale ) {
// render the greyscale heightmap
TCODColor c(val,val,val);
img.putPixel(x,y,c);
} else if ( normal) {
// render the normal map
float n[3];
hm->getNormal((float)x,(float)y,n,mapmin);
uint8_t r = (uint8_t)((n[0]*0.5f+0.5f)*255);
uint8_t g = (uint8_t)((n[1]*0.5f+0.5f)*255);
uint8_t b = (uint8_t)((n[2]*0.5f+0.5f)*255);
TCODColor c(r,g,b);
img.putPixel(x,y,c);
} else {
// render the colored heightmap
img.putPixel(x,y,mapGradient[val]);
}
}
}
img.save("hm.bmp");
message(3.0f,"The bitmap has been exported to ./hm.bmp");
}
// Display buttons callbacks
void colorMapCbk(Widget *w, void *data) {
slope=false;
greyscale=false;
normal=false;
}
void greyscaleCbk(Widget *w, void *data) {
slope=false;
greyscale=true;
normal=false;
}
void slopeCbk(Widget *w, void *data) {
slope=true;
greyscale=false;
normal=false;
}
void normalCbk(Widget *w, void *data) {
slope=false;
greyscale=false;
normal=true;
}
void changeColorMapIdxCbk(Widget *w, float val, void *data) {
intptr_t i=((intptr_t)data);
keyIndex[i]=(int)(val);
if ( i == 1 ) sandHeight = (float)(i)/255.0f;
initColors();
}
void changeColorMapRedCbk(Widget *w, float val, void *data) {
intptr_t i=((intptr_t)data);
keyColor[i].r=(int)(val);
keyImages[i]->setBackgroundColor(keyColor[i]);
initColors();
}
void changeColorMapGreenCbk(Widget *w, float val, void *data) {
intptr_t i=((intptr_t)data);
keyColor[i].g=(int)(val);
keyImages[i]->setBackgroundColor(keyColor[i]);
initColors();
}
void changeColorMapBlueCbk(Widget *w, float val, void *data) {
intptr_t i=((intptr_t)data);
keyColor[i].b=(int)(val);
keyImages[i]->setBackgroundColor(keyColor[i]);
initColors();
}
void changeColorMapEndCbk(Widget *w, void *data) {
colorMapGui->setVisible(false);
}
void changeColorMapCbk(Widget *w, void *data) {
colorMapGui->move(w->x+w->w+2,w->y);
colorMapGui->clear();
for (int i=0; i < nbColorKeys; i++ ) {
char tmp[64];
sprintf(tmp,"Color %d",i);
colorMapGui->addSeparator(tmp);
HBox *hbox=new HBox(0,0,0);
VBox *vbox=new VBox(0,0,0);
colorMapGui->addWidget(hbox);
Slider *idxSlider=new Slider(0,0,3,0.0f,255.0f,"index","Index of the key in the color map (0-255)");
idxSlider->setValue((float)keyIndex[i]);
idxSlider->setFormat("%.0f");
idxSlider->setCallback(changeColorMapIdxCbk,(void *)i);
vbox->addWidget(idxSlider);
keyImages[i]=new Image(0,0,0,2);
keyImages[i]->setBackgroundColor(keyColor[i]);
vbox->addWidget(keyImages[i]);
hbox->addWidget(vbox);
vbox=new VBox(0,0,0);
hbox->addWidget(vbox);
Slider *redSlider=new Slider(0,0,3,0.0f,255.0f,"r","Red component of the color");
redSlider->setValue((float)keyColor[i].r);
redSlider->setFormat("%.0f");
redSlider->setCallback(changeColorMapRedCbk,(void *)i);
vbox->addWidget(redSlider);
Slider *greenSlider=new Slider(0,0,3,0.0f,255.0f,"g","Green component of the color");
greenSlider->setValue((float)keyColor[i].g);
greenSlider->setFormat("%.0f");
greenSlider->setCallback(changeColorMapGreenCbk,(void *)i);
vbox->addWidget(greenSlider);
Slider *blueSlider = new Slider(0,0,3,0.0f,255.0f,"b","Blue component of the color");
blueSlider->setValue((float)keyColor[i].b);
blueSlider->setFormat("%.0f");
blueSlider->setCallback(changeColorMapBlueCbk,(void *)i);
vbox->addWidget(blueSlider);
}
colorMapGui->addWidget(new Button("Ok",NULL,changeColorMapEndCbk,NULL));
colorMapGui->setVisible(true);
}
// build gui
void addOperationButton(ToolBar *tools, Operation::OpType type, void (*cbk)(Widget *w,void *data)){
tools->addWidget(new Button(Operation::names[type],Operation::tips[type],cbk,NULL));
}
void buildGui() {
// status bar
new StatusBar(0,0,HM_WIDTH,1);
VBox *vbox=new VBox(0,2,1);
// stats
ToolBar *stats = new ToolBar(0,0,21,"Stats","Statistics about the current map");
stats->addWidget(new Label(0,0,landMassTxt,"Ratio of land surface / total surface"));
stats->addWidget(new Label(0,0,minZTxt,"Minimum z value in the map"));
stats->addWidget(new Label(0,0,maxZTxt,"Maximum z value in the map"));
stats->addWidget(new Label(0,0,seedTxt,"Current random seed used to build the map"));
vbox->addWidget(stats);
// tools
ToolBar *tools=new ToolBar(0,0,15,"Tools","Tools to modify the heightmap");
tools->addWidget(new Button("cancel","Delete the selected operation",cancelCbk,NULL));
tools->addWidget(new Button("clear","Remove all operations and reset all heightmap values to 0.0",clearCbk,NULL));
tools->addWidget(new Button("reseed","Replay all operations with a new random seed",reseedCbk,NULL));
// operations
tools->addSeparator("Operations","Apply a new operation to the map");
addOperationButton(tools,Operation::NORM,normalizeCbk);
addOperationButton(tools,Operation::ADDFBM,addFbmCbk);
addOperationButton(tools,Operation::SCALEFBM,scaleFbmCbk);
addOperationButton(tools,Operation::ADDHILL,addHillCbk);
addOperationButton(tools,Operation::RAIN,rainErosionCbk);
addOperationButton(tools,Operation::SMOOTH,smoothCbk);
addOperationButton(tools,Operation::VORONOI,voronoiCbk);
addOperationButton(tools,Operation::NOISELERP,noiseLerpCbk);
addOperationButton(tools,Operation::ADDLEVEL,raiseLowerCbk);
// display
tools->addSeparator("Display","Change the type of display");
RadioButton::setDefaultGroup(1);
RadioButton *colormap=new RadioButton("colormap","Enable colormap mode",colorMapCbk,NULL);
tools->addWidget(colormap);
colormap->select();
tools->addWidget(new RadioButton("slope","Enable slope mode",slopeCbk,NULL));
tools->addWidget(new RadioButton("greyscale","Enable greyscale mode",greyscaleCbk,NULL));
tools->addWidget(new RadioButton("normal","Enable normal map mode",normalCbk,NULL));
tools->addWidget(new Button("change colormap","Modify the colormap used by hmtool", changeColorMapCbk,NULL));
// change colormap gui
colorMapGui=new ToolBar(0,0,"Colormap","Select the color and position of the keys in the color map");
colorMapGui->setVisible(false);
// in/out
tools->addSeparator("In/Out","Import/Export stuff");
tools->addWidget(new Button("export C","Generate the C code for this heightmap in ./hm.c",exportCCbk,NULL));
tools->addWidget(new Button("export CPP","Generate the CPP code for this heightmap in ./hm.cpp",exportCppCbk,NULL));
tools->addWidget(new Button("export PY","Generate the Python code for this heightmap in ./hm.py",exportPyCbk,NULL));
tools->addWidget(new Button("export bmp","Save this heightmap as a bitmap in ./hm.bmp",exportBmpCbk,NULL));
vbox->addWidget(tools);
// params box
params = new ToolBar(0,0,"Params","Parameters of the current tool");
vbox->addWidget(params);
params->setVisible(false);
// history
history = new ToolBar(0,tools->y+1+tools->h,15,"History","History of operations");
vbox->addWidget(history);
}
int main(int argc, char *argv[]) {
TCODConsole::initRoot(HM_WIDTH,HM_HEIGHT,"height map tool",false);
guicon = new TCODConsole(HM_WIDTH,HM_HEIGHT);
guicon->setKeyColor(TCODColor(255,0,255));
Widget::setConsole(guicon);
TCODSystem::setFps(25);
initColors();
buildGui();
hm = new TCODHeightMap(HM_WIDTH,HM_HEIGHT);
hmold = new TCODHeightMap(HM_WIDTH,HM_HEIGHT);
rnd=new TCODRandom(seed);
noise=new TCODNoise(2,rnd);
uint8_t fade=50;
bool creditsEnd=false;
while ( ! TCODConsole::isWindowClosed() ) {
render();
guicon->setDefaultBackground(TCODColor(255,0,255));
guicon->clear();
Widget::renderWidgets();
if (! creditsEnd ) {
creditsEnd=TCODConsole::renderCredits(HM_WIDTH-20,HM_HEIGHT-7,true);
}
if ( Widget::focus ) {
if ( fade < 200 ) fade += 20;
} else {
if ( fade > 80 ) fade -= 20;
}
TCODConsole::blit(guicon,0,0,HM_WIDTH,HM_HEIGHT,TCODConsole::root,0,0,fade/255.0f,fade/255.0f);
TCODConsole::flush();
TCOD_key_t key;
TCOD_mouse_t mouse;
TCODSystem::checkForEvent(TCOD_EVENT_KEY_PRESS|TCOD_EVENT_MOUSE,&key,&mouse);
Widget::updateWidgets(key,mouse);
switch(key.c) {
case '+' : (new AddLevelOperation((mapmax-mapmin)/50))->run(); break;
case '-' : (new AddLevelOperation(-(mapmax-mapmin)/50))->run(); break;
default:break;
}
switch(key.vk) {
case TCODK_PRINTSCREEN : TCODSystem::saveScreenshot(NULL); break;
default:break;
}
}
return 0;
}