Terrain height painting with brush

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.

Terrain height painting with brush

Postby Luiz » Sat Sep 10, 2011 12:22 am

Hello everybody.

After reading Katsankat's post (that you can find here http://irrlicht.sourceforge.net/forum/viewtopic.php?f=9&t=32251), I wanted to do something more flexible, I wanted to deform my terrain without doing it vertex by vertex. So I changed Katsankat's code. Here it is :
cpp Code: Select all
#include <irr/irrlicht.h>
 
using namespace irr;
 
 
video::IImage* heightmap;
video::IImage* brush;
 
scene::ITerrainSceneNode* terrain;
 
/*==============================================================================
  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(s32 index, f32 step, bool up){
    scene::IMesh* pMesh = terrain->getMesh();
 
    s32 heightmapWidth = heightmap->getDimension().Width;
    s32 heightmapHeight = heightmap->getDimension().Height;
 
    s32 b;
    for (b=0; b<pMesh->getMeshBufferCount(); ++b){
        scene::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();
 
        s32 brushWidth = brush->getDimension().Width;
        s32 brushHeight = brush->getDimension().Height;
 
        for(int y = 0; y < brushHeight; y++){
            for(int x = 0; x < brushWidth; x++){
                video::SColor brushPixel = brush->getPixel(x, y);
 
                if((index-(brushWidth/2)-((brushWidth/2)*heightmapWidth) + (x+(y*heightmapWidth))) >= 0){
                    f32 hy = pVertices[index-(brushWidth/2)-((brushWidth/2)*heightmapWidth) + (x+(y*heightmapWidth))].Pos.Y;
                    f32 bp = brushPixel.getRed()/255.0*step;
                    bp = (up)?bp:-bp;
 
                    if(bp > 0 && hy+bp <= 255)
                        pVertices[index-(brushWidth/2)-((brushWidth/2)*heightmapWidth) + (x+(y*heightmapWidth))].Pos.Y = hy+bp;
                }
            }
        }
    }
 
    // force terrain render buffer to reload
    terrain->setPosition(terrain->getPosition());
}
 
/*==============================================================================
  Save file
==============================================================================*/

void save (video::IVideoDriver* driver){
    s32 heightmapWidth = heightmap->getDimension().Width;
    s32 heightmapHeight = heightmap->getDimension().Height;
 
    const core::dimension2d<u32> dim (heightmapWidth, heightmapHeight);
    video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);
 
    video::S3DVertex2TCoords* verts = (video::S3DVertex2TCoords*)terrain->getMesh()->getMeshBuffer(0)->getVertices();
 
    for (u32 y= 0, i = 0; y < heightmapHeight; y++){
        for(u32 x = 0; x < heightmapWidth; i++, x++){
            u8 py = (u8)verts[i].Pos.Y;
            img->setPixel((heightmapHeight-1)-y, x, video::SColor(0, py, py, py));
        }
    }
 
    driver->writeImageToFile(img, "heightmap.bmp", 0);
    img->drop();
}
 
 
int main(){
    MyEventReceiver receiver;
 
    IrrlichtDevice* device = createDevice(video::EDT_OPENGL, core::dimension2d<u32>(800, 600), 32, false, true, false, &receiver);
 
    video::IVideoDriver* driver = device->getVideoDriver();
    scene::ISceneManager* smgr = device->getSceneManager();
 
    device->getCursorControl()->setVisible(false);
 
    io::path heightmapFile = "heightmap.bmp";
    heightmap = driver->createImageFromFile(heightmapFile);
    brush = driver->createImageFromFile("brush.png");
 
    terrain = smgr->addTerrainSceneNode(heightmapFile, 0, -1, core::vector3df(0, 0, 0));
    terrain->setScale(core::vector3df(32, 5, 32));
    terrain->setMaterialFlag(video::EMF_LIGHTING, false);
 
    terrain->setPosition(terrain->getPosition());
 
    scene::ITriangleSelector* selector = smgr->createTerrainTriangleSelector(terrain, 0);
 
    // Arrow
    scene::ISceneNode* arrow = smgr->addAnimatedMeshSceneNode(smgr->addArrowMesh("arrow", video::SColor(255, 255, 0, 0), video::SColor(255, 0, 255, 0)), NULL);
    arrow->setMaterialFlag(video::EMF_LIGHTING, false);
    arrow->setScale(core::vector3df(10, 10, 10));
    arrow->setRotation(core::vector3df(0,0,180));
 
    scene::ICameraSceneNode* cam = smgr->addCameraSceneNodeFPS(0, 100.0f, .1f);
    cam->setPosition(core::vector3df(-100,500,100));
    cam->setTarget(core::vector3df(0,0,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);
 
                // move the arrow to the nearest vertex ...
                //400, 300 si la résolution utilisée est 800x600
                const core::position2di clickPosition = core::position2di(400, 300);
                const core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(clickPosition, cam);
                core::vector3df pos;
                core::triangle3df Tri;
 
                const scene::ISceneNode* out;
                if (smgr->getSceneCollisionManager()->getCollisionPoint(ray, selector, pos, Tri, out)){
                    //arrow->setPosition(pos);
                    static const s32 scale = 32; // terrain is scaled 32X
                    static const s32 size = 512; // heightmap is 512x512 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(index, step, receiver.IsLMBDown());
                        then = now + 100;
                    }
 
                    x *= scale;
                    z *= scale;
 
                    arrow->setPosition(core::vector3df(x, terrain->getHeight(x, z) + 20, z));
                }
 
                driver->beginScene(true, true, video::SColor(255, 255, 255, 255));
                smgr->drawAll();
                driver->endScene();
 
                then30 = now + 30;
            }
        }
    }
 
    heightmap->drop();
    brush->drop();
 
    device->closeDevice();
    device->drop();
 
    return 0;
}


