Multiple textures to different parts of a model: how?

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.
Devil Master
Posts: 81
Joined: Wed Apr 23, 2008 8:47 pm

Multiple textures to different parts of a model: how?

Post by Devil Master »

I thought I had a fair experience about the basics of Irrlicht, and in fact, I have already created several complex virtual environments with it, but all of them have always had one texture per model. Now I've tried to to place an animated .md3 model, made out of different sub-objects, into a scene, and I couldn't texture it the way I wanted.

This is a Duke Nukem model from an old version of the High Resolution Pack of Duke Nukem 3D. If I open it with Deep Exploration, I can see that it is made out three sub-objects:
- exp_duke (the Duke model proper)
- exp_jetpack (the jetpack on his back)
- exp_rpg (the rocket launcher in his hands)
So far, I did this:

Code: Select all

 
 // Models
 mesh[0] = smgr->getMesh("media/1405_duke.md3"); // the Duke model, made out of the three sub-objects
 
 // Textures
 duketexture = driver->getTexture("media/1405_duke.png"); // the texture for Duke's body
 rpgtexture = driver->getTexture("media/0023_rpg.png"); // the texture for the RPG
 
 // The objects are created, textured and placed
 node[0] = smgr->addAnimatedMeshSceneNode( mesh[0] ); dlPositionObject(0,0,0,0);
 node[0]->setMaterialTexture( 0, duketexture);
 
 node[0]->setMaterialFlag(EMF_LIGHTING, true);
 node[0]->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);
 node[0]->setMaterialFlag(video::EMF_GOURAUD_SHADING, true);
This results in the same texture being applied to all three sub-objects (the jetpack and RPG are covered with a jumble of Duke's face, hair and clothes). I want to apply it on Duke's body only, and apply the appropriate textures on the other sub-objects, but I don't have a clue about how to do that. Would anyone tell me how to do that?
Last edited by Devil Master on Thu Mar 26, 2015 11:59 pm, edited 1 time in total.
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: Multiple textures to different parts of a model: how?

Post by hendu »

Set it on the meshbuffer, not on the node.
Devil Master
Posts: 81
Joined: Wed Apr 23, 2008 8:47 pm

Re: Multiple textures to different parts of a model: how?

Post by Devil Master »

I'm not sure I understand.

I have googled "meshbuffer" to attempt to understand what you mean, and I found this page, the examples in which use an object of a type called IMeshBuffer*. Is that what you mean? So far I declared an array of those and followed that example to do what I think is how you get a meshbuffer:

Code: Select all

mesh_buffer[0]=mesh[0]->getMeshBuffer(0);
If I understand it correctly, a mesh is made out of several meshbuffers (which I previously called "sub-objects") and what I did here was to get the meshbuffer number 0 of mesh[0] and place it into mesh_buffer[0] (an element of an array of IMeshBuffer* type objects I have declared). Have I gotten it right so far?

If I have: now that I have a meshbuffer, what do I do with it? You told me "set it (the material) on the meshbuffer", so I had assumed I had to do this:

Code: Select all

mesh_buffer[0]->setMaterial(duketexture);
but there is no setMaterial for IMeshBuffer* type objects, only getMaterial. So what do I do?
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: Multiple textures to different parts of a model: how?

Post by hendu »

Yeah, you got it. Each meshbuffer has its own material with its own textures, when you set a texture on a node it loops over and sets it to every meshbuffer.

IMeshbuffer->getMaterial().setTexture(0, duketexture)
Devil Master
Posts: 81
Joined: Wed Apr 23, 2008 8:47 pm

Re: Multiple textures to different parts of a model: how?

Post by Devil Master »

So I added this line after the loading of the textures:

Code: Select all

mesh_buffer[0]->getMaterial().setTexture(0, duketexture);
The program compiled with 0 errors and 0 warnings, but when I launched it, it crashed! The console says that the process returned -1073741819 (0xC0000005).
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: Multiple textures to different parts of a model: how?

Post by hendu »

Some pointer was NULL. Ask your debugger.
Devil Master
Posts: 81
Joined: Wed Apr 23, 2008 8:47 pm

Re: Multiple textures to different parts of a model: how?

Post by Devil Master »

Indeed. At the moment, mesh_buffer[0] remains at zero even after assigning it to mesh[0]->getMeshBuffer(0);
But why?
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Multiple textures to different parts of a model: how?

Post by CuteAlien »

As hendu said - you have to use a debugger to investigate such problems. Learn to use it - it's an essential skill in programming which you will need pretty much every day. If you use an IDE like VisualStudio or Code::Blocks (which I assume) they have very comfortable debuggers. When you compile in debug you should on the crash already see in which line it crashes and the values of all variables at that moment.
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
Devil Master
Posts: 81
Joined: Wed Apr 23, 2008 8:47 pm

Re: Multiple textures to different parts of a model: how?

Post by Devil Master »

CuteAlien wrote:When you compile in debug you should on the crash already see in which line it crashes and the values of all variables at that moment.
I'm using Code::Blocks, and all the debugger is telling me is information I already know and which I cannot use to solve the problem. It's telling me that:
1) the program receives a SIGSEGV signal
2) when it does, it's executing the function CreateWorld().
It's not telling me anything more than that.

