Page 1 of 1

Abnormals normals

Posted: Mon Mar 09, 2009 11:14 pm
by Oddmonger
Well, hello everyone.

I tried to compute normals on a small object (a pyramid).
The results are strange, when i turn on "normals display" in debug mode.

I got this:
Image



Hmmm strange normals.. i wrote some code to display normals my own way. Here it is:
Image
the small lines attached to the edges of the pyramid are my normals.

Let's face it. I'm dazed and confused (i should have set my color background as deep purple).

Here is the code :

Code: Select all

/* pyramid with computed normals */

#include <irrlicht.h>

irr::IrrlichtDevice *device=0;
irr::video::IVideoDriver *driver=0;
irr::scene::ISceneManager *scenemgr=0;
irr::scene::ICameraSceneNode *cam=0;

irr::scene::SMesh *mesh_pyramid=0;
irr::scene::ISceneNode *mesh_scenenode=0; 


irr::scene::SMesh * create_mesh(float scale_x,float scale_y,float scale_z)
{
  irr::scene::SMesh *mesh; //le mesh
  irr::scene::SMeshBuffer *mesh_buffer; //son buffer

  mesh = new irr::scene::SMesh();
  mesh_buffer = new irr::scene::SMeshBuffer();

  irr::f32 cubeSize = 5.f;
  irr::video::SColor colour(255,255,0,0);

#define NB_INDICES 18
   irr::u16 indices[NB_INDICES] = { 0,1,3, 1,2,3, 3,4,0, 2,4,3, 1,4,2, 0,4,1 };

   mesh_buffer->Indices.set_used(NB_INDICES);
   for (irr::s32 i=0; i<NB_INDICES; ++i)
     mesh_buffer->Indices[i] = indices[i];

   //S3DVertex: pos(x,y,z) normal(x,y,z) color(c) texture_coords(u,v)
   //only vertices position are defined, normals will be computed later...
   mesh_buffer->Vertices.set_used(5);
   mesh_buffer->Vertices[0]  = irr::video::S3DVertex(-.5,0,-.5,  0,0,0, colour, 0, 1);
   mesh_buffer->Vertices[1]  = irr::video::S3DVertex( .5,0,-.5,  0,0,0, colour, 1, 1);
   mesh_buffer->Vertices[2]  = irr::video::S3DVertex( .5,0, .5,  0,0,0, colour, 1, 0);
   mesh_buffer->Vertices[3]  = irr::video::S3DVertex(-.5,0, .5,  0,0,0, colour, 0, 0);
   mesh_buffer->Vertices[4]  = irr::video::S3DVertex(  0,1,  0,  0,0,0, colour, 0, 0); //normal OK

   //now that we've defined vertices, we get their buffer, and recompute their normals.
   // vertice[4] is the top of the pyramid, vertices 1,2,3,4 are the base of the prism.
   irr::video::S3DVertex *vertice = (irr::video::S3DVertex *)mesh_buffer->getVertices();
   irr::core::vector3df normal;
   
   normal =  (vertice[4].Pos - vertice[1].Pos).crossProduct ((vertice[0].Pos-vertice[4].Pos)) ;
   normal.normalize();
   vertice[0].Normal = normal;
   
   normal =  (vertice[4].Pos - vertice[2].Pos).crossProduct ((vertice[1].Pos-vertice[4].Pos)) ;
   normal.normalize();
   vertice[1].Normal = normal;
   
   normal =  (vertice[4].Pos - vertice[3].Pos).crossProduct ((vertice[2].Pos-vertice[4].Pos)) ;
   normal.normalize();
   vertice[2].Normal = normal;

   normal =  (vertice[4].Pos - vertice[0].Pos).crossProduct ((vertice[3].Pos-vertice[4].Pos)) ;
   normal.normalize();
   vertice[3].Normal = normal;
   //normals computing done.
  
   //the mesh buffer is now ready (bounding box is computed just after assignation)
   mesh->addMeshBuffer(mesh_buffer);

   //recalculate bounding box
   for (int j=0; j<mesh->MeshBuffers.size(); ++j) 
      ((irr::scene::SMeshBuffer*)mesh->MeshBuffers[j])->recalculateBoundingBox(); 
   mesh->recalculateBoundingBox();

   return mesh;
}


//the draw of the normals in Irrlicht seems weird to me.
//i want to display normals my own way.
static void draw_my_normals()
{
  irr::scene::SMeshBuffer *mesh_buffer; //pyramid mesh buffer

  //when you do getMeshBuffer, you have to pass the *index* of mesh buffer.
  //usually, it's "0"
  mesh_buffer = (irr::scene::SMeshBuffer *) mesh_pyramid->getMeshBuffer(0);
  irr::video::S3DVertex *vertice = (irr::video::S3DVertex *)mesh_buffer->getVertices();

  for (int i=0; i<5; i++) {
    driver->draw3DLine(vertice[i].Pos, vertice[i].Pos + vertice[i].Normal); 
  }

}


