How to use a custom mesh loader?

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.
Post Reply
irr::Dave
Posts: 8
Joined: Wed Jul 16, 2014 4:17 pm

How to use a custom mesh loader?

Post by irr::Dave »

Hello everyone,
a few days ago I started programming with the Irrlicht Engine. I did some tutorials and read on the forums and in the API...

What I want to do is a terrain with loading a heightmap-file and texturing it later.
Since I cannot ensure map-sizes of (2^n+1) I couldn't use the ITerrainSceneNode.
With the addTerrainMesh()-function of the SceneManager I got a working result and was able to display the terrain.
Unfortunately with much less FPS than I had in other examples. I think its because addTerrainMesh() is
using IImage instead of ITexture, which is not loaded into hardware-buffers or sth the like(?).

Therefore I programmed a small class implementing the IMeshLoader interface to become my own heightmap-loader in the future,
using a custom file format. To test this I'm currently not reading something from the file,
its just creating the hardcoded mesh from Tutorial 3 inside the createMesh() function.

Code: Select all

#define NUM_VERTICES 4
#define VERTICES_PER_POLY 3
class MyMeshLoader : public irr::scene::IMeshLoader {
private:
    irr::scene::SAnimatedMesh animatedMesh;
    irr::scene::SMesh mesh;
    irr::scene::SMeshBuffer meshBuffer;
    irr::video::SMaterial material;
    irr::core::aabbox3d<irr::f32> box;
    irr::video::S3DVertex *vertices;
    irr::u16 *indices;
public:
    MyMeshLoader() {
        std::cout << "MyMeshLoader() constructor call\n";
    }
    
    virtual ~MyMeshLoader() {
        std::cout << "MyMeshLoader() destructor call\n";
        if (vertices) free(vertices);
        if (indices) free(indices);
    }
 
    virtual irr::scene::IAnimatedMesh *createMesh(irr::io::IReadFile *file) {
        std::cout << "MyMeshLoader.createMesh() called\n";
        bool failed = false;
        vertices = (irr::video::S3DVertex *)malloc(NUM_VERTICES * sizeof(irr::video::S3DVertex));
        if (!vertices) failed = true;
        indices = (irr::u16 *)malloc(NUM_VERTICES * VERTICES_PER_POLY * sizeof(irr::u16));
        if (!indices) failed = true;
        if (failed) {
            std::cout << "Failed to malloc resources for the mesh\n";
            if (vertices) free(vertices);
            if (indices) free(indices);
        }
        irr::u16 indicesInit[] = { 0,2,3, 2,1,3, 1,0,3, 2,0,1 };
        int i = 0;
        for (i = 0; i < NUM_VERTICES * VERTICES_PER_POLY; ++i) {
            indices[i] = indicesInit[i];
        }
        vertices[0] = irr::video::S3DVertex(0,0,10, 1,1,0, irr::video::SColor(255,0,255,255), 0, 1);
        vertices[1] = irr::video::S3DVertex(10,0,-10, 1,0,0, irr::video::SColor(255,255,0,255), 1, 1);
        vertices[2] = irr::video::S3DVertex(0,20,0, 0,1,1, irr::video::SColor(255,255,255,0), 1, 0);
        vertices[3] = irr::video::S3DVertex(-10,0,-10, 0,0,1, irr::video::SColor(255,0,255,0), 0, 0);
        material.Lighting = false;
        meshBuffer.Material = material;
        box.reset(vertices[0].Pos);
        for (i = 1; i < NUM_VERTICES; ++i) {
            box.addInternalPoint(vertices[i].Pos);
        }
        meshBuffer.setBoundingBox(box);
        meshBuffer.append(&vertices[0], NUM_VERTICES, &indices[0], NUM_VERTICES * VERTICES_PER_POLY);
        mesh.setBoundingBox(box);
        mesh.addMeshBuffer(&meshBuffer);
        animatedMesh.setBoundingBox(box);
        animatedMesh.addMesh(&mesh);
        std::cout << "MyMeshLoader.createMesh() finished\n";
        return &animatedMesh;
    }
 
    virtual bool isALoadableFileExtension(irr::io::path const &filename) const {
        irr::core::string<irr::c8> extension = filename.subString(filename.findLast('.'), filename.size(), true);
        std::cout << "File extension is: " << extension.c_str() << '\n';
        return extension == ".sta";
    }
};
Here I pass MyMeshLoader to the SceneManager and "load" a mesh (empty dummy-file).
With this mesh I then create the SceneNode.
--> Is it correct to call meshLoader->drop() after passing him to the SceneManager?

Code: Select all

MyMeshLoader *meshLoader = new MyMeshLoader();
    smgr->addExternalMeshLoader(meshLoader);
    meshLoader->drop();
    irr::scene::IAnimatedMesh *animatedMesh = smgr->getMesh("../terrain/mymesh.sta");
    if (!animatedMesh) {
        std::cout << "Could not load 'mymesh.sta'\n";
        return 1;
    }
    irr::scene::IAnimatedMeshSceneNode *node = smgr->addAnimatedMeshSceneNode(animatedMesh, NULL, -1,
        irr::core::vector3df(0,0,0), irr::core::vector3df(0,0,0), irr::core::vector3df(1.0f,1.0f,1.0f));
The program runs as expected until I quit:

Image