And here's the function CreateWorld().

Code: Select all

void CreateWorld(void)
{
 unsigned int i;
 
 camera = smgr->addCameraSceneNodeFPS(0,100.0f,0.05f,-1,keyMap,4,false,0.f);
 camera->setFarValue(1000);
 camera->setNearValue(0.01);
 mainlight=smgr->addLightSceneNode(0, core::vector3df(0,100,-100), video::SColorf(1.0f,1.0f,1.0f,1.0f), 500.0f);
 MakeSkybox();
 
 //Meshes
 mesh[0] = smgr->getMesh("media/1405_duke.md3");
 mesh_buffer[0]=mesh[0]->getMeshBuffer(0);
 
 // Textures
 duketexture = driver->getTexture("media/1405_duke.png");
 rpgtexture = driver->getTexture("media/0023_rpg.png");
 
 // this is the line that makes it CRASH!
 mesh_buffer[0]->getMaterial().setTexture(0, duketexture);
 
 // The objects are created, textured and placed
 node[0] = smgr->addAnimatedMeshSceneNode( mesh[0] );
 
 node[0]->setMaterialFlag(EMF_LIGHTING, true);
 node[0]->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);
 node[0]->setMaterialFlag(video::EMF_GOURAUD_SHADING, true);
}
If I remove the line that makes it crash, it doesn't crash anymore, and, as expected, all I get is a completely untextured Duke model.
If, instead, I modify that line like this...

Code: Select all

if (!mesh_buffer[0])
 return;
else
 mesh_buffer[0]->getMaterial().setTexture(0, duketexture);
 
it doesn't crash anymore, and, as expected, I get an empty scene, because the function returns before placing the model.

So I know it crashes because mesh_buffer[0] remains at zero even after the execution of the getMeshBuffer function, contrary to expectations, and this is already more than what the debugger is telling me.
I even thought that maybe I'm not supposed to use 0 as a parameter for getMeshBuffer, so I changed this line

Code: Select all

mesh_buffer[0]=mesh[0]->getMeshBuffer(0);
into this:

Code: Select all

 for (i=0; i<65535 && !mesh_buffer[0]; i++)
  mesh_buffer[0]=mesh[0]->getMeshBuffer(i);
 if (!mesh_buffer[0])
  return;
and it still returns before placing the model, because no matter what parameter I use for getMeshBuffer, the value of mesh_buffer[0] always remains zero.

At this point, there are 3 possible explanations:
1) there's something wrong with the model (unlikely, because it's textured correctly in Duke Nukem 3D and it can be textured in Deep Exploration, although its animations cannot be exported for a limitation of the program)
2) there's something wrong with the implementation of getMeshBuffer (unlikely, because someone would have probably found out before me, although it could be... possible?)
3) there's something wrong with one of the assumptions I followed while writing the functions (but what? I have no idea).
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Multiple textures to different parts of a model: how?