I used a black 512x512 image as heightmap, and that brush : Image

Have fun :) .
Last edited by Luiz on Sat Sep 10, 2011 1:01 pm, edited 2 times in total.
Luiz
 
Posts: 4
Joined: Sat Aug 20, 2011 4:54 pm

Re: Terrain painting with brush

Postby tecan » Sat Sep 10, 2011 9:26 am

awesomeness thanks!
Live long and phosphor!
--Luna - Status 60%
User avatar
tecan
 
Posts: 284
Joined: Fri Jun 06, 2008 12:50 pm
Location: Edmonton, Alberta, Canada

Re: Terrain height painting with brush

Postby Luiz » Sat Sep 10, 2011 1:58 pm

Here you are 2 screenshots textured and wireframe :
Image
Image

And it would be cool to share the brushes if you make some.

mines are : ImageImageImageImage
Luiz
 
Posts: 4
Joined: Sat Aug 20, 2011 4:54 pm

Re: Terrain height painting with brush

Postby tbw » Sat Sep 10, 2011 5:46 pm

Really good job! thank you for sharing.
tbw
 
Posts: 58
Joined: Sat Jan 15, 2011 9:51 am
Location: Germany

Re: Terrain height painting with brush

Postby fmx » Sun Sep 11, 2011 7:59 pm

Interesting, thanks for sharing
User avatar
fmx
 
Posts: 559
Joined: Wed Dec 06, 2006 9:28 am
Location: UK

Re: Terrain height painting with brush

Postby Brainsaw » Mon Sep 12, 2011 11:21 am

This is great. I think I'll give it a try and maybe create an editor of my own from it.
User avatar
Brainsaw
 
Posts: 1038
Joined: Wed Jan 07, 2004 12:57 pm
Location: Bavaria

Re: Terrain height painting with brush

Postby tecan » Mon Sep 12, 2011 3:37 pm

if you guys figure out how to fix the scaling issue before i do plz lemme know. move the camera a bit and it no longer raises above the arrow
Live long and phosphor!
--Luna - Status 60%
User avatar
tecan
 
Posts: 284
Joined: Fri Jun 06, 2008 12:50 pm
Location: Edmonton, Alberta, Canada

Re: Terrain height painting with brush

Postby shadowslair » Mon Sep 12, 2011 5:46 pm

tecan wrote:if you guys figure out how to fix the scaling issue before i do plz lemme know. move the camera a bit and it no longer raises above the arrow
Probably your heightmap resolution is not the same as the one the example assumes?
cpp Code: Select all
static const s32 size = 512; //heightmap is 512x512 pixels
In this case heightmap needs to be 512x512
Image
"Although we walk on the ground and step in the mud... our dreams and endeavors reach the immense skies..."
User avatar
shadowslair
 
