Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Your Own Quadrics in OpenGL MFC

5.00/5 (10 votes)
20 Nov 2017CPOL6 min read 17.3K   1.1K  
The alternative Quadrics in OpenGL MFC for your Own design and Universal Polyhedron Procedure.

Image 1

Introduction

The Quadrics procedures (gluSphere, gluCilinder, gluCone, etc.) in OpenGL are very simple and useful instruments. But they have limitations and restrictions for the texture, colour and shape performance due to closed source. And sometimes it is worth having some similar procedures with the codes available.

Background

The semi-Quadrics implementation in OpenGL with open sources has been developed on the base of primitive polygon performance . The demo project GLPlane has been derived from my former ImgTexture project of my former CodeProject article "Texture Mapping in OpenGL using Class CImage" (also derived from the standard MFC Cube Sample project) renamed with the project of my former CodeProject article "MFC Project from Existing Code in One Click".

Before you start building the project provided, it is highly recommended to have a look to the Demo presentation enclosed in order to get an idea of the output expected.

Demo Explanations

The executable GLPlane.exe in the GLPlaneDemo directory has been built with MSVS-2015 pro using the instruments of MSVS-2010. Therefore the GLPlane.exe is valid even for Windows-XP. Just if you want to start in Windows-XP, please do not forget to insert mfc100.dll, mfc100u.dll, msvcp100.dll, msvcr100.dll files into the GLPlaneDemo directory (or into the ..windows\system32 directory).

Some menu and some special Accelerator keys arranged in order to demonstrate the GLPlane project implementation:

Image 2

  • Menu File->Open - selecting the file with the image for texture mapping
  • Menu File->Play - play/stop object rotation
  • Menu View->Default - original Cube Sample performance
  • Menu View->Colour->Select Color - select colour of the object with the standard Colour Dialog
  • Menu View->Colour->Random Color - install random colour for each face
  • Menu View->Texture - current texture performance
  • Menu View->Next Texture - next texture performance (also Right Arrow click)
  • Menu View->Prev Texture - previous texture performance (also Left Arrow click)
  • Menu Object->Default - original ImgTexture performance
  • Menu Object-><Object Name>- change shape of the Quadric to the object selected
  • Menu Object->Next Object - next object performance (also Space click)
  • Menu Object->Prev Object - previous object performance (also Backspace click)
  • Up Arrow - move object far
  • Down Arrow - move object near
  • Mouse Move with the Left Button pressed - rotate object around vertical and horizontal axes

Building Notes

Solution configuration must be installed as Release and the platform to be Win32.

The project provided has been developed with MSVS-2015 pro using the instruments of MSVS-2010. Therefore, the exe files are valid even for Windows-XP. If you do not need to run the application in Windows-XP, just change the instruments to MSVS-2015 .

The default coding property is UNICODE; nevertheless MBCS coding is also available, just change the property.

Even if you are first time working with MSVS, just select menu Debug->Start without debugging and the program GLPlane.exe should start building and working.

Project Source and Data storage

Standard Source codes in GLPlaneproj path have been created with the standard MFC Application Wizard:

  • GLPlane.cpp - defines the standard class behaviors for the application
  • MainFrm.cpp - implementation of the standard CMainFrame class
  • CGLPlaneView.cpp - implementation of the standard CView class; messages handling procedures created by the author using standard MFC Application Wizard procedures
  • GLPlane.rc and resource.h - menu, dialog, accelerator resources created by the author using the standard Resource Wizard procedures

Special source codes in GLPlaneProj\GlobUse path have been developed by the author based on the standard OpenGL and geometry routine procedures:

  • GlobTexture.cpp - to be used in GL Texture procedures (instead of former Glaux.lib library used in original projects)
  • Material.cpp - colour performance of the object
  • Vector_3D.cpp, Plane.cpp, PlaneArea.cpp, PlaneObject.cpp - 3D object handling
  • Vector_2D.cpp, Poligon_2D.cpp - 2D object handling

