terrain height painting

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Katsankat
Posts: 178
Joined: Sun Mar 12, 2006 4:15 am
Contact:

terrain height painting

Post by Katsankat »

I wanted to use a bsp and a terrain. Actually the bsp was built without any terrain at all ...
There are many ways to do the terrain ... 1) As a model, 2) as part of the solid geometry (old school), and 3) as a heightmap ... So if you want to use irrlicht terrain scene node this little app might help to paint the terrain height in order to adapt it to the solid geometry of the bsp.

Image

Complete code below, and download file for binary and images (2.37 Mb)

Code: Select all

#include "irrlicht.h"
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace gui;

/*==============================================================================
  Receiver class
==============================================================================*/
class MyEventReceiver : public IEventReceiver 
{ 
   bool KeyIsDown[KEY_KEY_CODES_COUNT];
   bool LEFTBUTTONCLICKED;
   bool RIGHTBUTTONCLICKED;

public: 
    virtual bool OnEvent(const SEvent& event) 
    { 
        if (event.EventType == irr::EET_KEY_INPUT_EVENT) 
            KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;

        if(event.EventType == EET_MOUSE_INPUT_EVENT)
        {
         if     (event.MouseInput.Event==EMIE_LMOUSE_PRESSED_DOWN) LEFTBUTTONCLICKED = true;
         else if(event.MouseInput.Event==EMIE_LMOUSE_LEFT_UP     ) LEFTBUTTONCLICKED = false;
         else if(event.MouseInput.Event==EMIE_RMOUSE_PRESSED_DOWN) RIGHTBUTTONCLICKED = true;
         else if(event.MouseInput.Event==EMIE_RMOUSE_LEFT_UP     ) RIGHTBUTTONCLICKED = false;
        }

        return false; 
    } 

    virtual bool IsKeyDown(EKEY_CODE keyCode) const { return KeyIsDown[keyCode]; } 
    virtual bool IsLMBDown() const { return LEFTBUTTONCLICKED; } 
    virtual bool IsRMBDown() const { return RIGHTBUTTONCLICKED; } 

    MyEventReceiver() 
    { 
        for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
          KeyIsDown[i] = false;

        LEFTBUTTONCLICKED = RIGHTBUTTONCLICKED = false;
    } 
}; 

/*==============================================================================
  Raise or lower terrain (selected vertice)
==============================================================================*/
void RaiseTerrainVertex(ITerrainSceneNode* terrain, s32 index, f32 step, bool up)
{
   IMesh* pMesh = terrain->getMesh(); 

   s32 b; 
   for (b=0; b<pMesh->getMeshBufferCount(); ++b) 
   { 
      IMeshBuffer* pMeshBuffer = pMesh->getMeshBuffer(b); 
      // skip mesh buffers that are not the right type 
      if (pMeshBuffer->getVertexType() != video::EVT_2TCOORDS) continue; 

      video::S3DVertex2TCoords* pVertices = (video::S3DVertex2TCoords*)pMeshBuffer->getVertices(); 

      pVertices[index].Pos.Y += (up) ? step : -step;
   }

   // force terrain render buffer to reload 
   terrain->setPosition(terrain->getPosition()); 
}

/*==============================================================================
  Save file
==============================================================================*/
void save (IVideoDriver* driver, ITerrainSceneNode* terrain)
{
   core::dimension2d<s32> dim (256,256);
   video::IImage *img = driver->createImage(ECF_R8G8B8, dim);

   u32 VertexCount = terrain->getMesh()->getMeshBuffer(0)->getVertexCount(); 
   S3DVertex2TCoords* verts = (S3DVertex2TCoords*)terrain->getMesh()->getMeshBuffer(0)->getVertices(); 

   for (u32 i=0; i<VertexCount; i++)
   {
     S3DVertex2TCoords* vertex = verts + i;
     u8 y = (u8)vertex->Pos.Y;
     img->setPixel((u32)vertex->Pos.X, (u32)vertex->Pos.Z, video::SColor(0, y,y,y));
   }

   driver->writeImageToFile(img, "heightmap.bmp", 0);
   img->drop();
}