Posts: 758
Joined: Mon Mar 31, 2008 3:32 pm
Location: Bulgaria

Re: Terrain height painting with brush

Postby Luiz » Mon Sep 12, 2011 9:14 pm

Glad to see that my code interest you, I'll continue to improve it (or die trying :twisted: ).

Brainsaw wrote:This is great. I think I'll give it a try and maybe create an editor of my own from it.

That's what I'm doing actually, because I was looking for a level editor for GNU/Linux and I finaly figured out that there's nothing else than Blender for terrain editing (may be i'm wrong, so if you got something i realy would like to know what it is, and what it worth as level/terrain editor).
Luiz
 
Posts: 4
Joined: Sat Aug 20, 2011 4:54 pm

Re: Terrain height painting with brush

Postby Brainsaw » Tue Sep 13, 2011 5:46 am

Luiz wrote:Glad to see that my code interest you, I'll continue to improve it (or die trying :twisted: ).

Brainsaw wrote:This is great. I think I'll give it a try and maybe create an editor of my own from it.

That's what I'm doing actually, because I was looking for a level editor for GNU/Linux and I finaly figured out that there's nothing else than Blender for terrain editing (may be i'm wrong, so if you got something i realy would like to know what it is, and what it worth as level/terrain editor).


I started a project yesterday, but I spent all the time in creating a texture from the heightmap (and failed), so I'll first add the necessary features for height painting. I'm programming on Windows, but as this project will also be Irrlicht-only it should compile on Linux as well. I'll post a link here once I got a first version ready.
User avatar
Brainsaw
 
Posts: 1038
Joined: Wed Jan 07, 2004 12:57 pm
Location: Bavaria

Re: Terrain height painting with brush

Postby tecan » Tue Oct 11, 2011 12:45 am

http://www.xup.in/dl,85839677/terrain-painting.7z/

not much more just a more complete project to work on , happy editing and thanks giving
Live long and phosphor!
--Luna - Status 60%
User avatar
tecan
 
Posts: 284
Joined: Fri Jun 06, 2008 12:50 pm
Location: Edmonton, Alberta, Canada

Re: Terrain height painting with brush

Postby wiedzmin112 » Thu Oct 27, 2011 12:46 pm

Hi!
I write new save and load function.
These not use heightmap but bin format.
Now you can create higher hills :)
Binary Format.h
cpp Code: Select all
 
#ifndef _BINARY_FORMAT_H_
#define _BINARY_FORMAT_H_
 
#include <string>
#include <fstream>
#include <vector>
//Binary format VERSION 0.1 alpha
 
 
namespace BinaryFormat
{
        class Root
        {
        private:
                Root() {}
                Root(Root&) {}
        public:
                std::ofstream ostream;
                std::ifstream istream;
                static Root &Singleton()
                {
                        static Root Instance;
                        return Instance;
                }
        };
 
        //Binary - base class for all object's.
        //version 1.0 2011-09-14
        template<class type>
        class Binary
        {
        public:
                Binary() {}
 
                Binary(type obj) : object(obj) {}
 
                type object;
 
                static void save(Binary<type> *obj)
                {
                        Root::Singleton().ostream.write((char*)obj,sizeof(Binary<type>));
                }
 
                static void save(type obj)
                {
                        Root::Singleton().ostream.write((char*)new Binary<type>(obj),sizeof(Binary<type>));
                }
 
                static void load(Binary<type> *obj)
                {
                        Root::Singleton().istream.read((char*)obj,sizeof(Binary<type>));
                }
        };
 
        //Some useful types for creating object's.
        typedef Binary<int> Int;
        typedef Binary<float> Float;
        typedef Binary<bool> Bool;
        typedef Binary<char> Char;
 
        class String
        {
        public:
                std::string object;
                static void save(String* s)
                {
                        Int::save(s->object.length());
                        for(unsigned int i=0;i<s->object.length();i++)
                        {
                                Char::save(s->object[i]);
                        }
                }
 