Codes Explanations

All the properties from the original standard Cube Sample and previous ImgTexture project have been retained.

All the following Menu and Accelerator Commands has been done with standard MFC AppWizard technologies.

1. Init Application

The original Init() procedure with the procedures related remains unchanged from the original standard Cube Sample; in CGLPlaneView::OnCreate procedure in demo purposes, the five lines of the texture implementation from the image files in the Data folder of the root directory located:

C++
 int CGLPlaneView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
 if (CView::OnCreate(lpCreateStruct) == -1)
  return -1;
 Init(); // initialize OpenGL borrowed from Standard Cube Sample
 m_globTexture.Add((WORD)LoadGLPlane(_T("Data/MSUN.jpg"))); //you may insert 
                                                            //here any image file 
 m_globTexture.Add((WORD)LoadGLPlane(_T("Data/famrt.jpg")));//from any pathway valid 
 m_globTexture.Add((WORD)LoadGLPlane(_T("Data/vldv.jpg"))); //and as much as you like 
 m_globTexture.Add((WORD)LoadGLPlane(_T("Data/rsfsr.jpg")));
 m_globTexture.Add((WORD)LoadGLPlane(_T("Data/ussr.jpg")));
 m_pPlaneObj = new CPlaneObject;  //Plane Object Initialization
 return 0;
}

Here, you may insert any image files from any valid and pathway as much as you like.

The only difference from the previous ImgTexture project is the CPlaneObject initialization.

2. Draw GL Scene

OpenGL drawing procedure to be called by virtual procedure OnDraw:

C++
void CGLPlaneView::OnDraw(CDC* /*pDC*/)
{
 DrawScene();
}
void CGLPlaneView::DrawScene(void)
{
 static BOOL     bBusy = FALSE;                       //Draw Scene busy flag
 if(bBusy)                                            //If Drawing going on return
  return;
 bBusy = TRUE;                                        //Draw Scene busy flag on
 glClearDepth(1.0f);                                  //Specifies the clear value 
                                                      //for the depth buffer 
 glClearColor(0.9f, 0.9f, 0.9f, 1.0f);                //Set Background Color
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  //Indicate the buffers to be cleared
 glPushMatrix();                                      //push the current matrix stack
 glTranslatef(0.0f, 0.0f, -m_z);                      //move object far-near
 glRotatef(wAngleX, 1.0f, 0.0f, 0.0f);                //rotate object    
 glRotatef(wAngleY, 0.0f, 1.0f, 0.0f);                //around the axe
 glRotatef(wAngleZ, 0.0f, 0.0f, 1.0f);                //specified

 if(!m_bTexture)                                      //if Texture flag off
  switch (m_idObj)                                    //depends on object style 
  {
  case ID_DEFAULT:DefaultDemoCube();break;            //default Cube Sample performance
  default:
   glEnable(GL_LIGHTING);                             //enable material properties
   m_pPlaneObj->DrawPlaneObject(FALSE);               //Draw Plane object
   glDisable(GL_LIGHTING);                            //disable material properties
   break;
  }//switch (m_idObj)
 else                                                 //if Texture flag On
 {
  glEnable(GL_TEXTURE_2D);                            //install current texture
  glBindTexture(GL_TEXTURE_2D, (GLuint)m_globTexture.GetAt(m_texNum));
  switch (m_idObj)                                   //depends on object style 
  {
  case ID_DEFAULT:
   glDisable(GL_LIGHTING);
   DefaultTextureCube();                    //Texture Cube ImgTexture performance
   break;
  default:m_pPlaneObj->DrawPlaneObject(TRUE); break;//Draw Plane object
  }//switch (m_idObj)
  glDisable(GL_TEXTURE_2D);
 }//if(m_bTexture)
 glPopMatrix();                             //Pop the current matrix stack 
 glFinish();                                //Blocks until all OpenGL execution is complete
 SwapBuffers(wglGetCurrentDC());            //Exchanges the front and back buffers 
 bBusy = FALSE;                             //Draw Scene busy flag off
}

