A few questions from a first-timer

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Post by Acki »

hybrid wrote:Also note that you need to use clockwise orientation of the faces.
not neccessarily, it depends on from what side you want it to see... :lol:
if you want it to see from the front than it must be orientated anti-clockwise, if you want it to see from the back than it must be clockwise... ;)
(I know you know this, hybrid, I just wanted to make this clear)
Last edited by Acki on Wed Nov 05, 2008 5:27 pm, edited 1 time in total.
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
jddevnet
Posts: 17
Joined: Mon Oct 06, 2008 8:27 pm

Post by jddevnet »

geoff wrote:Suddenly it all makes perfect sense.

When I've got it all working, which surely shouldn't be too far away, would you be interested in the relevant part of my code, since I'm probably not the first person who's likely to want to initialise a mesh from a heightmap in this way?
This thread has been most helpful for me as well! Hybrid's last picture made it "click". For me, I've been learning the Custom Scene Node, which seems a bit related. I, for one, would happy to see your working code.
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

geoff wrote:Suddenly it all makes perfect sense.

When I've got it all working, which surely shouldn't be too far away, would you be interested in the relevant part of my code, since I'm probably not the first person who's likely to want to initialise a mesh from a heightmap in this way?
Yes, as said it would make perfect sense to show some stuff with the SMesh struct. And this example seems like it is simple enough to keep in comprehensible, but still show all necessary stuff with a real case study. I'll add some more SMesh methods later on.
The custom scene node is indeed quite related, but it doesn't use SMesh and stuff. We could have changed that example as well, but for a custom node it might make sense to avoid SMesh and the Irrlicht structures to some extent, so I'll leave it as is and just point to the new tutorial as well.
geoff
Posts: 9
Joined: Sat Oct 25, 2008 7:10 pm

Post by geoff »

OK then; here's the final working version. There isn't much complicated about it, although you may want to change some of the comments. Don't hesitate to ask if something doesn't make sense. Once again, many thanks for your help with this; what irrlicht does with it is very impressive.

Code: Select all

#include <irrlicht.h>
#include <iostream>
#include <math.h>

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
using namespace std;

//////////////////////////////////////////////////////////////////////////////

// The type of the functions which work out the colour. None of these
// have any effect with the OPENGL driver.

typedef SColor colour_func(f32 x, f32 y, f32 z);

// Greyscale, based on the height.

SColor grey(f32, f32, f32 z)
{
  u8 n = 255 * z;
  return SColor(255, n, n, n);
}

// Interpolation between blue and white, with red added in one
// direction and green in the other.

SColor yellow(f32 x, f32 y, f32)
{
  return SColor(255, 128 + 127 * x, 128 + 127 * y, 255);
}

// Pure white.

SColor white(f32, f32, f32) { return SColor(255, 255, 255, 255); }

// The type of the functions which generate the heightmap. x and y
// range between -0.5 and 0.5, and r = sqrt(x * x + y * y), i.e. the
// distance from the centre of the heightmap.

typedef f32 generate_func(s16 x, s16 y, f32 r);

// An interesting sample function :-)

f32 eggbox(s16 x, s16 y, f32 r)
{
  f32 z = exp(-r * 2) * (cos(0.2 * x) + cos(0.2 * y));
  return 0.25 + 0.25 * z;
}

//////////////////////////////////////////////////////////////////////////////

// A simple class for representing heightmaps. Most of this should be
// obvious.

class HeightMap
{
private:
  u16 w, h;
  f32 s, *data;
public:
  HeightMap(u16 w, u16 h)
  {
    this -> w = w; this -> h = h;
    s = sqrt(w * w + h * h) / 4.0;

    data = new f32[w * h];
  }

  ~HeightMap() { delete [] data; }

  // Fill the hieghtmap with values generated from f.

  void generate(generate_func f)
  {

    for(u16 y = 0; y < h; y ++)
      for(u16 x = 0; x < w; x ++)
        set(x, y, calc(f, x, y));
  }

  u16 height() { return h; }
  u16 width()  { return w; }

  f32 calc(generate_func f, u16 x, u16 y)
  {
    f32 xx = x * 1.0 - w / 2, yy = y * 1.0 - h / 2;
    f32 r = sqrt(xx * xx + yy * yy);
    return f(xx, yy, r / s);
  }

  // The height at (x, y) is at position y * w + x.

  void set(u16 x, u16 y, f32 z) { data[y * w + x] = z; }
  f32  get(u16 x, u16 y) { return data[y * w + x]; }

  // The only difficult part. This considers the normal at (x, y) to
  // be the cross product of the vectors between the adjacent points
  // in the horizontal and vertical directions. Please don't tell me
  // irrlicht already does this somewhere.
  //
  // s is a scaling factor, which is necessary if the height units are
  // different from the coordinate units; for example, if your map has
  // heights in metres and the coordinates are in units of a
  // kilometre.

  vector3df getnormal(u16 x, u16 y, f32 s)
  {
    f32 zc = get(x, y), zl, zr, zu, zd;

    if(     x == 0)     { zr = get(x + 1, y); zl = zc + zc - zr; }
    else if(x == w - 1) { zl = get(x - 1, y); zr = zc + zc - zl; }
    else                { zr = get(x + 1, y); zl = get(x - 1, y); }

    if(     y == 0)     { zd = get(x, y + 1); zu = zc + zc - zd; }
    else if(y == h - 1) { zu = get(x, y - 1); zd = zc + zc - zu; }
    else                { zd = get(x, y + 1); zu = get(x, y - 1); }

    return vector3df(s * 2 * (zl - zr), 4, s * 2 * (zd - zu)).normalize();
  }
};