pops up, which is not very helpful :?
The following is the console output from running the programm once (including program closure, at this moment the above error appears)

Image

So I'm not sure why this happens, probably something failes during the cleanup.

A hint into the right direction would be nice. :wink:
Is it the initialization of my mesh in the meshloader which is insufficient?
Do I have to pass specific other information along with my meshloader to the SceneManager?


Best regards and thanks in advance
mongoose7
Posts: 1227
Joined: Wed Apr 06, 2011 12:13 pm

Re: How to use a custom mesh loader?

Post by mongoose7 »

Probably a memory problem - double free or accessing memory that has been freed. Use the debugger. Also, if you use Visual Studio, there is an automatic runtime heap check when compiled for debugging. On Linux you can use Valgrind.
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: How to use a custom mesh loader?

Post by hendu »

The terrain mesh may be slower, because the terrain node does dynamic lod, while the mesh creates a static buffer. The heightmap is not stored anywhere, so the texture/image difference does not matter, it's only used at load time to set the vertices.
irr::Dave
Posts: 8
Joined: Wed Jul 16, 2014 4:17 pm

Re: How to use a custom mesh loader?

Post by irr::Dave »

Hi again,

Debugging says there was an access violation reading some memory location and points
to the 'delete this;' line:

Code: Select all

bool drop() const {
    // someone is doing bad reference counting.
    _IRR_DEBUG_BREAK_IF(ReferenceCounter <= 0)
 
    --ReferenceCounter;
    if (!ReferenceCounter)
    {
        delete this;  // access violation reading blah
        return true;
    }
 
    return false;
}
Probably the following happens:
1. I create my hardcoded 'animatedMesh' inside MyMeshLoader.createMesh() and store the data in private class-variables of MyMeshLoader.
2. Then a pointer to that variable is passed to the SceneManager when I call smgr->getMesh("blah.sta"). (Which calls MyMeshLoader.createMesh())
3. 'meshLoader' (instance of MyMeshLoader) is deleted, including the 'animatedMesh' variable.
4. When the SceneManager deletes the AnimatedMeshSceneNode he probably tries to delete the 'animatedMesh' too, which is not existent anymore
--> access violation reading blah

I will allocate my mesh-variables on the heap so that only pointers will be deleted together with my class-instance, then the SceneManager still points
to valid memory.
We'll see if that fixes it...

Thanks for your help :)
irr::Dave
Posts: 8
Joined: Wed Jul 16, 2014 4:17 pm

Re: How to use a custom mesh loader?

Post by irr::Dave »

Problem solved :D

I made my class variables pointing to heap memory:

Code: Select all

class MyMeshLoader : public irr::scene::IMeshLoader {
private:
    irr::scene::SAnimatedMesh *animatedMesh;
    irr::scene::SMesh *mesh;
    irr::scene::SMeshBuffer *meshBuffer;
    irr::core::aabbox3d<irr::f32> *box;
    irr::video::S3DVertex *vertices;
    irr::u16 *indices;
public:
    MyMeshLoader() {
        std::cout << "MyMeshLoader() constructor call\n";
    }
    
    virtual ~MyMeshLoader() {
        std::cout << "MyMeshLoader() destructor call\n";
        //if (animatedMesh) delete(animatedMesh);
        //if (mesh) delete(mesh);
        if (meshBuffer) delete(meshBuffer);
        if (box) delete(box);
        if (vertices) free(vertices);
        if (indices) free(indices);
    }
This way it works fine, I noticed that when I turn on deletion of 'animatedMesh' and/or 'mesh' inside my destructor the program crashes as expected
when I close it. Not so when deleting meshBuffer/boundingBox etc.

This leads to an important question:
When I want to create new meshes (either hardcoded or with loading from a custom file), how would you correctly do this?

It looks like I need to take care of vertices/indices/meshbuffers/boundingboxes/(what-else?) myself
while the higher structures (mesh/animatedMesh) are handled by the SceneManager or the Engine in general.
One could of course check the source if a function does copy/deepcopy or just takes the reference but for now
I'm only asking if there is some kind of 'good practice'.

Thanks and Regards,
Dave
mongoose7
Posts: 1227
Joined: Wed Apr 06, 2011 12:13 pm

Re: How to use a custom mesh loader?

Post by mongoose7 »

Sounds weird. Are you sure you are not doing something like, for example, dropping the same object twice? You shouldn't have to micro-manage scene nodes. For example, this is how I do it.

Code: Select all

    mesh = smgr->createSkinnedMesh();
 
    if (!(mbuffer = mesh->addMeshBuffer()))
        return 0;
    // need vertices and faces
    mbuffer->Vertices_Standard.reallocate(12);
    mbuffer->Indices.reallocate(10);
irr::Dave
Posts: 8
Joined: Wed Jul 16, 2014 4:17 pm

Re: How to use a custom mesh loader?

Post by irr::Dave »

Right now I have exactly one meshLoader->drop() call, which is after adding him to the ScenerManager with .addExternalMeshLoader(meshLoader) and
together with the changes mentioned in my previous post it works well. But I actually tried that too, calling drop once, never or only at the end of my program,
not calling it made an assertion fail when closing the program.
So I guess creating an instance of an 'IMeshLoader'-derived class with the new-operator increases ref count by one in the base-constructor or sth similiar,
since I didn't use a 'create...()'-function.
Post Reply