/*==============================================================================
  Entry point
==============================================================================*/
int main()
{
  IrrlichtDevice* device = createDevice(EDT_OPENGL, dimension2d<s32>(800,600), 32, false);
  if (!device) return 0;

    IVideoDriver* driver = device->getVideoDriver();
    ISceneManager* smgr  = device->getSceneManager();
    device->getCursorControl()->setVisible(false);
    device->setWindowCaption(L"terrain");
    MyEventReceiver receiver;
    device->setEventReceiver(&receiver);

  // Terrain
  ITerrainSceneNode* terrain = smgr->addTerrainSceneNode("heightmap.bmp");
    terrain->setScale(core::vector3df(32, 2.f, 32));
    terrain->setMaterialFlag(video::EMF_LIGHTING, false);
    terrain->setMaterialTexture(0, driver->getTexture("aer.jpg"));
   ITriangleSelector* terrainSelector = smgr->createTerrainTriangleSelector(terrain, 0);

  // Camera
   scene::ICameraSceneNode* cam = smgr->addCameraSceneNodeFPS(0, 100.0f, .1f); 
   cam->setPosition(core::vector3df(-100,500,100)); 
   cam->setTarget(core::vector3df(0,0,0)); 

  // Arrow
   ISceneNode* arrow = smgr->addAnimatedMeshSceneNode(smgr->addArrowMesh("arrow", 
            SColor(255, 255, 0, 0), SColor(255, 0, 255, 0)), NULL);
   arrow->setMaterialFlag(video::EMF_LIGHTING, false);
   arrow->setScale(vector3df(20,20,20));
   arrow->setRotation(vector3df(0,0,180));

  // GUI
    IGUIEnvironment* guienv = device->getGUIEnvironment();
    IGUIFont* font = guienv->getFont("battlefield.bmp");
    guienv->getSkin()->setFont(font);

    IGUIStaticText* txt[2];
    txt[0] = guienv->addStaticText(L"Left/Right Mouse Button : raise/lower\nF4/F5 : increase/decrease step\nW : switch wireframe\nS : save heightmap",
                core::rect<s32>(10,10,600,120), false, true, 0, -1, false);
    txt[0]->setOverrideColor(video::SColor(255,150,100,60));


    txt[1] = guienv->addStaticText(L"", core::rect<s32>(10,80,600,200), false, true, 0, -1, false);
    txt[1]->setOverrideColor(video::SColor(255,0,255,0));


  ITimer* irrTimer = device->getTimer();
  u32 then = 0, then30 = 0;
  char c[24];
  f32 step = 2.f;
  bool wireframe = false;

  while(device->run()) if (device->isWindowActive()) 
   { 
      u32 now = irrTimer->getTime();
      if (then30 < now)
      {
        if(receiver.IsKeyDown(irr::KEY_ESCAPE)) break;


        if (receiver.IsKeyDown(irr::KEY_KEY_W) && then < now)
        {
          wireframe = !wireframe;
          terrain->setMaterialFlag(video::EMF_WIREFRAME, wireframe);
          then = now + 300;
        }

        if (receiver.IsKeyDown(irr::KEY_F4) && then < now)
        {
          step += 1.f;
          then = now + 100;
        }
        else if (receiver.IsKeyDown(irr::KEY_F5) && then < now && step > 0)
        {
          step -= 1.f;
          then = now + 100;
        }

        if(receiver.IsKeyDown(irr::KEY_KEY_S))
          save (driver, terrain);

        // move the arrow to the nearest vertex ...
        const position2di clickPosition = device->getCursorControl()->getPosition(); 
        const line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(clickPosition, cam);
        vector3df pos;
        triangle3df Tri;
        if (smgr->getSceneCollisionManager()->getCollisionPoint(ray, terrainSelector, pos, Tri))
        {
          //arrow->setPosition(pos);
          static const s32 scale = 32; // terrain is scaled 32X
          static const s32 size = 256; // heightmap is 256X256 pixels
          s32 x = (s32)(pos.X / scale);
          s32 z = (s32)(pos.Z / scale);
          s32 index = x * size + z;

          // ... Move it if clicked
          if( (receiver.IsLMBDown() || receiver.IsRMBDown()) && then < now)
          {
            RaiseTerrainVertex(terrain, index, step, receiver.IsLMBDown());
            then = now + 100;
          }

          x *= scale;
          z *= scale;

          arrow->setPosition(vector3df(x, terrain->getHeight(x, z) + 20, z));
        }

        driver->beginScene(true, true, video::SColor(255,0,0,0)); 
        smgr->drawAll();

        sprintf(c,"elevation step : %.0f units", step);
        txt[1]->setText(stringw(c).c_str());

        guienv->drawAll();
        driver->endScene(); 

        then30 = now + 30;

      } // 30Hz
   }
 
  device->closeDevice();  
  device->drop();

  return 0;
}
Tested only with GCC.
Pyritie
Posts: 120
Joined: Fri Jan 16, 2009 12:59 pm
Contact:

Post by Pyritie »

Soooo... what is this used for? I'm sorta confused.
Hive Workshop | deviantART

I've moved to Ogre.
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

It modifies the mesh buffer that the terrain scene node is rendered from. It is a primitive terrain editor.
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Post by christianclavet »

This small code does all this? Really cool!
The only thing missing would be to add a "nozzle" width size definition. To move more than one vertice at a time.

I think that studying this code could perhaps show also me how to really "paint" on the surface (putting a trail for example). Or adding "decals" on a surface.

Thanks for the code!
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Post by Mel »

In fact i would like that Irrlicht had also the ability to paint and blend arbitrary textures in the terrains like the Torque Engine does (perhaps). That would definately improve quite a lot the look of the terrain Node.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
Katsankat
Posts: 178
Joined: Sun Mar 12, 2006 4:15 am
Contact:

Post by Katsankat »

Painting terrain is fun and fast.

However you are right 64X scaled terrain and a 1024X1024 ground texture (scaled down 32X) is not good enough:
Image
polycount budget :
Terrain 14000
bsp 11000
and some tree models (4 tris per tree)


I was thinking about creating explosion craters at run time.
Or allow players to dig foxholes ... Already done? Interesting for gameplay.

For explosion craters the challenge is to manipulate the terrain with best
performances. What about initializing an array of heights (say 6X8 f32 or u8 ) ...

Code: Select all

0  0   1  1  0  0
0  1   2  1  0  0
1  2   1  2  1  1
1  2  -1 -1  2  1
1  2  -1 -1  2  1
0  0   1  2  1  0
0  1   2  1  0  0
0  0   1  0  0  0
... and apply this transformation to the nearest vertices of the impact.
Dorth
Posts: 931
Joined: Sat May 26, 2007 11:03 pm

Post by Dorth »

Go one step further, create brushes ^^
Simply a) create a secondary terrain, the size of the brush
b) modify the brush, taking the starting height as 0
c) apply the brush modifier on the original map wherever you use the brush.

Heh, even let brushes be saved, thus creating sets that ppl can trade ;)
Katsankat
Posts: 178
Joined: Sun Mar 12, 2006 4:15 am
Contact:

Post by Katsankat »

Fast craters done. Here is the code.
;) nbBarrels is the number of barrels in the volley.

Code: Select all