Post by CuteAlien »

You should post the whole code - makes it easier to discuss it. My guess would be that your declarations of mesh and mesh_buffer are wrong as doing something like mesh[0]= or mesh_buffer[0] = looks a little bit suspicions (only works if you have allocated arrays for those.
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
Devil Master
Posts: 81
Joined: Wed Apr 23, 2008 8:47 pm

Re: Multiple textures to different parts of a model: how?

Post by Devil Master »

CuteAlien wrote:doing something like mesh[0]= or mesh_buffer[0] = looks a little bit suspicions (only works if you have allocated arrays for those.
Yes, they are elements of arrays. Global arrays, to be exact. I do it like this because it makes it easier to repeat the same operation for more objects of the same type.

Anyway, here's the full code.

main.cpp

Code: Select all

#include <cstdio>
#include <ctime>
#include <cstdlib>
 
#include "pch.h"
#include "Device.h"
 
using namespace irr;
 
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
 
using namespace irrklang;
 
SKeyMap keyMap[4];
 
ISoundEngine* engine = createIrrKlangDevice();
 
 
IVideoDriver* driver = device->getVideoDriver();
 
IGUIEnvironment* guienv = device->getGUIEnvironment();
 
IGUISkin* skin = guienv->getSkin();
 
IAnimatedMesh* mesh[65536];
IAnimatedMeshSceneNode* node[65536];
IMeshBuffer* mesh_buffer[3];
 
ICameraSceneNode* camera;
 
scene::ISceneNode* SkyBox;
scene::ISceneNode* mainlight;
ITexture* sky;
ITexture* skytop;
ITexture* skybottom;
ITexture* skyleft;
ITexture* skyright;
ITexture* skyfront;
ITexture* skyback;
 
ITexture* duketexture;
ITexture* rpgtexture;
 
void CreateKeymap(void)
{
 keyMap[0].Action = EKA_MOVE_FORWARD;
 keyMap[0].KeyCode = KEY_KEY_W;
 
 keyMap[1].Action = EKA_MOVE_BACKWARD;
 keyMap[1].KeyCode = KEY_KEY_S;
 
 keyMap[2].Action = EKA_STRAFE_LEFT;
 keyMap[2].KeyCode = KEY_KEY_A;
 
 keyMap[3].Action = EKA_STRAFE_RIGHT;
 keyMap[3].KeyCode = KEY_KEY_D;
}
 
void MakeSkybox(void)
{
 skytop=driver->getTexture("media/skyboxes/up.png");
 skybottom=driver->getTexture("media/skyboxes/dn.png");
 skyleft=driver->getTexture("media/skyboxes/rt.png");
 skyright=driver->getTexture("media/skyboxes/lf.png");
 skyfront=driver->getTexture("media/skyboxes/bk.png");
 skyback=driver->getTexture("media/skyboxes/ft.png");
 SkyBox=smgr->addSkyBoxSceneNode(skytop,skybottom,skyleft,skyright,skyfront,skyback);
}
 
 
void CreateWorld(void)
{
 unsigned int i;
 
 camera = smgr->addCameraSceneNodeFPS(0,100.0f,0.05f,-1,keyMap,4,false,0.f);
 camera->setFarValue(1000);
 camera->setNearValue(0.01);
 mainlight=smgr->addLightSceneNode(0, core::vector3df(0,100,-100), video::SColorf(1.0f,1.0f,1.0f,1.0f), 500.0f);
 MakeSkybox();
 
 //Meshes
 mesh[0] = smgr->getMesh("media/1405_duke.md3");
 
 for (i=0; i<65535 && !mesh_buffer[0]; i++)
  mesh_buffer[0]=mesh[0]->getMeshBuffer(i);
 
 // Textures
 duketexture = driver->getTexture("media/1405_duke.png");
 rpgtexture = driver->getTexture("media/0023_rpg.png");
 
 mesh_buffer[0]->getMaterial().setTexture(0, duketexture);
 
 // The objects are created, textured and placed
 node[0] = smgr->addAnimatedMeshSceneNode( mesh[0] );
 
 node[0]->setMaterialFlag(EMF_LIGHTING, true);
 node[0]->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);
 node[0]->setMaterialFlag(video::EMF_GOURAUD_SHADING, true);
}
 
 
int main()
{
 device->getCursorControl()->setVisible(false);
 
 if (!device)
  return 1;
 
 CreateKeymap();
 CreateWorld();
 
 while(device->run())
 {
  driver->beginScene(true, true, SColor(255,100,101,140));
 
  smgr->drawAll();
  guienv->drawAll();
 
  driver->endScene();
 }
 device->drop(); // drops Irrlicht device
 engine->drop(); // drops Irrklang engine
 return 0;
}
pch.h

Code: Select all

#ifndef PCH_H_INCLUDED
#define PCH_H_INCLUDED
#define WINDOWS
 
// Put here all rarely changing header files for compilation speedup
 
 #include <irrlicht/irrlicht.h> // This is more platform-independent than just irrlicht.h
#include <irrKlang.h>
#include <cmath> // newschool header, instead of antique <math.h>
 
#endif // PCH_H_INCLUDED
Device.h

Code: Select all

#ifndef DEVICE_H
#define DEVICE_H
 
using namespace irr;
 
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
 
int oldkey,currentkey;
 
unsigned char buf[11];
IrrlichtDevice *nulldev = createDevice(video::EDT_NULL);
io::IFileSystem* FileSystem = nulldev->getFileSystem();
io::IReadFile* file = FileSystem->createAndOpenFile("config.cfg"); // if a file called config.cfg is NOT present in the same directory as the executable, the program will crash
size_t result = file->read(buf,sizeof buf);
 
unsigned char renderer=buf[0];
unsigned int resx=(buf[1]<<8)+buf[2];
unsigned int resy=(buf[3]<<8)+buf[4];
unsigned char bpp=buf[5];
unsigned char screenmode=buf[6];
unsigned char stencil=buf[7];
unsigned char vsync=buf[8];
 
// the structure of this class comes from Irrlicht Example 04.Movement
class MyEventReceiver : public IEventReceiver
{
 public:
 virtual bool OnEvent(const SEvent& event)
 {
  if (event.EventType==irr::EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown
      && event.KeyInput.Key!=KEY_KEY_W
      && event.KeyInput.Key!=KEY_KEY_S
      && event.KeyInput.Key!=KEY_KEY_A
      && event.KeyInput.Key!=KEY_KEY_D)
  {
   currentkey=event.KeyInput.Key;
   return true;
  }
  else if (event.EventType==irr::EET_MOUSE_INPUT_EVENT && event.MouseInput.Event==EMIE_LMOUSE_PRESSED_DOWN)
  {
   currentkey=KEY_LBUTTON;
   return true;
  }
  else if (event.EventType==irr::EET_MOUSE_INPUT_EVENT && event.MouseInput.Event==EMIE_RMOUSE_PRESSED_DOWN)
  {
   currentkey=KEY_RBUTTON;
   return true;
  }
  else
   currentkey=0;
 
  return false;
 }
};
MyEventReceiver receiver;
 
IrrlichtDevice *device = createDevice(irr::video::E_DRIVER_TYPE(renderer),dimension2d<u32>(resx,resy),bpp,screenmode,stencil,vsync,&receiver);
ISceneManager* smgr = device->getSceneManager();
 
#endif
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: Multiple textures to different parts of a model: how?

Post by Seven »

mesh[0] = smgr->getMesh("media/1405_duke.md3");
for (i=0; i<65535 && !mesh_buffer[0]; i++)
mesh_buffer[0]=mesh[0]->getMeshBuffer(i);


round 0 mesh_buffer[0]=mesh[0]->getMeshBuffer(0);
round 1 mesh_buffer[0]=mesh[0]->getMeshBuffer(1);
round 2 mesh_buffer[0]=mesh[0]->getMeshBuffer(2);
round 3 mesh_buffer[0]=mesh[0]->getMeshBuffer(3);
round 4 mesh_buffer[0]=mesh[0]->getMeshBuffer(4);
round 5 mesh_buffer[0]=mesh[0]->getMeshBuffer(5);
round 6 mesh_buffer[0]=mesh[0]->getMeshBuffer(6);
round 7 mesh_buffer[0]=mesh[0]->getMeshBuffer(7);
round 8 mesh_buffer[0]=mesh[0]->getMeshBuffer(8);
.........
round 65532 mesh_buffer[0]=mesh[0]->getMeshBuffer(65532);
round 65533 mesh_buffer[0]=mesh[0]->getMeshBuffer(65533);
round 65534 mesh_buffer[0]=mesh[0]->getMeshBuffer(65534);

why is mesh_buffer[0] always used?
what is mesh[0]->getMeshBuffer[65534] value? probably NULL?
at the end, mesh_buffer[0] always = mesh[0]->getMeshBuffer[65534] which is probably NULL

so you try to limit the loop

65535 && !mesh_buffer[0]
so now it only looks like you are looping 65535 times, but really you are only looping once because mesh_buffer[0] should get a value on the first pass.

seems like you would want something like....

mesh[0] = smgr->getMesh("media/1405_duke.md3");
mesh_buffer[0]=mesh[0]->getMeshBuffer(0);
Devil Master
Posts: 81
Joined: Wed Apr 23, 2008 8:47 pm

Re: Multiple textures to different parts of a model: how?

Post by Devil Master »

why is mesh_buffer[0] always used?
Because I'm not sure about which mesh buffer actually contains valid data, and I want the value to be stored explicitly into mesh_buffer[0], not in another element of the array. Let me direct your attention to the condition:

Code: Select all

for (i=0; i<65535 && !mesh_buffer[0]; i++)
So, for the loop to keep iterating, both of the following conditions must be true:
- i must be less than 65535
and
- mesh_buffer[0] must be zero.

As soon as mesh_buffer[0] receives any non-zero value, the program is supposed to stop looping and continue to the instructions that follow.
However, what actually happens, is that the loop stops iterating when i reaches 65535. Which means it never read anything but zero.

As a test, I modified the condition this way (less elegant, but more explicit):

Code: Select all

 for (i=0; i<65535; i++)
 {
  mesh_buffer[0]=mesh[0]->getMeshBuffer(i);
  if (mesh_buffer[0])
   break;
 }
And the same thing happens. The program never breaks from the loop, which instead stops when i reaches 65535.
mongoose7
Posts: 1227
Joined: Wed Apr 06, 2011 12:13 pm

Re: Multiple textures to different parts of a model: how?

Post by mongoose7 »

From IAnimatedMesh.h:

Code: Select all

        //! Returns the type of the animated mesh.
        /** In most cases it is not neccessary to use this method.
        This is useful for making a safe downcast. For example,
        if getMeshType() returns EAMT_MD2 it's safe to cast the
        IAnimatedMesh to IAnimatedMeshMD2.
        \returns Type of the mesh. */
        virtual E_ANIMATED_MESH_TYPE getMeshType() const
        {
            return EAMT_UNKNOWN;
        }
My reading of this is, you cannot obtain the mesh buffers from IAnimatedMesh*. Moreover, looking at the loader, the mesh does not use standard mesh buffers. You may have to look into using the correct underlying types.
Foaly
Posts: 142
Joined: Tue Apr 15, 2014 8:45 am
Location: Germany

Re: Multiple textures to different parts of a model: how?

Post by Foaly »

Yeah, I think mongoose7 is right.
But you can set the materials in the SceneNode:

Code: Select all

 
node[0]->setReadOnlyMaterials(false);
node[0]->getMaterial(0)->setTexture(0, duketexture);
node[0]->getMaterial(1)-> ...
 
I hope that works.
Post Reply