Few questions about terrain.

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Wodzu
Posts: 27
Joined: Wed May 07, 2014 5:41 pm

Few questions about terrain.

Post by Wodzu »

Hi guys,

this is my first post here on forum. :)

Currently I am just playing with tutorials and would like to ask you about few things regarding terrain and engine itself.

1. I have prepared my own terrain. It is a bunch of triangles stored in file. How do I load this terrain? Could you give me some pointers where to start?
2. Is it possible to use ITerrainSceneNode for my terrain and optimize its LOD just like on the tutorial?
3. Can I have more then one ITerrainSceneNode? For exmaple I would like to have a map on which I will have a) flat ground, b) mountain, c) dunes. I would like to put dunes and mountain on the ground.
4. What is the role of Parent in the Scene Node? Is this only for my convenience so I could build myself a hierarchy of nodes? For example mountain and dunes both would have the same parent - flat ground. Does setting a parrent affect somehow the rendering, terrain optimization and so on?

Thank you for your time :)

Regards,

Wodzu
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Few questions about terrain.

Post by CuteAlien »

1. Depends on the format you are using. If it's a custom format then you have to write a custom loader.
2. No - that one is special - it does not handle any triangle-mesh but uses some optimizations.
3. You can have several of those nodes. The trouble is probably how to make the transitions between them.
4. Yes, it's for your convenience. Terrain usually has no parent. It's mainly to make stuff like rotations easier (for example you have items attached to a parent because you want them to rotate together with the parent always).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Wodzu
Posts: 27
Joined: Wed May 07, 2014 5:41 pm

Re: Few questions about terrain.

Post by Wodzu »

CuteAlien wrote:1. Depends on the format you are using. If it's a custom format then you have to write a custom loader.
What simple common format could I use for my case? This is just a mesh, nothing more.
CuteAlien wrote:2. No - that one is special - it does not handle any triangle-mesh but uses some optimizations.
That is a shame:( Does Irrlicht have any other way to help me optimize my terrain mesh?

Regards.
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: Few questions about terrain.

Post by hendu »

Try OBJ for example. There is the heightmapOptimize function which may be useful for your terrain.
Wodzu
Posts: 27
Joined: Wed May 07, 2014 5:41 pm

Re: Few questions about terrain.

Post by Wodzu »

hendu wrote:Try OBJ for example. There is the heightmapOptimize function which may be useful for your terrain.
Is this a correct description for the OBJ file? http://en.wikipedia.org/wiki/Wavefront_.obj_file

Do I need to also calculate normals or will they be calculated automatically by Irrlight? Will be enough to just create a list of vertices and nothing more?
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Few questions about terrain.

Post by CuteAlien »

You need normals only for lighting and you can let Irrlicht calculate them (unless your normals have special needs). I think obj files don't need to have them.

But writing an own loader might be simple as well if you already have your own format. I wonder a little bit how you created your terrain if you already have it in a file as it doesn't sound like you used some tool for it which can export the usual formats?
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Wodzu
Posts: 27
Joined: Wed May 07, 2014 5:41 pm

Re: Few questions about terrain.

Post by Wodzu »

CuteAlien wrote: But writing an own loader might be simple as well if you already have your own format. I wonder a little bit how you created your terrain if you already have it in a file as it doesn't sound like you used some tool for it which can export the usual formats?
Thank you for your interest. Before I will answer your question I just wanted to say that I do not have a lot of experience in 3D. Few years ago I was working a little bit with Direct 3D. A year ago I was doing this terrain stuff with help of pure OpenGL. My application was generating triangles in memory and then directly send them to OpenGL. Terrain was rendered correctly but after spending like 3 days on building my own camera component I've decided that I do not want to do everything from scratch. There is no need to reinvent the wheel, especially when other people (like you :) ) have done it already and done it way better. So, one year passed since last time I was doing anything in OpenGL but now I am determined to do it for real.

I have my own tool which is genereting terrain for me, I have different approach for generating terrain compared to standard technique. I mean, I am not generating terrain based on heightmap such terrain is to random for me. I am carving mountains, lakes, rivers, plains, dunes and so on separately, then I am arranging them on the map and then I hope to glue them togheter. I hope there is a way of making a smooth transition which you've mentioned it earlier. This application is not writtern in C++ so I thought that it will be simpler to generate file rather then port it to different language, just for now. In the future there will be no need to generate the file, I will create mesh in the memory and send it directly to Irrlicht (I am sure there is a method of doing this).

So now you know all the background. If custom loader is better approach then I am all for it, just tell me where to start:) My current approach is very simple, I just want to display one mesh on the scene. Then I will be bugging you about multiple meshes, transition, optimization, and so on ;)