                static void load(String *s)
                {
                        Int *length=new Int(-1);
                        Int::load(length);
                        for(int i=0;i<length->object;i++)
                        {
                                Char *temp=new Char();
                                Char::load(temp);
                                s->object+=temp->object;
                        }
                }
        };
 
 
 
        template<class type>
        class BinaryVector
        {
        public:
                BinaryVector() {}
               
                BinaryVector(std::vector<type> &obj) : object(obj) {}
 
                std::vector<type> object;
 
                static void saveptr(std::vector<type*> obj)
                {
                        Int::save(obj.size());//save vector size
                        for(unsigned int i=0; i < obj.size(); i++)
                                obj[i]->save();
                }
 
                static void saveptrObject(std::vector<type*> obj)
                {
                        Int::save(obj.size());//save vector size
                        for(unsigned int i=0; i < obj.size(); i++)
                                type::save(obj[i]);
                }
 
                static void save(std::vector<type> obj)
                {
                        Int::save(obj.size());//save vector size
                        for(unsigned int i=0; i < obj.size(); i++)
                                obj[i].save();
                }
 
                static void load(std::vector<type> &obj)
                {
                        Int *size=new Int();
                        Int::load(size);
                        for(int i=0; i < size->object; i++)
                        {
                                type temp;
                                temp.load();
                                obj.push_back(temp);
                        }
                }
 
                static void loadptr(std::vector<type*> &obj)
                {
                        Int *size=new Int();
                        Int::load(size);
                        for(int i=0; i < size->object; i++)
                        {
                                type *temp=new type();
                                temp->load();
                                obj.push_back(temp);
                        }
                }
 
                static void loadptrObject(std::vector<type*> &obj)
                {
                        Int *size=new Int();
                        Int::load(size);
                        for(int i=0; i < size->object; i++)
                        {
                                type *temp=new type();
                                type::load(temp);
                                obj.push_back(temp);
                        }
                }
        };
 
        class Terrain
        {
        public:
 
                Float ***Height;
 
                Terrain() {}
                int s;
                void GenerateTable(int size)
                {
                        s=size;
                        Height=new Float**[size];
                        for(int i=0;i<size;i++)
                        {
                                Height[i]=new Float*[size];
                                for(int x=0;x<size;x++)
                                        Height[i][x]=new Float();
                        }
                }
 
                void save()
                {
                        Int::save(s);
                        for(int i=0;i<s;i++)
                        {
                                for(int x=0;x<s;x++)
                                {
                                        Float::save(Height[i][x]);
                                }
                        }
                }
 
                void load()
                {
                        Int *size;
                        Int::load(size);
                        GenerateTable(size->object);
                        for(int i=0;i<s;i++)
                        {
                                for(int x=0;x<s;x++)
                                {
                                        Float::load(Height[i][x]);
                                }
                        }
                }
        };
}
 
 
#endif
//wiedzmin112 2011-09-15
//For non-commercial use
 



And modifed main.cpp
cpp Code: Select all
 
...
#include "Binary Format.h"
 
...
 
/*==============================================================================
Save file
==============================================================================*/

void save (video::IVideoDriver* driver){
        BinaryFormat::Root::Singleton().ostream=std::ofstream("binTerrain",std::ios::binary);
        BinaryFormat::Terrain *bin=new BinaryFormat::Terrain();
 
        s32 heightmapWidth = heightmap->getDimension().Width;
        s32 heightmapHeight = heightmap->getDimension().Height;
 
        if(heightmapWidth!=heightmapHeight)
                return;
        bin->GenerateTable(heightmapWidth);
 
 
        video::S3DVertex2TCoords* verts = (video::S3DVertex2TCoords*)terrain->getMesh()->getMeshBuffer(0)->getVertices();
 
        for (s32 y= 0, i = 0; y < heightmapHeight; y++){
                for(s32 x = 0; x < heightmapWidth; i++, x++){
                        u8 py = (u8)verts[i].Pos.Y;
                        bin->Height[y][x]->object=verts[i].Pos.Y;
                }
        }
 
 
 
        //and save to binary
 
        bin->save();
        BinaryFormat::Root::Singleton().ostream.close();
}
 