//////////////////////////////////////////////////////////////////////////////

// A class which generates a mesh from a heightmap.

class TMesh: public SMesh
{
private:
  u16 w, h;
  f32 s;
public:

  // This constructor takes an actual heightmap as its first parameter...

  TMesh(HeightMap &hm, f32 scale, colour_func cf, IVideoDriver *driver)
    : SMesh()
  {
    init(hm, scale, cf, driver);
  }

  // ... whereas this one generates it for you.

  TMesh(u16 w, u16 h, generate_func f, f32 scale,
        colour_func cf, IVideoDriver *driver)
  {
    HeightMap hm = HeightMap(w, h);
    hm.generate(f);
    init(hm, scale, cf, driver);
  }

  // Unless the heightmap is small, it won't all fit into a single
  // SMeshBuffer. This function chops it into pieces and generates a
  // buffer from each one.

  void init(HeightMap &hm, f32 scale, colour_func cf, IVideoDriver *driver)
  {
    u32 mp = driver -> getMaximalPrimitiveCount();
    w = hm.width(); h = hm.height(); s = scale;

    u16 sw = mp / (6 * h); // the width of each piece

    for(u16 y0 = 0; y0 < h; y0 += sw)
    {
      u16 y1 = y0 + sw;
      if(y1 >= h) y1 = h - 1; // the last one might be narrower
      addstrip(hm, cf, y0, y1);
    }

    recalculateBoundingBox(); 
  }

  // Add a vertex representing the height at (x, y) to the buffer, and
  // colour it according to cf (the OPENGL driver ignores this,
  // unfortunately).

  void addpoint(SMeshBuffer *buf, HeightMap &hm, colour_func cf, u16 x, u16 y)
  {
    f32       z     = hm.get(x, y);
    f32 xx = (1.0 * x) / w, yy = (1.0 * y) / h;
    vector3df pos   = vector3df(x, s * z, y);
    vector3df norm  = hm.getnormal(x, y, s);
    SColor    c     = cf(xx, yy, z);
    vector2d<f32> t = vector2d<f32>(0, 0); 
    buf -> Vertices.push_back(S3DVertex(pos, norm, c, t));
  }

  // Add the six indices which make up the two triangles at (x, y) to
  // the buffer.

  void addindices(SMeshBuffer *buf, u16 x, u16 y)
  {
    u16 n = y * w + x;
    buf -> Indices.push_back(n);
    buf -> Indices.push_back(n + h);
    buf -> Indices.push_back(n + h + 1);
    buf -> Indices.push_back(n + h + 1);
    buf -> Indices.push_back(n + 1);
    buf -> Indices.push_back(n);
  }

  // Generate a SMeshBuffer which represents all the vertices and
  // indices for values of y between y0 and y1, and add it to the
  // mesh.

  void addstrip(HeightMap &hm, colour_func cf, u16 y0, u16 y1)
  {
    SMeshBuffer *buf = new SMeshBuffer(); 
    addMeshBuffer(buf); 
    buf -> Vertices.reallocate((1 + y1 - y0) * w);

    for(u16 y = y0; y <= y1; y ++)
      for(u16 x = 0; x < w; x ++)
        addpoint(buf, hm, cf, x, y);

    buf -> Indices.reallocate(6 * (w - 1) * (y1 - y0));

    for(u16 y = y0; y < y1; y ++)
      for(u16 x = 0; x < w - 1; x ++)
        addindices(buf, x, y - y0);

    buf -> recalculateBoundingBox(); 
  }
};

//////////////////////////////////////////////////////////////////////////////

// Much of this is code taken from some of the examples. We merely set
// up a mesh from a heightmap, light it with a moving light, and allow
// the user to navigate around it.

int main()
{
  // The software driver simply isn't up to this, although it does
  // show the colours.
  video::E_DRIVER_TYPE driverType = video::EDT_OPENGL;

  IrrlichtDevice* device = createDevice(driverType,
                                        core::dimension2d<s32>(640, 480));

  if(device == 0) return 1;
  
  IVideoDriver    *driver = device -> getVideoDriver();
  ISceneManager   *smgr   = device -> getSceneManager();
  IGUIEnvironment *env    = device -> getGUIEnvironment();

  TMesh mesh = TMesh(255, 255, eggbox, 50.0, yellow, driver);
  smgr -> addMeshSceneNode(&mesh);

  ISceneNode *node = smgr->addLightSceneNode(0, vector3df(0,0,0),
	 SColorf(1.0f, 0.6f, 0.7f, 1.0f), 1200.0f);
  ISceneNodeAnimator* anim = 0;
  anim = smgr->createFlyCircleAnimator (vector3df(0,150,0),250.0f);
  node->addAnimator(anim);
  anim->drop();

  ICameraSceneNode* camera = 
		smgr->addCameraSceneNodeFPS(0,100.0f,1200.f);

  camera->setPosition(vector3df(400.f, 400.f, 0.f));
  camera->setTarget(vector3df(127.f, 0.f, 127.f));
  camera->setFarValue(12000.0f);

  while(device->run())
  {
    if(!device->isWindowActive()) continue;

    driver->beginScene(true, true, SColor(0, 0, 0, 0));
    smgr->drawAll();
    env->drawAll();
    driver->endScene();
  }

  device->drop();

  return 0;
}

//////////////////////////////////////////////////////////////////////////////
Post Reply