Thanks for helping me out.
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Few questions about terrain.

Post by CuteAlien »

Basically the job of sceneloader is to create a mesh. I think example 23 SMeshHandling could be a good starting point for creating own meshes. And for examples how to write a meshloader maybe take a look at the *MeshFileLoader.cpp files in Irrlicht/source. Some of those (like CSTLMeshFileLoader) are very short and can give you the basic idea (that one is using an SAnimatedMesh, in your case using an SMesh as target buffer for the mesh is likely sufficient as you won't need animations).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Wodzu
Posts: 27
Joined: Wed May 07, 2014 5:41 pm

Re: Few questions about terrain.

Post by Wodzu »

I am trying to implement my custom loader class by inheriting IMeshLoader. The thing is that IMeshLoader.createMesh() requires to return an IAnimatedMesh. My mesh will not be animated so I do not know if I am doing it right?
Is it ok to write my custom loader inheriting IMeshLoader despite the fact that my mesh will not be animated?
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Few questions about terrain.

Post by CuteAlien »

Ah yeah, strange but doesn't matter too much. Take a look at the implementation of COBJMeshFileLoader::createMesh which has the same problem. What it does (at the end of the function) is to create a SAnimatedMesh and put the static mesh in there.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Wodzu
Posts: 27
Joined: Wed May 07, 2014 5:41 pm

Re: Few questions about terrain.

Post by Wodzu »

Ok,

so I've finally wrote my mesh loader, the problem is that I do not see anything after loading a mesh. I do not know how to properly debug the reason which I am not seeing my test mesh. Could you help me a little?

Here is the createMesh method:

Code: Select all

 
IAnimatedMesh* YWFMeshFileLoader::createMesh(io::IReadFile* file)
{
    u16 vertexCount = 0;
    u16 indexCount = 0;
    long fileSize;
    array<vector3df> vertices;
    array<u16> indices; //Each index points to position in vertices.
    
    vertices.push_back(core::vector3df(-100, 0, 100));
    vertices.push_back(core::vector3df(100, 0, 100));
    vertices.push_back(core::vector3df(100, 0, -100));
 
    indices.push_back(0);
    indices.push_back(1);
    indices.push_back(2);
 
    SMesh* mesh = new SMesh();
    SMeshBuffer* meshBuffer = new SMeshBuffer();
    mesh->addMeshBuffer(meshBuffer);
 
    video::SColor color(255, 255, 0, 0);
    vector3df normal;
    for (u32 i = 0; i != indices.size(); i = i + 3) {
        u16 p1 = indices[i];
        u16 p2 = indices[i + 1];
        u16 p3 = indices[i + 2];
 
        //Perhaps for smooth lighting I should calculate normal based on each neighbouring vertices.
        normal = core::plane3df(vertices[p1], vertices[p2], vertices[p3]).Normal;
 
        meshBuffer->Vertices.push_back(video::S3DVertex(vertices[p1], normal, color, core::vector2df()));
        meshBuffer->Vertices.push_back(video::S3DVertex(vertices[p2], normal, color, core::vector2df()));
        meshBuffer->Vertices.push_back(video::S3DVertex(vertices[p3], normal, color, core::vector2df()));
 
        meshBuffer->Indices.push_back(p1);
        meshBuffer->Indices.push_back(p2);
        meshBuffer->Indices.push_back(p3);
    }
    meshBuffer->drop();
 
    //Wrap mesh in animated mesh.
    SAnimatedMesh* animMesh = 0;
    if (0 != mesh->getMeshBufferCount())
    {
        mesh->recalculateBoundingBox();
        animMesh = new SAnimatedMesh();
        animMesh->Type = EAMT_OBJ;
        animMesh->addMesh(mesh);
        animMesh->recalculateBoundingBox();
    }
 
    mesh->drop();
 
    return animMesh;
}
 
And here is the main loop:

Code: Select all

 
#include "stdafx.h"
#include <irrlicht.h>
#include "YWFMeshLoader.h"
 
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
 
int main()
{
    IrrlichtDevice *device = createDevice(video::EDT_SOFTWARE, dimension2d<u32>(640, 480), 16, false, false, false, 0);
 
    if (!device)
        return 1;
    YWFMeshFileLoader *loader = new YWFMeshFileLoader();
    IVideoDriver* driver = device->getVideoDriver();
    ISceneManager* smgr = device->getSceneManager();
    smgr->addExternalMeshLoader(loader);
    loader->drop();
    IGUIEnvironment* guienv = device->getGUIEnvironment();
 
    
//  IAnimatedMesh* mesh = smgr->getMesh("E:\\CODING\\Developement\\C++\\Libs\\irrlicht-1.8.1\\media\\sydney.md2"); //this loads mesh properly.
    IAnimatedMesh* mesh = smgr->getMesh(device->getFileSystem()->getWorkingDirectory() + "//Debug//test.ywf"); //File path does not matter in this example. Content is emulated inside YWFMeshFileLoader::createMesh()
    if (!mesh)
    {
        device->drop();
        return 1;
    }
    IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode(mesh);
 
    smgr->addCameraSceneNode(0, vector3df(0, 100, 0), vector3df(0, 0, 0));
 
    while (device->run())
    {
        driver->beginScene(true, true, SColor(255, 100, 101, 140));
 
        smgr->drawAll();
        guienv->drawAll();
 
        driver->endScene();
    }
 
    device->drop();
 
    return 0;
}
 
The problem is probably something silly but I am not able to figure it out on myself. I've checked the positionig of camera and it should be ok.

Thanks in advance forany help.
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Few questions about terrain.

Post by CuteAlien »

That part of the code you have posted does work.
I just simplified it a little for a quick test and it does show the triangle:

Code: Select all

 
#include <irrlicht.h>
 
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
 
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif
 
IAnimatedMesh* createMesh(io::IReadFile* file)
{
    array<vector3df> vertices;
    array<u16> indices; //Each index points to position in vertices.
   
    vertices.push_back(core::vector3df(-100, 0, 100));
    vertices.push_back(core::vector3df(100, 0, 100));
    vertices.push_back(core::vector3df(100, 0, -100));
 
    indices.push_back(0);
    indices.push_back(1);
    indices.push_back(2);
 
    SMesh* mesh = new SMesh();
    SMeshBuffer* meshBuffer = new SMeshBuffer();
    mesh->addMeshBuffer(meshBuffer);
 
    video::SColor color(255, 255, 0, 0);
    vector3df normal;
    for (u32 i = 0; i != indices.size(); i = i + 3) {
        u16 p1 = indices[i];
        u16 p2 = indices[i + 1];
        u16 p3 = indices[i + 2];
 
        //Perhaps for smooth lighting I should calculate normal based on each neighbouring vertices.
        normal = core::plane3df(vertices[p1], vertices[p2], vertices[p3]).Normal;
 
        meshBuffer->Vertices.push_back(video::S3DVertex(vertices[p1], normal, color, core::vector2df()));
        meshBuffer->Vertices.push_back(video::S3DVertex(vertices[p2], normal, color, core::vector2df()));
        meshBuffer->Vertices.push_back(video::S3DVertex(vertices[p3], normal, color, core::vector2df()));
 
        meshBuffer->Indices.push_back(p1);
        meshBuffer->Indices.push_back(p2);
        meshBuffer->Indices.push_back(p3);
    }
    meshBuffer->drop();
 
    //Wrap mesh in animated mesh.
    SAnimatedMesh* animMesh = 0;
    if (0 != mesh->getMeshBufferCount())
    {
        mesh->recalculateBoundingBox();
        animMesh = new SAnimatedMesh();
        animMesh->Type = EAMT_OBJ;
        animMesh->addMesh(mesh);
        animMesh->recalculateBoundingBox();
    }
 
    mesh->drop();
 
    return animMesh;
}
 
int main()
{
    IrrlichtDevice *device = createDevice(video::EDT_OPENGL, dimension2d<u32>(640, 480), 16, false, false, false, 0);
     if (!device)
        return 1;
    
    IVideoDriver* driver = device->getVideoDriver();
    ISceneManager* smgr = device->getSceneManager();
    IAnimatedMesh* mesh = createMesh(NULL);
    if (!mesh)
    {
        device->drop();
        return 1;
    }
    smgr->addAnimatedMeshSceneNode(mesh);
    smgr->addCameraSceneNode(0, vector3df(0, 200, 0), vector3df(0, 0, 0));
 
    while (device->run())
    {
        driver->beginScene(true, true, SColor(255, 100, 101, 140));
        smgr->drawAll();
        driver->endScene();
    }
 
    device->drop();
    return 0;
}
 
So must be some part which you didn't show which caused the problem. For example your meshloader might not check correctly if it's responsible for ".ywf" files (your overloaded isALoadableFileExtension has to check for that).

Maybe just try loading without a meshloader like I did above first until it works. You only really need meshloaders once your application has to be able to handle different formats.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Wodzu
Posts: 27
Joined: Wed May 07, 2014 5:41 pm

Re: Few questions about terrain.

Post by Wodzu »

Wow, you are fast:)