void CArty::explode(f32 X, f32 Z, u8 nbBarrels, u8 caliber)
{
  //printf("desired impact (%.2f,%.2f)\n", X, Z);

  //elevation around impact. This array can be stored as raw image:
  static const char eai[25]={
    0,  1,  1,  1, 0,
    1, -1, -1, -1, 1,
    1, -1, -2, -1, 1, // <- impact point vertex will be lowered by -2 units
    1, -1, -1, -1, 1,
    0,  1,  1,  1, 0
  };

  //ok lets get terrain vertices
  video::S3DVertex2TCoords* verts = map->getTerrainVertices();

  for (u8 n=0; n<_nbBarrels; n++)
  {
    //arty shells are always deviated
    u16 d;
    d = rand()%2000;
    f32 nX = (f32)((rand()%2) == 0) ? d : -d;
    nX += X;

    d = rand()%2000;
    f32 nZ = (f32)((rand()%2) == 0) ? d : -d;
    nZ += Z;

    //printf("barrel %d (%.2f,%.2f)\n", n, nX,nZ);

    //nearest vertice on terrain
    s32 x = (s32)(nX / map->header->terrainScaleX);
    s32 z = (s32)(nZ / map->header->terrainScaleZ);

    //from the array above its origin is 2 verts left top of impact
    //so always drop arty at least 2 vertices from borders
    x -= 2;
    z -= 2;

    //apply the crater transformation
    int cpt = 0;
    for (u8 v=0; v<5; v++)
      for (u8 u=0; u<5; u++)
      {
        s32 index = (x+u) * map->header->terrainPixels + (z+v);
        verts[index].Pos.Y += eai[cpt++];
      }

    // force terrain render buffer to reload 
    terrain->setPosition(terrain->getPosition());

    //emit particles...

  }
  //shaking...

  //play sound...
}
kingdutch
Posts: 76
Joined: Tue Sep 02, 2008 7:01 am

Post by kingdutch »

The above mentioned function for explode didn't work so I created my own based on that, using the earlier made RaiseTerrainVertex:

Code: Select all

void explode(ITerrainSceneNode* terrain, s32 indexMid, u8 nbBarrels, s32 size)
{
  //printf("desired impact (%.2f,%.2f)\n", X, Z);
 
  //elevation around impact. This array can be stored as raw image:
  static const char eai[25]={
    0, 0, 0, 0, 0,
    0, 1, 1, 1, 0,
    0, 1, 2, 1, 0, // <- impact point vertex will be lowered by -2 units
    0, 1, 1, 1, 0,
    0, 0, 0, 0, 0
  };
 
  //ok lets get terrain vertices
 
  for (u8 n=0; n<nbBarrels; n++)
  {
 
    //apply the crater transformation
    int cpt = 0;
    for (u8 v=0; v<5; v++)
      for (u8 u=0; u<5; u++)
      {
        s32 index = indexMid - 2 * size + v*size - 2 + u;
                RaiseTerrainVertex(terrain, index, eai[cpt++], false);
      }
 
    // force terrain render buffer to reload
    //terrain->setPosition(terrain->getPosition());
 
    //emit particles...
 
  }
  //shaking...
 
  //play sound...
}
If you have any problems, let me know.

I've also added some rounding to getting the index which now works like this:

Place this at the top of your main.cpp just after your #includes

Code: Select all

inline float round( float f ){ return (f > 0.0) ? (float)floor(f + 0.5) : (float)ceil(f - 0.5); }
And replace the index calculation in your main game loop by

Code: Select all

s32 index = round(x) * size + round(z);
This should make selecting vertices a bit more accurate by not truncating everything after the . but rounding to the nearest vertex.

EDIT:
Just wanted to thank randomMesh for pointing me to this topic, I'd still be messing with vertexbuffer stuff if not for him :idea:
Last edited by kingdutch on Thu Apr 29, 2010 9:14 pm, edited 1 time in total.
if (msg.getRubbishFactor() > rubbishLimit) { ignorePost(); cout << how it should be done << "\n"; }
Insomniacp
Posts: 288
Joined: Wed Apr 16, 2008 1:45 am
Contact:

Post by Insomniacp »

I did have a terrain editor that had brush size changes and vertex painting I believe. Let me see if it still compiles. (chances are very high the code is very messy).

Found IT! (well one of the 5 attempts)
!this uses irrlicht 1.5!!! (yes it is old).
http://rofhonline.com/Realm%20Editor%20Final.zip