In the DrawScene procedure, the only point of interest is the DrawPlaneObject procedure (all others are the OpenGL routine that may be followed in the projects of my former CodeProject article "50 OpenGL MFC Projects in One"):

C++
void CPlaneObject::DrawPlaneObject(BOOL bTexture )
{
 for(POSITION pos = m_planeList.GetHeadPosition(); pos != NULL;)
 {
      CPlaneArea  * pp = (CPlaneArea  *)m_planeList.GetNext(pos);
   pp->DrawPlaneArea (bTexture);
 }//for(POSITION pos = m_planeList.GetHeadPosition(); pos != NULL;)
}

The idea is that CPlaneObject contains the Object List with the CPlaneArea faces with the DrawPlaneArea procedure for drawing:

C++
void CPlaneArea ::DrawPlaneArea(BOOL bTexture)
{
if(!m_pointList.GetCount())                    //if no points do nothing
  return;
 POSITION psn = NULL;                          //Position in m_texturePointList 
 if (m_texturePointList.GetCount() && bTexture)//if has texture points list and texture flag 
 {
  glEnable(GL_TEXTURE_2D);                     //enable texturing 
  glDisable(GL_LIGHTING);                      //disable material properties
  psn = m_texturePointList.GetHeadPosition();  //Position in m_texturePointList initialize
  if (m_myTextureNum >= 0)                     //If the Face has own texture
   if (m_myTextureNum != m_texNum)             //install Face own texture
    glBindTexture(GL_TEXTURE_2D, (GLuint)m_globTexture.GetAt(m_myTextureNum));
 }
 else                                         //if no texture points in list or no texture flag
 {
  glDisable(GL_TEXTURE_2D);                   //disable texturing 
  glEnable(GL_LIGHTING);                      //enable material properties
  material.SetMaterial(GL_FRONT);             //install Face's own material
 }
 ::glEnable(GL_AUTO_NORMAL);                  //enable normalization
 ::glNormal3f(m_n.x, m_n.y, m_n.z);           //set Face's own normal
 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);   //start Polygon Draw
    glBegin(GL_POLYGON);

    for ( POSITION pos = m_pointList.GetHeadPosition(); pos != NULL;)
 { 
      Vector_3D * pnt = (Vector_3D *)m_pointList.GetNext(pos);//Every point of the Face
   if (m_texturePointList.GetCount()&& bTexture)//if has texture points in list 
                                                //and texture flag
    if(psn != NULL)                             //Position in m_texturePointList valid
   {
    //Every point of the m_texturePointList:
    Vector_2D * ptx = (Vector_2D *)m_texturePointList.GetNext(psn);
      glTexCoord2f((float)ptx->x, (float)ptx->y);//bind point corresponding in texture 
   }
   //Draw current point of the face:
   ::glVertex3f( (GLfloat) pnt->x, (GLfloat) pnt->y, (GLfloat) pnt->z );
  }//for ( POSITION pos = m_pointList.GetHeadPosition(); pos != NULL;)

  glEnd();                                      //complete Polygon Draw

  if (m_texturePointList.GetCount() && bTexture)//if has texture points in list 
                                                //and texture flag
  {
   if (m_myTextureNum >= 0)                     //If the Face has own texture
    if (m_myTextureNum != m_texNum)             //restore Global texture
     glBindTexture(GL_TEXTURE_2D, (GLuint)m_globTexture.GetAt(m_texNum));
  }
}

The class CPlaneArea:public CPlane derived from 3D CPlane class:

C++
  class CPlane : public CObject
{
public:
 CPlane();
 CPlane(Vector_3D v0, Vector_3D v1, Vector_3D v2);//three points determine the Plane
 virtual ~CPlane();
 Vector_3D m_n;     //normal to the Plane
 float m_D;         //distance from axes center point to the Plane 
};

and contains next key features in description:

C++
CPlaneObject * m_pPlaneObject; //Reference to Parent Plane object
CObList m_pointList;           //Object List of 3D points
CObList m_texturePointList;    //Object List of 2D points bound to texture
int m_myTextureNum;            //Planes' own texture
CMaterial material;            //Planes' own material

3. Plane Object Creation

The identificators for Plane Object creation in this project declared in PlaneObject.h are as follows: ID_BOX, ID_SPHERE, ID_CYLINDER, ID_CONE, ID_DISK, ID_QUADPLANE, ID_THORUS, ID_TETRAHEDRON, ID_CUBE, ID_OCTOHEDRON, ID_DODECA, ID_ICOSAHEDRON.

In the case of ID_BOX or ID_QUADPLANE, the initialisation of the Plane Object is quite clear: just assign the points to the face corresponding.

In the case of ID_SPHERE, ID_CYLINDERID_CONEID_DISK or ID_THORUS, the initialisation of the Plane Object arranged by standard quadric equations to each face.

But for Platonic Solid Polyhedra ID_TETRAHEDRON, ID_CUBE, ID_OCTOHEDRON, ID_DODECA, ID_ICOSAHEDRON, I am proud to represent a feather in my bonnet: the Universal Polyhedron Procedure CPlaneObject::CreatePolyHedron:

 

C++
void CPlaneObject::CreatePolyHedron(int nType, double a)
{
 double fi = 0.5 * (1 + sqrt(5.));         //special for ID_DODECA and ID_ICOSAHEDRON
 double ksi = sqrt((5. - sqrt(5.))*0.5);   //special for ID_DODECA 	
  int nn = 3;                              //num of sides of the face
 if (nType == ID_DODECA) nn = 5;
 else if( nType == ID_CUBE) nn = 4;
  Polygon_2D * pPl = new Polygon_2D;
  //Create 2D polygon with equal sides a:
 double m = pPl->CreateEquilateral(nn, a); //m - inradius of equilateral polygon
 //default nType = ID_TETRAHEDRON expected:
 double r = a * 0.5 / sqrt(6.);           //inradius of the  polyhedron
 double tn2 = 1 / sqrt(2.);               //tan of half angle between faces
  switch (nType)
 {
  case ID_CUBE:
  r = a*0.5;
  tn2 = 1;
  break;
 case ID_OCTOHEDRON:
  r = a*0.5*sqrt(2. / 3.);
  tn2 = sqrt(2.);
  break;
 case ID_DODECA:
	 r = a * 0.5 * fi *fi / ksi;
	 tn2 = fi;
	 break; 
 case ID_ICOSAHEDRON:
  r = a*0.5 * fi * fi / sqrt(3.);
  tn2 = fi*fi;
  break;
 }
  double tetta = 2.*atan(tn2);           //angle between faces

//Create First face:
 CPlaneArea * plNew = AppendPlaneArea();//Create 3D polygon from 2D: 
 plNew->CreateFromPolygon_2D(Vector_3D(0, 0, (float)-r), 
        Vector_3D(1, 0, 0), Vector_3D(0, 1, 0), pPl);

 plNew->CreatePolyHedron(pPl, m, r, tetta); //Create next face using 2D polygon 
                                            //and m, r, tetta
 SetClockwise();
 int nCount = 0;

 for (POSITION pos = m_planeList.GetHeadPosition(); pos != NULL; nCount++)
 {
  CPlaneArea * ppl = (CPlaneArea *)m_planeList.GetNext(pos);
  //every face obtains its' own texture from textures' list:
  ppl->m_myTextureNum = nCount % m_globTexture.GetSize();
 }//for (POSITION pos = m_planeList.GetHeadPosition(); pos != NULL;)
}

CPlaneObject::CreatePolyHedron creates the first face and all the others to be done one from another with the CPlaneArea::CreatePolyHedron procedure:

C++
void CPlaneArea::CreatePolyHedron(Polygon_2D * pPl, double m, double r, double tetta)
{
 for (POSITION pos = m_pointList.GetHeadPosition(); pos != NULL;)
 {//for each point on a current face:
  Vector_3D * pV = (Vector_3D *)m_pointList.GetNext(pos);
  if (GetNeigbourOfLineSegment(pV) != NULL) //if the polygon side is already common 
   continue;                                //with another polygon do nothing
  Vector_3D * pVn = GetNextPoint(pV, TRUE);
  Vector_3D rzn = (*pVn - *pV);          //Vector from current point to the nect one
  Vector_3D ex = Normalize(rzn);         //make unit vector
  Vector_3D ny =  m_n^ex;                //x unit vector of new face
  Vector_3D ey = m_n * (float)sin(tetta) + ny *(float) cos(tetta);//y unit vector of new face

  CPlaneArea * plt = m_pPlaneObject->AppendPlaneArea();
  Vector_3D vc((*pV + *pVn)* 0.5f + (float)m * ey);  //center of the new face

  if (fabs(!vc - r) < GeomTreshold)
    plt->CreateFromPolygon_2D(vc, ex, ey, pPl);
  else
  {
   ey = m_n * (float)sin(tetta) - ny * (float)cos(tetta);
   vc = Vector_3D((*pV + *pVn)* 0.5f - (float)m * ey);
   plt->CreateFromPolygon_2D(vc, Vector_3D(0) - ex, Vector_3D(0) - ey, pPl);
  }

  plt->CreatePolyHedron(pPl, m, r, tetta);//Create next face using 2D polygon 
                                          //pPl and m, r, tetta
 }
 //Create texture points for the current face :
 for (POSITION pos = pPl->m_PointList.GetHeadPosition(); pos != NULL;)
 {
  Vector_2D * pV = (Vector_2D *)pPl->m_PointList.GetNext(pos);
  Vector_2D vv((*pV)*0.5 / m);
  Vector_2D * pt = new Vector_2D(vv + Vector_2D(0.5));
  m_texturePointList.AddTail(pt);
 }
 }

The construction principle of the Platonic Solid is shown in the figure below:

Image 3

  • The procedure CPlaneObject::CreatePolyHedron creates the first equilateral polygon (blue) and calculates the inradius of the polygon and the angle between the faces.
  • The procedure CPlaneArea::CreatePolyHedron with the blue polygon creates the next equilateral polygon (red) using the inradius of the polygon and the angle between the faces.
  • The procedure CPlaneArea::CreatePolyHedron with the red polygon creates the next equilateral polygon (green) using the inradius of the polygon and the angle between the faces.
  • The procedure CPlaneArea::CreatePolyHedron continues until all the faces of the Platonic Solid are created.

Your Own Applications Development Using the Project Provided

You may pick up all this project, rename it with the project of my former CodeProject article "MFC Project from Existing Code in One Click" and combine and improve the code as you like.

Or you may pick up the GlobUse directory from this project and include the special procedures contained in any your Own OpenGL project with menu Project->Existing Item.

Your references to my codes if any should be highly appreciated.

Points of Interest

You may compare the GLPlane.exe provided with the Lesson 18 sample "Quadratics" from my former Code Project article "50 OpenGL MFC Projects in One" where the "true" quadrics presentation has been demonstrated.

The semi-Quadric objects provided are technologically simple to maintain because just at the moment of creation, they "know" which Quadric they represent. After the creation, they became just the Object list of polygons which are easy to handle: cut, delete, divide, move, increase, decrease, rotate - all that you want them to do with your open codes. I hope to demonstrate it in my forthcoming articles.

The object list m_texturePointList in CPlaneArea class is the instrument for your own performance of the texture presentation.

The project has been developed in MFC platform. Nevertheless, everything developed in MFC may be converted to Win32 and vice versa.

History

All my previous CodeProject articles were the precursors to the present article.

And I believe that the present article is a precursor to my next forthcoming articles.

The job to be continued...

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)