static void start_irr()
{
  device = irr::createDevice(irr::video::EDT_OPENGL, irr::core::dimension2d<irr::s32>(800,600), 32 );
  if (!device) { printf("impossible de créer le device!\n"); exit(0); }
  device->setWindowCaption(L"Pyramid");

  driver = device->getVideoDriver();
  if (!driver) printf("Erreur driver\n");

  scenemgr = device->getSceneManager();

  mesh_pyramid = create_mesh(1.0f,1.0f,2.0f);
  mesh_scenenode = scenemgr->addMeshSceneNode(mesh_pyramid);
  mesh_scenenode->setPosition(irr::core::vector3df(0,0,1) );
  mesh_scenenode->setDebugDataVisible(irr::scene::EDS_FULL);

}

static void stop_irr()
{
  device->drop();
}

static void main_loop()
{
  irr::scene::ILightSceneNode *light=0;

  cam = scenemgr->addCameraSceneNodeFPS (0, 10.0f, .01f, 0, 0, 0, false);
  device->getCursorControl ()-> setVisible (false);

  scenemgr->addLightSceneNode(0,irr::core::vector3df(0,0,-10));

  
  while(device->run() && driver)
  {
    driver->beginScene(true, true, irr::video::SColor(255,0,0,255));
    scenemgr->drawAll();
    draw_my_normals(); //for debug purposes: draw the normals of the pyramid
    driver->endScene();
    device->sleep(20); 
  }
}


int main(void)
{
  start_irr();
  main_loop();
  stop_irr();
  return 0;
}


Thank you, and good night.

a few details about my question.

Posted: Tue Mar 10, 2009 1:45 pm
by Oddmonger
- Hey, it seems nobody replied to me.
- Yeah . Perhaps the question was not so obvious ?
- You're right dear me. Let's sum this up.
- Oh me, you're so smart.


Well, the question was:
is my normal computing done right ?

And, another one:
why the display of the normal with irrlicht's debug mode is not the same that my own normal display ?

For the first question, here is a little drawing for helping the representation of my pyramid.

Image



And here is the part of my code which do the normals computing (taken from the listing above):

mesh_buffer hold the datas of my mesh, and is defined just before. The normals in the mesh buffer are not defined during initialisation (they are at (0,0,0) value).

Code: Select all

irr::video::S3DVertex *vertice = (irr::video::S3DVertex *)mesh_buffer->getVertices();
   irr::core::vector3df normal;
   
   normal =  (vertice[4].Pos - vertice[1].Pos).crossProduct ((vertice[0].Pos-vertice[4].Pos)) ;
   normal.normalize();
   vertice[0].Normal = normal;
   
   normal =  (vertice[4].Pos - vertice[2].Pos).crossProduct ((vertice[1].Pos-vertice[4].Pos)) ;
   normal.normalize();
   vertice[1].Normal = normal;
   
   normal =  (vertice[4].Pos - vertice[3].Pos).crossProduct ((vertice[2].Pos-vertice[4].Pos)) ;
   normal.normalize();
   vertice[2].Normal = normal;

   normal =  (vertice[4].Pos - vertice[0].Pos).crossProduct ((vertice[3].Pos-vertice[4].Pos)) ;
   normal.normalize();
   vertice[3].Normal = normal;
   //normals computing done. 

Please help me.
I'm lost in the C ! :(

Posted: Tue Mar 10, 2009 2:35 pm
by hybrid
Both normal groups are wrong. Moreover, you need separate normals for each face, because you want sharp edges. So it's a total of 12 normals. All of them should be perpendicular to the face they belong to, facing away from the visible side.

Posted: Tue Mar 10, 2009 3:27 pm
by Oddmonger
Hello,

Let's call "Irrlicht's normals representation in debug mode" BYA (i.e. big yellow arrow)

1) Why BYA aren't connected to the vertices of the pyramid ? They wander in the space, they should be connected to the matching vertex (even if the normal's computing is wrong.)

2) What do you call "normal groups" ?

Thank you.

Posted: Tue Mar 10, 2009 9:55 pm
by hybrid
With groups I meant those from Irrlicht and those you have rendered. I don't know why the normals are rendered this way, it could be the rotation code used in the debug render setup.

Posted: Tue Mar 10, 2009 10:42 pm
by Acki
I think your normals are not drawn right because you don't set the tranformation matrix... :lol:

Code: Select all

static void draw_my_normals()
{
  irr::scene::SMeshBuffer *mesh_buffer; //pyramid mesh buffer

  //when you do getMeshBuffer, you have to pass the *index* of mesh buffer.
  //usually, it's "0"
  mesh_buffer = (irr::scene::SMeshBuffer *) mesh_pyramid->getMeshBuffer(0);
  irr::video::S3DVertex *vertice = (irr::video::S3DVertex *)mesh_buffer->getVertices();

// it's important to set the transformation !!!
driver->setTransform(video::ETS_WORLD, core::matrix4());

  for (int i=0; i<5; i++) {
    driver->draw3DLine(vertice[i].Pos, vertice[i].Pos + vertice[i].Normal);
  }

} 

Posted: Wed Mar 11, 2009 12:44 pm
by Oddmonger
Thanks for the answers.

I haven't thought i'd have to manipulate transformation matrix.
That's an interesting discovery :D
Matrix in Irrlicht seem to work the same way as OpenGL do (modelview, projection ..).

Doing the change in my code with adding

Code: Select all

driver->setTransform(video::ETS_WORLD, core::matrix4());
(before drawing my normals) brings some changes, not as expected, but that's another story :?

Now i'm going to try and calculate normals by face (each face will get its own vertices, as Hybrid said).
I still don't understand the Irrlicht's debug output for normals.