/*==============================================================================
Load file
==============================================================================*/

 
void load()
{
        BinaryFormat::Root::Singleton().istream=std::ifstream("binTerrain",std::ios::binary);
        BinaryFormat::Terrain *bin=new BinaryFormat::Terrain();
       
        s32 heightmapWidth = heightmap->getDimension().Width;
        s32 heightmapHeight = heightmap->getDimension().Height;
 
        if(heightmapWidth!=heightmapHeight)
                return;
        bin->GenerateTable(heightmapWidth);
        bin->load();
        video::S3DVertex2TCoords* verts = (video::S3DVertex2TCoords*)terrain->getMesh()->getMeshBuffer(0)->getVertices();
 
        for (s32 y= 0, i = 0; y < heightmapHeight; y++)
        {
                for(s32 x = 0; x < heightmapWidth; i++, x++)
                {
                        verts[i].Pos.Y=bin->Height[y][x]->object;
                }
        }
        terrain->setPosition(terrain->getPosition());
        BinaryFormat::Root::Singleton().istream.close();
}
int main(){
        MyEventReceiver receiver;
 
        IrrlichtDevice* device = createDevice(video::EDT_DIRECT3D9, core::dimension2d<u32>(800, 600), 32, false, true, false, &receiver);
 
 
        video::IVideoDriver* driver = device->getVideoDriver();
        scene::ISceneManager* smgr = device->getSceneManager();
 
        device->getCursorControl()->setVisible(false);
 
        io::path heightmapFile = "heightmap.bmp";
        heightmap = driver->createImageFromFile(heightmapFile);
        brush = driver->createImageFromFile("brush.png");
 
        terrain = smgr->addTerrainSceneNode(heightmapFile);
        terrain->setScale(core::vector3df(32, 5, 32));
        terrain->setMaterialFlag(video::EMF_LIGHTING, false);
 
        terrain->setMaterialTexture(0,
                driver->getTexture("grass.jpg"));
        terrain->scaleTexture(100, 100);
        terrain->setPosition(terrain->getPosition());
 
        scene::ITriangleSelector* selector = smgr->createTerrainTriangleSelector(terrain, 0);
 
        // Arrow
        scene::ISceneNode* arrow = smgr->addAnimatedMeshSceneNode(smgr->addArrowMesh("arrow", video::SColor(255, 255, 0, 0), video::SColor(255, 0, 255, 0)), NULL);
        arrow->setMaterialFlag(video::EMF_LIGHTING, false);
        arrow->setScale(core::vector3df(10, 10, 10));
        arrow->setRotation(core::vector3df(0,0,180));
 
        scene::ICameraSceneNode* cam = smgr->addCameraSceneNodeFPS(0, 100.0f, .1f);
        cam->setPosition(core::vector3df(-100,500,100));
        cam->setTarget(core::vector3df(0,0,0));
 
        ITimer* irrTimer = device->getTimer();
        u32 then = 0, then30 = 0;
        f32 step = 2.f;
        bool wireframe = false;
 
        load();
        ...
}
 




Soory for my bad english :)


Have fun!
wiedzmin112
 
Posts: 30
Joined: Tue Oct 18, 2011 3:48 pm

Re: Terrain height painting with brush

Postby hybrid » Thu Oct 27, 2011 1:01 pm

Not to stop you from re-inventing the wheel. But Irrlicht has a RAW loader for terrain, which also supports 32bit floats. You can simply pass in the binary file and some parameters to let Irrlicht know about the shape of the information.
hybrid
Admin
 
Posts: 13946
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany

Re: Terrain height painting with brush

Postby wiedzmin112 » Thu Oct 27, 2011 4:34 pm

Thanks for reply but i wrote this format specially for my world editor(i want to be all objects in one file).
wiedzmin112
 
Posts: 30
Joined: Tue Oct 18, 2011 3:48 pm

Re: Terrain height painting with brush

Postby hybrid » Thu Oct 27, 2011 8:56 pm

Well, the format is not the point. You can just remove about 95% of your loading code by simply using the RAW loader (just one line after terrain creation). And it's also much faster, because you can create the point directly t the proper places.
hybrid
Admin
 
Posts: 13946
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany

Next

Return to Code Snippets

Who is online

Users browsing this forum: Triadian and 0 guests