Thank you for helping me out. I've tried your example and it works, I am not able to figure out the difference between your example and full mesh loader which does not work. I did not posted the full source because I did not want to clutter the forum but if you be so kind, please take a look:

YWFMeshLoader.h

Code: Select all

 
#ifndef __YWF_MESH_FILE_LOADER_H_INCLUDED__
#define __YWF_MESH_FILE_LOADER_H_INCLUDED__
 
#include "IMeshLoader.h"
#include "irrString.h"
#include "vector3d.h"
#include "irrlicht.h"
 
using namespace irr;
using namespace scene;
 
class YWFMeshFileLoader : public IMeshLoader
{
public:
 
    //! returns true if the file maybe is able to be loaded by this class
    //! based on the file extension (i.e. ".ywf")
    virtual bool isALoadableFileExtension(const io::path& filename) const;
 
    //! creates/loads an animated mesh from the file.
    //! \return Pointer to the created mesh. Returns 0 if loading failed.
    //! If you no longer need the mesh, you should call IAnimatedMesh::drop().
    //! See IReferenceCounted::drop() for more information.
    virtual IAnimatedMesh* createMesh(io::IReadFile* file);
 
private:
    //! Check if the file has correct header and size, returns information how many indices and vertices are contained in file.
    bool checkHeader(io::IReadFile* file, long& fileSize, u16& verticesCount, u16& indicesCount) const;
    //! All vertices are loaded at once, returns fallse in case of error.
    bool loadIndices(io::IReadFile* file, u16 count, core::array<u16> &indices) const;
    //! All vertices are loaded at once, returns fallse in case of error.
    bool loadVertices(io::IReadFile* file, u16 count, core::array<core::vector3df> &vertices) const;
};
#endif
 
