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!
geoff
Posts: 9
Joined: Sat Oct 25, 2008 7:10 pm

A few questions from a first-timer

Post by geoff »

Hi,

I've been looking for a decent 3D engine for a project of mine, and irrlicht looks very impressive and *almost* what I'm after. But I'm not quite sure yet if it meets my requirements, and would greatly appreciate it if someone could put my mind at rest. I'm too lazy to wade through all the documentation, and I don't really need much of the functionality of irrlicht, although the speed is *very* nice.

What I'm trying to do is display an arbitrary rectangular heightmap in three dimensions, similar to but not quite the same as the terrain demo (#12), with one distant light source, and with an arbitrary colour for each point. For example, the heightmap could represent of a landscape, with the colours representing quantized levels (as in an atlas map) and the light source representing the Sun, or it could be the solution to an equation in three dimensions with an arbitrary image overlaid. So:

1. Currently, the code only seems to accept square heightmaps. Is there a way to allow rectangular heightmaps too?

2. The file CTerrainSceneMap.cpp has two very similar functions for loading heightmaps, one which reads from a bitmap and one which reads raw data. Would it be possible to move the code which sets the individual heights into its own function, so that it's easy to add support for other formats without having to copy the entire function verbatim?

3. How would I set a single light source to cast shadows?

4. How would I associate a particular colour with a single point in the heightmap?

I hope this makes some kind of sense! Thanks in advance for any help.
geoff
Posts: 9
Joined: Sat Oct 25, 2008 7:10 pm

Post by geoff »

Is this really unanswerable...?
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

I think the problem is that no one really understood what you intend to do. The questions are not really that hard:
Improvability of the terrain scene node is rather hard to argue about, because it's a huge code basis which was written for this specific purpose. So I doubt that rectangular terrain maps are easily addable, but should be possible. However the terrain mesh should support this (and I guess that you don't need LOD anyway)!
You can load your heightmap manually into an IImage structure and pass that one to the terrain node. This allows arbitrary loaders. But if you have some suggestions we'll happily look into it.
Just create one light node and add the shadow volume node to your meshes that should cast a shadow.
You need access to the vertices (where you could add vertex colors) or use a texture which spans the terrain exactly.
geoff
Posts: 9
Joined: Sat Oct 25, 2008 7:10 pm

Post by geoff »