As I said it is quite messy, that has an exe that you can look at it with, and then source code as well. Most transformation things are in map.h (which is big and messy) the function useTool does the manipulation based off of the tool selected on the gui. As this system I made uses multiple terrain scene nodes placed side by side to make the entire map so you could build infinitely, it does save and load I believe, saves a color map and height map. For some reason the coloring doesn't work well in this editor, made another one where coloring was really nice.
http://rofhonline.com/images/neweditor.jpg
but something happened somewhere and coloring doesn't work as well anymore. As seen by screen shot below, the colors just don't blend as well.
http://rofhonline.com/images/mapeditorV05.jpg
Pardon the UI overlap, the gui system was quickly taken from the game I was working on and I didn't want to recode it or make a new background gui image so I used the preexisting one. I have started a more complete map editor recently which consists of physics manipulation, object placing, map manipulation and ai manipulation. I don't know if it will be open source since it is very specifically oriented toward the game I am developing, may be able to pull out the map and physics manipulation though.
The maps are loaded in a different thread (not sure if i did it properly think i just kind of tossed them in and hoped for the best). Seemed to work well so far.
One strange part of this is that I made an above and below grid of terrain nodes, I was planning on making caves and things like that by removing vertices from the terrain itself and then moving the other terrain to meld into the other one so they combine and allow you to walk through a hole in the map onto another map. Never completed that as it wasn't going to work very well with the LOD system, so that is why there are some maps above and below which is why fps is lower than it seems like it should be.
ACE247
Posts: 704
Joined: Tue Mar 16, 2010 12:31 am

Post by ACE247 »

OMG! :D Thanks guys this is just what I was looking for. Infact I just today wanted to start making my own when I saw this. Awesome.
netpipe
Posts: 670
Joined: Fri Jun 06, 2008 12:50 pm
Location: Edmonton, Alberta, Canada
Contact:

Post by netpipe »

added some lightning to cause the crators...
http://www.xup.in/dl,92001497/terrain-painter.7z/ << forgot to include the inline and thing from KD's post above

duno what the heck is wrong with this one, i converted it to linux and now it wants to crash every time it deletes a the cubescenenodes
http://www.xup.in/dl,13441876/RealmEditorFinal.7z/
Live long and phosphor!
-- https://github.com/netpipe/Luna Game Engine Status 95%
Insomniacp
Posts: 288
Joined: Wed Apr 16, 2008 1:45 am
Contact:

Post by Insomniacp »

for the realm editor it may be because it was made with irrlicht 1.5. Not sure what you compiled it with, though I think i may have put 1.5 in the folder. Was prolly a programming error on my end :P Sadly im not very inclined to problem solve it at the moment. I would suggest just looking at the code on how I did things. Could be used as an example of what not to do as well as I don't think it was a very good chunk of code. I optimized it a little as you can tell by the 5ms and things of the like in the comments (though considering it could manipulate across terrain nodes that may not have been to bad a piece of code). The current one I am working on is much improved in concept and in design and will be in implementation. I won't be using as large of terrain scene nodes for 1 ( think i did 1kx1k in the realm editor which I don't think LOD's very well wish I could find the code for the old vertex painter I had as that one looked much better.
ACE247
Posts: 704
Joined: Tue Mar 16, 2010 12:31 am

Post by ACE247 »

Tecan, could you please upload the Real Editor Elsewere. Xup doesnt want to work for me.
Thanks.
Insomniacp
Posts: 288
Joined: Wed Apr 16, 2008 1:45 am
Contact:

Post by Insomniacp »

http://rofhonline.com/RealmEditorFinal.7z
There you go, uploaded to my site.

http://rofhonline.com/rofh%20open%20source.rar
I discontinued this project a while ago but that contains the files I used for the editor aswell as the client, patcher and server if anyone wants to see them. you will have to change the ip to the computer your on, or 127.0.0.1 for the loop back interface and start the server then the client. I think the port is 50000 or something of the likes.
Post Reply