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:
- 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:
int CGLPlaneView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
Init(); m_globTexture.Add((WORD)LoadGLPlane(_T("Data/MSUN.jpg"))); m_globTexture.Add((WORD)LoadGLPlane(_T("Data/famrt.jpg"))); m_globTexture.Add((WORD)LoadGLPlane(_T("Data/vldv.jpg"))); m_globTexture.Add((WORD)LoadGLPlane(_T("Data/rsfsr.jpg")));
m_globTexture.Add((WORD)LoadGLPlane(_T("Data/ussr.jpg")));
m_pPlaneObj = new CPlaneObject; 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
:
void CGLPlaneView::OnDraw(CDC* )
{
DrawScene();
}
void CGLPlaneView::DrawScene(void)
{
static BOOL bBusy = FALSE; if(bBusy) return;
bBusy = TRUE; glClearDepth(1.0f); glClearColor(0.9f, 0.9f, 0.9f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glTranslatef(0.0f, 0.0f, -m_z); glRotatef(wAngleX, 1.0f, 0.0f, 0.0f); glRotatef(wAngleY, 0.0f, 1.0f, 0.0f); glRotatef(wAngleZ, 0.0f, 0.0f, 1.0f);
if(!m_bTexture) switch (m_idObj) {
case ID_DEFAULT:DefaultDemoCube();break; default:
glEnable(GL_LIGHTING); m_pPlaneObj->DrawPlaneObject(FALSE); glDisable(GL_LIGHTING); break;
} else {
glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, (GLuint)m_globTexture.GetAt(m_texNum));
switch (m_idObj) {
case ID_DEFAULT:
glDisable(GL_LIGHTING);
DefaultTextureCube(); break;
default:m_pPlaneObj->DrawPlaneObject(TRUE); break; } glDisable(GL_TEXTURE_2D);
} glPopMatrix(); glFinish(); SwapBuffers(wglGetCurrentDC()); bBusy = FALSE; }
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"):
void CPlaneObject::DrawPlaneObject(BOOL bTexture )
{
for(POSITION pos = m_planeList.GetHeadPosition(); pos != NULL;)
{
CPlaneArea * pp = (CPlaneArea *)m_planeList.GetNext(pos);
pp->DrawPlaneArea (bTexture);
}}
The idea is that CPlaneObject
contains the Object List with the CPlaneArea
faces with the DrawPlaneArea
procedure for drawing:
void CPlaneArea ::DrawPlaneArea(BOOL bTexture)
{
if(!m_pointList.GetCount()) return;
POSITION psn = NULL; if (m_texturePointList.GetCount() && bTexture) {
glEnable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); psn = m_texturePointList.GetHeadPosition(); if (m_myTextureNum >= 0) if (m_myTextureNum != m_texNum) glBindTexture(GL_TEXTURE_2D, (GLuint)m_globTexture.GetAt(m_myTextureNum));
}
else {
glDisable(GL_TEXTURE_2D); glEnable(GL_LIGHTING); material.SetMaterial(GL_FRONT); }
::glEnable(GL_AUTO_NORMAL); ::glNormal3f(m_n.x, m_n.y, m_n.z); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glBegin(GL_POLYGON);
for ( POSITION pos = m_pointList.GetHeadPosition(); pos != NULL;)
{
Vector_3D * pnt = (Vector_3D *)m_pointList.GetNext(pos); if (m_texturePointList.GetCount()&& bTexture) if(psn != NULL) {
Vector_2D * ptx = (Vector_2D *)m_texturePointList.GetNext(psn);
glTexCoord2f((float)ptx->x, (float)ptx->y); }
::glVertex3f( (GLfloat) pnt->x, (GLfloat) pnt->y, (GLfloat) pnt->z );
}
glEnd();
if (m_texturePointList.GetCount() && bTexture) {
if (m_myTextureNum >= 0) if (m_myTextureNum != m_texNum) glBindTexture(GL_TEXTURE_2D, (GLuint)m_globTexture.GetAt(m_texNum));
}
}
The class CPlaneArea:public CPlane
derived from 3D CPlane
class:
class CPlane : public CObject
{
public:
CPlane();
CPlane(Vector_3D v0, Vector_3D v1, Vector_3D v2); virtual ~CPlane();
Vector_3D m_n; float m_D; };
and contains next key features in description:
CPlaneObject * m_pPlaneObject; CObList m_pointList; CObList m_texturePointList; int m_myTextureNum; CMaterial 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_CYLINDER
, ID_CONE
, ID_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
:
void CPlaneObject::CreatePolyHedron(int nType, double a)
{
double fi = 0.5 * (1 + sqrt(5.)); double ksi = sqrt((5. - sqrt(5.))*0.5); int nn = 3; if (nType == ID_DODECA) nn = 5;
else if( nType == ID_CUBE) nn = 4;
Polygon_2D * pPl = new Polygon_2D;
double m = pPl->CreateEquilateral(nn, a); double r = a * 0.5 / sqrt(6.); double tn2 = 1 / sqrt(2.); 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);
CPlaneArea * plNew = AppendPlaneArea(); 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); SetClockwise();
int nCount = 0;
for (POSITION pos = m_planeList.GetHeadPosition(); pos != NULL; nCount++)
{
CPlaneArea * ppl = (CPlaneArea *)m_planeList.GetNext(pos);
ppl->m_myTextureNum = nCount % m_globTexture.GetSize();
}}
CPlaneObject::CreatePolyHedron
creates the first face and all the others to be done one from another with the CPlaneArea::CreatePolyHedron
procedure:
void CPlaneArea::CreatePolyHedron(Polygon_2D * pPl, double m, double r, double tetta)
{
for (POSITION pos = m_pointList.GetHeadPosition(); pos != NULL;)
{ Vector_3D * pV = (Vector_3D *)m_pointList.GetNext(pos);
if (GetNeigbourOfLineSegment(pV) != NULL) continue; Vector_3D * pVn = GetNextPoint(pV, TRUE);
Vector_3D rzn = (*pVn - *pV); Vector_3D ex = Normalize(rzn); Vector_3D ny = m_n^ex; Vector_3D ey = m_n * (float)sin(tetta) + ny *(float) cos(tetta);
CPlaneArea * plt = m_pPlaneObject->AppendPlaneArea();
Vector_3D vc((*pV + *pVn)* 0.5f + (float)m * ey);
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); }
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:
- 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...