YWFMeshLoader.cpp

Code: Select all

 
#include "stdafx.h"
#include "YWFMeshLoader.h"
#include "SMesh.h"
#include "SMeshBuffer.h"
#include "SAnimatedMesh.h"
#include "IReadFile.h"
#include "fast_atof.h"
#include "coreutil.h"
#include "os.h"
using namespace irr::core;
 
bool YWFMeshFileLoader::checkHeader(io::IReadFile* file, long& fileSize, u16& vertexCount, u16& indexCount) const
{
    fileSize = file->getSize();
    if (!fileSize)
        return false;
 
    if (file->read(&vertexCount, sizeof(vertexCount)) != sizeof(vertexCount))
        return false;
    if (file->read(&indexCount, sizeof(indexCount)) != sizeof(indexCount))
        return false;
 
    //Check if the file has expected size. fileSize should be unsigned long not long, typecast is safe here to get rid of warning.
    if ((unsigned long)fileSize != vertexCount * 3 * sizeof(f32) + indexCount * sizeof(u16) + sizeof(vertexCount) + sizeof(indexCount))
        return false;
    return true;
}
 
bool YWFMeshFileLoader::isALoadableFileExtension(const io::path& filename) const
{
    return core::hasFileExtension(filename, "ywf");
}
 
bool YWFMeshFileLoader::loadVertices(io::IReadFile* file, u16 count, core::array<core::vector3df> &vertices) const
{
    core::vector3df vector;
    for (size_t i = 1; i <= count; i++)
    {
        if (file->read(&vector.X, sizeof(vector.X)) == 0)
            return false;
        if (file->read(&vector.Y, sizeof(vector.Y)) == 0)
            return false;
        if (file->read(&vector.X, sizeof(vector.Z)) == 0)
            return false;
        vertices.push_back(vector);
    }
    return true;
}
 