OK; thanks for reassuring me that I wasn't asking the impossible! Please excuse my ignorance of the workings of irrlicht; I will probably still have to ask too many stupid questions here.
hybrid wrote:Improvability of the terrain scene node is rather hard to argue about, because it's a huge code basis which was written for this specific purpose. So I doubt that rectangular terrain maps are easily addable, but should be possible. However the terrain mesh should support this (and I guess that you don't need LOD anyway)!
What is LOD? Load-on-demand???
hybrid wrote:You can load your heightmap manually into an IImage structure and pass that one to the terrain node. This allows arbitrary loaders. But if you have some suggestions we'll happily look into it.
What I'd ideally like is a function which loads a rectangular heightmap (or terrain node?) with 16-bit values from a file, which can be reimplemented to allow arbitrary file formats. I'm not sure if I'm looking at the right header file, but IImage.h talks about images, not anything obviously related to heightmaps.
hybrid wrote:Just create one light node and add the shadow volume node to your meshes that should cast a shadow.
I'm sure this can be done with one line of code, but I'm at a loss to figure out how :(
hybrid wrote:You need access to the vertices (where you could add vertex colors) or use a texture which spans the terrain exactly.
Adding vertex colours sounds like what I want to do.

Maybe I should explain my main problem a bit more clearly... I'm trying to do a few very simple things, but there is rather a lot of source code to read before I can figure out how to do them in irrlicht, even if it's just one line of code. So, while "RTFM" may be a reasonable answer, there is a lot of TFM to R, and I'm not sure where TFM actually is.

Anyway, if I start from the terrain demo, could you tell me the specific function calls would I need to add to it so that I can do the following, or at least point me in the right direction?

- load my rectangular heightmap from a file
- add a lightnode, move it around, and make it cast shadows
- add (or set?) vertex colours arbitrarily

Thanks again.
fmx

Post by fmx »

LOD = Level of Detail
things further away having less polys/details, things closer having more.
particularly useful for terrains (which is why hybrid mentioned them), helps boost rendering speeds

maybe you could have a look at arras' terrain scene node too (Projects section), see if that helps?
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

For the shadow you should check example 8 from the tutorials, should explain everything. The terrain mesh (which is not restricted in dimensions, but also doesn't feature things like LOD) can be added directly from the SceneManager. The online docs will help
http://irrlicht.sourceforge.net/docu/index.html
Since the terrain mesh (in contrast to the terrain scene node) is a simple IMesh, you can access it via the typical IMesh methods. Direct access to the vertices allows to alter those values, including the vertex colors.
heightmaps are simply images, usually greymaps. So creating an image from the heightmap is a pretty usual idea. Reading the data with the IReadFile methods is simple, so if you need a special RAW format it's not too much to implement.
geoff
Posts: 9
Joined: Sat Oct 25, 2008 7:10 pm

Post by geoff »

Many thanks; it's all starting to make a lot more sense now!
geoff
Posts: 9
Joined: Sat Oct 25, 2008 7:10 pm

Almost there...

Post by geoff »

OK, I think I just need to ask two more stupid questions, and I should be able to continue by myself.

1. Is it correct to use an IMeshManipulator to set the vertex colours?

2. The code below shows the current state of my program. I've set up a test mesh with a ripple-like pattern (that's the sin(sqrt(...)) bit) and am trying to look at it from directly above, with the light in the same place as the camera, but I see nothing. Setting all the mesh values to zero makes no difference. What have I forgotten to do?

Geoff

Code: Select all

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

using namespace irr;

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

class TMesh: public irr::scene::SMesh
{
private:
  int w, h;
  u32 **data;
  u16 **I;
public:

  TMesh(u16 w, u16 h) : irr::scene::SMesh()
  {
    u16 s = sqrt(w * w + h * h) / 4;

    for(u16 i = 0; i < h; i ++)
    {
      u32 *data = new u32[w];
      u16 *ind  = new u16[w];

      for(u16 j = 0; j < w; j ++)
      {
        u16 ww = j - w / 2, hh = i - h / 2;
        data[j] = sin(sqrt(ww * ww + hh * hh) / s);
        ind[j]  = j;
      }

      scene::SMeshBuffer *buf = new scene::SMeshBuffer();
      buf -> append(data, w, ind, w);
      addMeshBuffer(buf);
    }
  }
};

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

int main()
{
  IrrlichtDevice* device = createDevice(video::EDT_OPENGL,
                                        core::dimension2d<s32>(640, 480));

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

  core::vector3df cam    = core::vector3df( 0.f, 100.f, 0.f);
  core::vector3df target = core::vector3df( 0.f,   0.f, 0.f);
  scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0, cam, target);

  TMesh m = TMesh(32, 32);

  smgr->addMeshSceneNode(&m);
  smgr->addLightSceneNode(0, cam);

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

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

  device->drop();

  return 0;
}

//////////////////////////////////////////////////////////////////////////////
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Well, MeshBuffers don't take just a u32 as data, but a S3DVertex (how else should we render 3d data :P ) So look up the S3DVertex struct and use the following way to fill the buffer:

Code: Select all

scene::SMeshBuffer *buf = new scene::SMeshBuffer();
buf->Vertices.reallocate(w*h);
addMeshBuffer(buf); 

// in the i/j loop, check the other values on your own, normals are necessary for lighting and vertex color is directly accessible here :-)
buf->Vertices.pushback(S3DVertex(i,j,sin(sqrt(ww * ww + hh * hh) / s), ...);

// and after the loop
recalculateBoundingBox();
I hope I have put everything there now. I guess we should make a new tutorial with this code for people to know how to work with the SMesh and SMeshBuffer :)
geoff
Posts: 9
Joined: Sat Oct 25, 2008 7:10 pm

Post by geoff »

Still not there yet! Here's the revised definition of TMesh, which generates a completely flat surface with normals pointing straight up:

Code: Select all

class TMesh: public SMesh
{
private:
  int w, h;
public:

  TMesh(u16 w, u16 h) : SMesh()
  {
    SMeshBuffer *buf = new SMeshBuffer(); 
    buf->Vertices.reallocate(w * h);
    addMeshBuffer(buf); 

    for(u16 y = 0; y < h; y ++)
    {
      for(u16 x = 0; x < w; x ++)
      {
        f32 xx = 1.0 * (x - w / 2), yy = 1.0 * (y - h / 2);
        f32  z = 0.0;
        f32 nx = 0.0, ny = 1.0, nz = 0.0;

        SColor    c = SColor(255, 255, 255, 255);
        S3DVertex v = S3DVertex(xx, yy, z, nx, ny, nz, c, 0, 0);
        buf->Vertices.push_back(v);
      }
    }

    buf -> recalculateBoundingBox(); 
    recalculateBoundingBox(); 
  }
};
This still shows totally black, and changing the normals makes no difference. Although, if I ignore TMesh and do this instead:

Code: Select all

  IAnimatedMesh* imesh = smgr->getMesh("sydney.md2");
  IMesh *pmesh = imesh -> getMesh(0);
  smgr->addMeshSceneNode(pmesh);
I get something recognisable.
hybrid wrote:I guess we should make a new tutorial with this code for people to know how to work with the SMesh and SMeshBuffer :)
Yes, that would be very useful :-)
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Why not diabling the lighting of the mesh for a test first, until all things with the mesh as is are figured out?
geoff
Posts: 9
Joined: Sat Oct 25, 2008 7:10 pm

Post by geoff »

Some progress, but much weirdness now. Here's the latest version of my code:

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;

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

class TMesh: public SMesh
{
private:
  int w, h;
public:
  TMesh(u16 w, u16 h) : SMesh()
  {
    SMeshBuffer *buf = new SMeshBuffer(); 
    addMeshBuffer(buf); 
    buf -> Vertices.reallocate(w * h);
    buf -> Indices.reallocate( w * h);

    SColor c = SColor(255, 255, 255, 255);
    u16    n = 0;

    for(s16 y = -h / 2; y < h / 2; y ++)
    {
      for(s16 x = -w / 2; x < w / 2; x ++)
      {
        vector3df pos   = vector3df(x, 0, y);
        vector3df norm  = vector3df(1, 0, 0);
        vector2d<f32> t = vector2d<f32>(0, 0);
        buf -> Vertices.push_back(S3DVertex(pos, norm, c, t));
        buf -> Indices.push_back(n ++);
      }
    }

    buf -> recalculateBoundingBox(); 
    recalculateBoundingBox(); 
  }
};

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

int main()
{
  video::E_DRIVER_TYPE driverType = video::EDT_SOFTWARE;

  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();

  vector3df cam    = vector3df( 0.f, 15.f, 0.f);
  vector3df target = vector3df( 0.f,  0.f, 0.f);
  vector3df light  = vector3df(10.f,  0.f, 0.f);
  TMesh     mesh   = TMesh(15, 15);

  smgr -> addMeshSceneNode(&mesh);
  smgr -> addCameraSceneNode(0, cam, target);
  smgr -> addLightSceneNode(0, light);

  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;
}
I found out that setting up the indices (buf -> Indices) caused some things to be displayed. However, I don't understand why it should be necessary to create a list which merely contains the nonnegative integers in sequence; why do you need an array like {0, 1, 2, 3, 4, 5}?

As before, the new mesh is supposed to be completely flat with normals at right angles to the line of sight. I'd expect to see a solid white square against a black background; however, what I actually see is four columns, slightly off the vertical, of little white lines which are longest at the bottom of the columns and degenerate to dots at the top. If I change the mesh size from 15x15 to 5x5, I see a small white triangle. If I change it to 7x7, 13x13, or 19x19 I see nothing; 9x9 gives two columns of lines, 11x11 gives three, and 17x17 gives five. What is happening here?

On a different note, if I change the driver to EDT_OPENGL, everything always comes up white regardless of what SColor c is set to.
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Post by Acki »

geoff wrote:I found out that setting up the indices (buf -> Indices) caused some things to be displayed. However, I don't understand why it should be necessary to create a list which merely contains the nonnegative integers in sequence; why do you need an array like {0, 1, 2, 3, 4, 5}?
then you realy should learn the basics of 3d arts !!! :lol:
look here, this might help you understanding this: http://irrlicht.sourceforge.net/phpBB2/ ... 367#171367
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Oh yes, indices are mandatory. Sorry that I skipped this in the first place, but I really thought that you saw something unlit instead of just nothing :?
However, your indices are quite wrong, that's why you get the wrong results. You need to triangulate your plane, because SMeshBuffer requires triangles. Also note that you need to use clockwise orientation of the faces. So the following quad results in indices 0,1,2,1,3,2

Code: Select all

0---1
|  / |
| /  |
2---3
And now indices also help to avoid using too many vertex duplicates, because in this example 1 and 2 are part of two faces, but need to be defined in the vertex array only once.
geoff
Posts: 9
Joined: Sat Oct 25, 2008 7:10 pm

Post by geoff »

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?
Post Reply