bool YWFMeshFileLoader::loadIndices(io::IReadFile* file, u16 count, core::array<u16> &indices) const
{
    u16 index;
    for (size_t i = 1; i <= count; i++)
    {
        if (file->read(&index, sizeof(index)) == 0)
            return false;
        indices.push_back(index);
    }
    return true;
}
 
IAnimatedMesh* YWFMeshFileLoader::createMesh(io::IReadFile* file)
{
    u16 vertexCount = 0;
    u16 indexCount = 0;
    long fileSize;
    array<vector3df> vertices;
    array<u16> indices; //Each index points to position in vertices.
 
    //if (checkHeader(file, fileSize, vertexCount, indexCount) == false)
    //  return 0;
    //if (loadVertices(file, vertexCount, vertices) == false)
    //  return 0;
    //if (loadIndices(file, indexCount, indices) == false)
    //  return 0;
    
    vertices.push_back(core::vector3df(-100, 0, 100));
    vertices.push_back(core::vector3df(100, 0, 100));
    vertices.push_back(core::vector3df(100, 0, -100));
 
    indices.push_back(0);
    indices.push_back(1);
    indices.push_back(2);
 
    SMesh* mesh = new SMesh();
    SMeshBuffer* meshBuffer = new SMeshBuffer();
    mesh->addMeshBuffer(meshBuffer);
 
    video::SColor color(255, 255, 0, 0);
    vector3df normal;
    for (u32 i = 0; i != indices.size(); i = i + 3) {
        u16 p1 = indices[i];
        u16 p2 = indices[i + 1];
        u16 p3 = indices[i + 2];
 
        //Perhaps for smooth lighting I should calculate normal based on each neighbouring vertices.
        normal = core::plane3df(vertices[p1], vertices[p2], vertices[p3]).Normal;
 
        meshBuffer->Vertices.push_back(video::S3DVertex(vertices[p1], normal, color, core::vector2df()));
        meshBuffer->Vertices.push_back(video::S3DVertex(vertices[p2], normal, color, core::vector2df()));
        meshBuffer->Vertices.push_back(video::S3DVertex(vertices[p3], normal, color, core::vector2df()));
 
        meshBuffer->Indices.push_back(p1);
        meshBuffer->Indices.push_back(p2);
        meshBuffer->Indices.push_back(p3);
    }
    meshBuffer->drop();
 
    //Wrap mesh in animated mesh.
    SAnimatedMesh* animMesh = 0;
    if (0 != mesh->getMeshBufferCount())
    {
        mesh->recalculateBoundingBox();
        animMesh = new SAnimatedMesh();
        animMesh->Type = EAMT_OBJ;
        animMesh->addMesh(mesh);
        animMesh->recalculateBoundingBox();
    }
 
    mesh->drop();
 
    return animMesh;
}
 
There is one thing I do not understand (apart from why it does not work). Why the triangle in your example is black, I thought that

Code: Select all

 
video::SColor color(255, 255, 0, 0);
 
will make it red?
Wodzu
Posts: 27
Joined: Wed May 07, 2014 5:41 pm

Re: Few questions about terrain.

Post by Wodzu »

Now I see the difference, it is the problem of camera. In your example it was y =200, in mine y = 100. Now I see the triangle, mesh loader works fine.

Could you tell me one more thing, why the triangle is completly cut off from the viewport when I set up camera y=137 and it is visible at y=138?
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Few questions about terrain.

Post by CuteAlien »

Ah, right - I moved camera back. And the reason it's cut-off in your case is that you use the Software renderer instead of OpenGL or DirectX. That one can do a little bit 3D but is mostly there for a quick dialog before you start the real 3D device. If you really need software rendering use the burnings renderer, that one has clipping. Otherwise I'd recommend using OpenGL.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Post Reply