ECE REPORT CSP-02-003 OpenGL PROGRAMMING –DIALOG BASED AND MATLAB INTERFACE Laurence.G.Hassebrook

advertisement
ECE REPORT CSP-02-003
OpenGL PROGRAMMING –DIALOG BASED AND MATLAB INTERFACE
Laurence.G.Hassebrook
Daniel Lau
VeeraGanesh Yalla
Geethavani Goli
Signal and Image Processing Laboratory
Department of Electrical Engineering
University of Kentucky
12/20/2002
ABSTRACT
The report describes the integration of OpenGL and Microsoft Foundation Class
(MFC) features. OpenGL is a powerful tool to create 3-D graphics in any programming
language. It only needs a set of library files to be included to create graphics in any
language. The MFC has many existing classes to create graphics. A simple application to
draw a cube in a dialog box with slider control is explained step by step. A small section
on the use of Matlab to create a user interface is included. The Matlab function calls itself
recursively without the use of global variables.
KEYWORDS
OpenGL, Microsoft Foundation Class (MFC), rendering context
1. INTRODUCTION
OpenGL® stands for “Open Graphics Library”. OpenGL is an operating system and
hardware platform independent graphics library, which is used to draw/create interactive
3D graphics. OpenGL is in itself not a programming language like C, C++ or any other
language. It can be called API - Application Program Interface. It is easily portable and
very efficient. Its library consists of some 250 to 300 commands, which are used to
create graphics.
As mentioned before, OpenGL is a hardware independent platform. In order to
incorporate these features, commands to perform windowing tasks and to accept user
inputs are not included in OpenGL. So, according to the hardware being used, one should
use the particular windowing system that supports it.
In the same way, OpenGL does not include high-level commands to model complex 3D
objects. All the OpenGL graphics models should be built using geometric primitivespoints, lines and polygons. All primitives are a group of one or more vertices.
OpenGL® is a registered trademark of Silicon Graphics Inc.
2
OpenGL library GL.lib does not include high level commands but libraries like GLU
(OpenGL Utility) and GLUT (OpenGL Utility Toolkit) can be built on top of OpenGL.
These additional libraries are built to serve some special purposes which OpenGL library,
GL.lib does not offer. For example, GLU has quadrics routines that create some of the
same three dimensional such as a sphere, cylinder, cone etc. GLUT includes several
routines that create more complicated three-dimensional objects such as a sphere, a torus,
and a teapot. OpenGL and GLUT together simply open windows, detecting input and so
on.
Most implementations of OpenGL have a similar order of operations, a series of
processing stages called the OpenGL rendering pipeline. This ordering is not a strict rule
of how OpenGL is implemented but provides a reliable guide for predicting what
OpenGL will do.
2. A simplified version of the OpenGL pipeline
OpenGL
API calls
OpenGL
Command
Buffer
Transform
And
Lighting
Rasterization
Frame Buffer
Fig 1: simplified version of the OpenGL pipeline
As an application makes OpenGL API function calls, the commands are placed in a
command buffer. This buffer eventually fills with commands, vertex data, texture data,
and so on. When the buffer is flushed, either programmatically or by driver’s design, the
commands and data are passed to the next stage in the pipeline.
3
Vertex data is usually transformed and lit initially. To make it simple, “Transform and
lighting” is a mathematically intensive stage where points used to describe an object’s
geometry are recalculated for the given object’s location and orientation. Lighting
calculations are performed as well to indicate how brightly the colors should be at each
vertex.
After this stage, the data is fed to the rasterization portion of the pipeline. The rasterizer
actually creates the color image from the geometric, color and texture data. The image is
then placed in the frame buffer. The frame buffer is the memory of the graphics display
device, which means the image is displayed on the screen. This is a brief overview of
how 3D graphics rendering is done.
3. Overview of Device Contexts, Rendering Contexts, and Pixel Formats
Windows by itself has a 2D graphics API called the GDI-graphics device interface. A
window is rectangular area of the screen where an application displays data and looks for
mouse clicks. In order to use OpenGL calls in a windows program, one need to:
1) Get a device context for the rendering location.
2) Select and set a pixel format for the device context.
3) Create a rendering context (RC) associated with the device context.
4) Draw using OpenGL commands.
5) Release the RC.
6) Release the DC.
To understand these steps, one needs to have a clear knowledge about DC and RC.
3.1 Device Context (DC)
GDI-windows original 2D graphics interface is capable of drawing to the screen, to
memory, to printers, or to any other device that provides a GDI interface layer and that
can process GDI calls.
All GDI calls are passed through a DC (a rendering handle to the selected device such as
screen, printer etc.) and it does the rendering. One advantage of DC is that a DC created
for the screen, does rendering on the screen, then it switches to a printer DC and renders
4
using the same commands. So, with the same rendering commands, but with a different
DC, printer will have the same the thing that was shown on the screen.
3.2 Rendering Context (RC)
Similarly, RC is the OpenGL equivalent of the GDI DC. An RC is the context through
which OpenGL calls are rendered to the device. To put it in simple words, a rendering
context is what links OpenGL calls to the DC. The DC connects the Window to the GDI
(Graphics Device Interface). The RC connects OpenGL to the DC.
3.3 Pixel Formats
Pixel Formats are the translation layer between OpenGL commands and the rendering
operation that a window performs. The selected pixel format describes such things as
how colors are displayed, the depth of field resolution.
There are four functions in windows OpenGL API that handles the pixel format. These
are:
1) ChoosePixelFormat()
Obtains a device context’s pixel format that’s
the closest match to a pixel format template
that was provided by the user.
2) SetPixelFormat()
Sets a DC’s current pixel format to the pixel format
index specified .
3) GetPixelFormat()
Returns the pixel format index of a DC’s current
pixel format.
4) DescribePixelFormat()
Given a DC and a pixel format index, fills a
PIXELFORMATDESCRIPTOR data structure
with the pixel format’s properties.
5
3.3.1 Pixel Format Structure
The capabilities of an OpenGL window depend on the pixel format selected for the
OpenGL rendering window. Listed below are the properties of this format:
• Single or double buffering.
• RGBA or color indexing.
• Drawing to a window or bitmap.
• Support of GDI or OpenGL calls.
• Color depth (depth= number of bits).
• Z-axis depth.
• Stencil buffer.
• Visibility buffers.
In windows, these values are set for each OpenGL window, using a data structure called
the PIXELFORMATDESCRIPTOR. The pixel format has to be selected before the
OpenGL rendering context is created and once the pixel format is set for a rendering
context, it cannot be changed.
The exact values of the PIXELFORMATDESCRIPTOR that are supported depend on
1) The implementation of OpenGL that is running.
2) The current video mode Windows is running in.
3) Current video hardware installed.
The structure of PIXELFORMATDESCRIPTOR is as follows:
Typedef structtagPIXELFORMATDESCRIPTOR
{
WORD
nSize;
WORD
nVersion;
DWORD
dwFlags;
BYTE
iPixelType;
BYTE
cColorBits;
BYTE
cRedbits;
BYTE
cRedShift;
BYTE
cGreenBits;
6
BYTE
cGreenShift;
BYTE
cBluebits;
BYTE
cBlueShift;
BYTE
cAlphaBits;
BYTE
cAlphaShift;
BYTE
cAccumBits;
BYTE
cAccumRedBits;
BYTE
cAccumGreenBits;
BYTE
cAccumBlueBits;
BYTE
cAccumAlphaBits;
BYTE
cDepthBits;
BYTE
cStencilBits;
BYTE
cAuxBuffers;
BYTE
iLayerType;
BYTE
bReserved;
DWORD dwLayerMask;
DWORD
dwVisibleMask;
DWORD dwDamageMask;
} PIXELFORMATDESCRIPTOR,
*PPIXELFORMATDESCRIPTOR,
FAR *LPPIXELFORMATDESCRIPTOR;
STRUCTURE ELEMENT
DESCRIPTION
nSize
Specifies the size in bytes of the data structure and should be
set to
sizeof(PIXELFORMATDESCRIPTOR).
nVersion
Specifies the version of the structure(and not the OpenGL
version).
dwFlags
Specifies a set of bit flags that specify properties
of the pixel buffer. The properties are generally not
mutually exclusive; however, there are exceptions. The
following constants are defined:
PFD_DOUBLEBUFFER:This is used when one needs
double buffering. This flag is important if you want fast
rendering, since you usually draw to the hidden, or "back,"
buffer , then swap it to the front.
This flag and PFD_SUPPORT_GDI are mutually exclusive in
the release 1.0 generic implementation.
PFD_STEREO: This is used when one needs a
steroscopic buffer. This flag is not supported in the release
1.0 generic implementation.
7
PFD_DRAW_TO_WINDOW: We want to draw to a
window or device, or for the device driver to support it.
PFD_DRAW_TO_BITMAP:We want to draw to a memory
bitmap or for the device driver to support it.
PFD_SUPPORT_GDI:We want to have a buffer that
support GDI drawing. This flag and PFD_DOUBLEBUFFER
are mutually exclusive in the release 1.0 generic
implementation.
PFD_SUPPORT_OPENGL:We want to use OpenGL, or the
device supports it.
PFD_GENERIC_FORMAT:The pixel format is supported
by the generic implementation. If this bit is clear, this pixel
format is supposed by a device driver or by hardware.
PFD_GENERIC_ACCELERATED:
New with OpenGL 1.1, this flag is used with
PFD_GENERIC_FORMAT to differentiate between the
various driver types.
PFD_NEED_PALETTE: The buffer used RGBA
pixels
on a palette managed device. This means that a logical palette
is required to achieve the best
results. The colors in the
palette are specified according to the values of the cRedBits,
cRedShift, cGreenBits, cGreenShift, cBluebits, and cBlueShift
members. The palette should be created and realized in the DC
before calling wglMakeCurrent().
PFD_NEED_SYSTEM_PALETTE: Flag used by OpenGL
hardware that supports only one palette. To use hardware
accelerations in such hardware, the hardware palette had to be
in a fixed order in RGBA mode or match the logical palette
in color-index-mode. In the release 1.0 generic
implementation the PFD_NEED_PALETTE flag doesn't have
such a requirement.
That is, if only PFD_NEED_PALETTE is set, the logical-tosystem palette mapping is performed by the system.
However, if PFD_NEED_SYSTEM_PALETTE is set, you
8
should take over the system palette in your program by calling
SetSystemPaletteUse () to force a one-to-one logical-tosystem palette mapping. If your OpenGL hardware supports
multiple hardware palettes and the device driver can allocate
spare hardware palettes for OpenGL, then this flag may not be
set. If a format requires PFD_NEED_SYSTEM_PALETTE
but your program ignores it because it doesn't want to mess up
the desktop colors, it won't get maximum performance but
should still work. The PFD_NEED_SYSTEM_PALETTE flag
isn't needed if the OpenGL hardware supports multiple
hardware palettes and the driver can allocate spare
hardware palettes and the driver can allcate spare hardware
palettes for OpenGL.The generic pixel formats don't have this
flag set.
PFD_SWAP_COPY: Not found in the original Windows NT
3.5 implementation. This is an advanced flag that depends on
your OpenGL implementation and its extensions. If this flag is
specified, it's a hint that you want the back buffer copied to the
front buffer when the buffers are swapped. This is different
from the default behaviour, whereby the back buffer becomes
undefined when the buffers are swapped. The contents of the
back buffer are not affected. Also see the next flag.
PFD_SWAP_EXCHANGE: Not found in the original
Windows NT 3.5 implementation. This is an advanced flag
that depends flag that depends on your OpenGL
implementation and its extensions. If this flag is specified, it is
a hint that you want the back buffer exchanged with the front
buffer when the buffers are swapped. This is different from the
default behaviour, whereby the back buffer becomes
undefined when buffers are swapped. The contents of the back
buffer are swapped with the front buffer.
PFD_DOUBLEBUFFER_DONTCARE:
Used only when calling the ChoosePixelFormat() function.
This means to ignore single or double buffering when
selecting a match for a format you've set up.
PFD_DOUBLEDONTCARE: Used only when calling the
ChoosePixelFormat() function. This means to ignore the
stereo flag when selecting a match for a format you've set up.
iPixelType
Specifies the color type of the pixel data. The following types
are defined:
• PFD_TYPE_RGBA: The pixel color is specified as
RGBA values. Each pixel has four separate color
9
•
components: red, green, blue and alpha. Each
component value varies from 0.0 to 1.0.
PFD_TYPE_COLORINDEX: The pixel color is
specified in a lookup table. Each pixel uses a color
index value to look up a palette value instead of an
RGBA value. This setting is needed if the RGBA
setting fails to render colors satisfactorily or if
palette animation is to be performed.
cColorBits
Specifies the number of color bit planes in each color
buffer. For RGBA mode it's the size in bits of the color
buffer. This value doesn’t include the alpha bit planes. If
current mode is "true color" mode (16 million colors, or
24-bit color), then the hardware supports 8 bits per RGB
color. So the value of cColorBits would be 24 bits
(3*8=24). If the current mode is 256-color mode, the
values for cColorBits will be never greater than 8 bits.
Values will generally range from 4 to 32.For color-index
mode it's the size of the color palette. Generally, RGBA
mode is preferred over color-index mode. If color-index
mode is used, one needs to set up the palette.
cRedBits
Specifies the number of red bit planes in each RGBA
color buffer. For example, a value of 3 indicates that
8(2^3) different intensities of red are available.
cRedShift
Specifies the shift count for red bit planes in each
RGBA color buffer. That is, it's where the red bits can
be found in the color buffer. The shifts for red, green,
and blue will all be different. For example, in an 8-bit,
256 color mode, the last two bits in an 8-bit color value
are usually the two blue bits. The blue shift value would
then be six.
cGreenBits
Specifies the number of green bit planes in each RGBA
color buffer.
cGreenShift
Specifies the shift count for green bit planes in
RGBA color buffer.
cBlueBits
Specifies the number of blue bit planes in each RGBA
color buffer.
cBlueShift
Specifies the shift count for blue bit planes in each
RGBA color buffer.
each
10
cAlphaBits
Specifies the number of alpha bit planes in each RGBA
color buffer. The alpha bit planes are used to specify the
opacity of a color. Alpha bit planes are not supported in
the release 1.0 generic implementation.
cAlphaShift
Specifies the shift count for alpha bit planes in each
RGBA color buffer.
cAccumBits
Specifies the total number of bit planes in the
accumulation buffer, which is used for accumulating
images.
cAccumRedBits
accumulation buffer.
Specifies the number of red bit planes in the
cAccumGreenBits
Specifies the number of green bit planes in the
accumulation buffer.
cAccumAplhaBits
Specifies the number of blue bit planes in the
accumulation buffer.
cDepthBits
Specifies the number of bits of the depth (z-axis) buffer.
Generally this value is 0, 16,24, or 32.
cStencilBits
Specifies the number of bits of the stencil buffer, which is
used to restrict drawing to certain areas.
cAuxBuffers
Specifies the number of auxiliary buffers, which are not
supported in the release 1.0 generic implementation.
iLayerType
Specifies the type of layer. The generic implementation
supports only the main plane, although the following values
are defined:
• PFD_MAIN_PLANE: The layer is main plane.
• PFD_OVERLAY_PLANE: The layer is the
overlay plane.
• PFD_UNDERLAY_PLANE: The layer is the
underlay plane.
bReserved
Not used. Must be zero.
11
dwLayerMask
Obsolete in OpenGL 1.1 or later. Designated the layer mask,
which is used in combination with the visible mask to see
whether one layer overlays another.
dwVisibleMask
Designates the visible mask, which is used in conjunction
with the layer mask to determine whether one layer overlays
another. If the result of the bitwise-AND of the visible mask
of a layer and the layer mask of a second layer is nonzero, the
first layer overlays the second layer, and a transparent pixel
value exists between the two layers. If the visible mask is 0,
the layer is opaque.
dwDamageMask
Obsolete in OpenGL 1.1 or later. Specified whether more
than one pixel format shares the same frame buffer. If the
result of the bitwise AND of the damage masks between two
pixel formats is nonzero, they share the same buffers.
3.4 WGL Rendering Contexts
When an OpenGL window is created, the RC is created along with the pixel format, and
each is held inside the class structure. Every OpenGL command is linked to a RC. A RC
is what links OpenGL calls to a Windows window.
In order to set up a connection between a RC and OpenGL calls, some routines are used.
These are as follows:
FUNCTIONS
DESCRIPTION
wglCreateContext()
Creates a new OpenGL RC, suitable for drawing on the device
referenced by the DC provided. The RC has the same pixel
format as the DC. An application should set the DC's pixel
format before creating a RC.
wglMakeCurrent()
Makes the specified RC the calling thread's current RC.
OpenGL calls made by the thread are then rendered on the
device identified by the DC. If the RC specified is NULL, the
function deselects the calling thread's current RC and releases
the DC used by the RC. In this case the DC is ignored.
wglDeletecontext()
Deletes the RC specified. It's an error to delete an RC that is
another thread's current RC. However, if an RC is the calling
12
thread's current RC, the function makes the RC not current
before deleting it.
wglGetCurrentContext()
If the calling thread has a current RC, the function
handle to that RC. Otherwise, NULL is returned.
returns a
wglGetCurrent()
If the calling thread has a current RC, the function returns a
handle to the DC associated with the RC by means of
wglMakeCurrent(). Otherwise, NULL is returned.
The process of creating a RC from a DC is shown in the figure (2) below. The optimized
approach is to get a DC first, then set the pixel format, create the RC, and make the RC
current, all when a WM_CREATE is received. When the WM_PAINT calls are received
(usually many WM_PAINT calls are received), only OPENGL calls have are to be
performed, since RC is already created and selected. Finally, when the program is
terminating, the RC is deselected and deleted, thereby releasing the DC.
WM_CREATE
GET DC
SET PIXELFORMAT
CREATE RC FROM DC
MAKE RC CURRENT
USING DC
WINDOWS
MESSAGE
LOOP
WM_PAINT
MAKE OpenGL CALLS
WM_DESTROY
MAKE RC NONCURRENT
DELETE RC
RELEASE DC
Fig 2: DC/RC Handling
13
4. Microsoft Foundation Class (MFC)
Any program, which uses OpenGL, requires a window to display the graphics. But as
OpenGL cannot handle windowing tasks by itself, MFC is used for windows 95/98/NT.
MFC window is a hybrid of C++ and Windows API calls. In effect, an MFC window
gives a C++ wrapper to a lot (but not all) of the Windows API, eliminating some of the
drudgery of writing a Windows application and providing some new services.
The Microsoft Foundation Class (MFC) Library is a collection of C++ classes
(generalized definitions used in object-oriented programming) that can be used in
building application programs. The classes in the MFC Library are written in the C++
programming language. The MFC Library saves a programmer time by providing code
that has already been written. It also provides an overall framework for developing the
application program.
There are MFC Library classes for all graphical user interface elements (windows,
frames, menus, tool bars, status bars, and so forth), for building interfaces to database, for
handling events such as messages from other applications, for handling keyboard and
mouse input and for creating ActiveX control.
OpenGL can be integrated with the MFC based graphic programs. The OpenGL output
can display in a portion of a Dialog box or a SDI (Single Document Interface) designed
using the MFC. The properties of the drawings so created using OpenGL can be
controlled by inserting additional Command buttons, Sliders etc. in the Dialog box or by
additional menu items in the SDI.
14
5. Creating a Dialog based OpenGL application
The following sections (5.1 – 5.4) give the step by step procedure to create and execute a
MFC based OpenGL application program.
5.1 Creating the Project:
Press File
Press New
Select "Projects"
Select “MFC AppWizard (exe)” (Opens up the MFC application wizard.)
Give the project name as say " DlgOgl” the same project name will be referred
to throughout the example.
Give the location of the project and press ok.
5.2 Steps within MFC Wizard:
Step 1:
Select "Dialog Based" Press Next
Step 2:
Change the title for the dialog box if needed by specifying your title in
the “Please enter a title for your dialog” box. The title for the
application we designed was “OpenGl in a Dialog Box”. Leave the
default settings and Press Next.
Step 3:
Check “statically linked library”, leave the other default settings.
Press Next
Step 4:
Press Finish.
Visual C++ responds by displaying the New Project Information
window.
Step 5:
Click OK button of the New project Information window.
15
Based upon MFC wizard selections a dialog box is shown in the design
window.
To set the project configurations:
From the Build menu select ‘set active configuration”, Visual C++.
responds by displaying the default project configuration dialog box.
Select DlgOgl – Win32 Release in the Default Project Configuration
dialog box, and then click the OK button.
The creation of project file and skeleton files of the DlgOgl program are
completed.
To add the needed libraries for open GL:
From the Project menu select “Settings”, click the Link tab and enter the
following in the Object/library modules box.
opengl32.lib glu32.lib glut32.lib
5.3 Visual Design of the DlgOgl program
To display the dialog box in the workspace design area if not already shown:
Select Project Workspace from the View menu
Select the ResourceView tab of the Project Workspace window.
Expand the DlgOgl resources item
Expand the Dialog item.
Double-click the IDD_DLGOGL_DIALOG item.
Visual C++ responds by displaying the IDD_DLGOGL_DIALOG dialog box in
design mode.)
Design the IDD_DLGOGL_DIALOG dialog box according to Table 1 .Delete the Ok
button, Cancel button, and text in the IDD_DLGOGL_DIALOG dialog box.
16
Table 1: The properties table of the IDD_DLGOGL_DIALOG dialog box
Object
Property
Setting
Dialog Box
ID
Caption
Font
Minimize box
Maximize box
IDD_DLGOGL_DIALOG
OpenGL in a Dialog Box
System, Size 10 (General tab)
Checked (Styles tab)
Checked (Styles tab)
Push Button
ID
Caption
IDOK
&Quit
Slider
ID
Orientation
Point
IDC_SLIDER
Horizontal (Styles tab)
Both (Styles tab)
Picture Box
ID
Type
Visible
IDC_OPENGLWIN
Frame (General Tab)
Checked (General Tab)
5.3.1 Attaching Variable to the Slider:
Step 1:
Select ClassWizard from the View menu
Visual C++ responds by displaying the MFC ClassWizard dialog box.
Step 2:
Select the Member Variables tab of the MFC ClassWizard dialog box. (See
figure 3)
17
Fig 3: selecting ClassWizard’s Member Variables tab
Step 3:
Make sure that the Class name drop-down list box is set to CDlgOglDlg.
Step 4:
Select IDC_SLIDER in the Object IDs list to attach a variable to the
IDC_SLIDER, the slider control.
Step 5:
Click the Add Variable button
Visual C++ responds by displaying the Add Member Variable dialog box (see
figure 4)
18
Fig 4: Add Member Variable dialog box
Step 6:
Set the Add Member Variable dialog box as follows:
Member Variable Name: m_clSlider
Category: Value
Variable type: CSliderCtrl
Step 7:
Click the OK button of the Add Member Variable dialog box.
Step 8:
Click the OK button of the MFC ClassWizard dialog box.
5.3.2 Adding the CGlView Class:
The CGlView class will contain the OpenGL code.
Step 1:
Select ClassWizard from the View menu
Visual C++ responds by displaying the MFC ClassWizard dialog box.
Step 2:
Click on the Add Class button and select “New”.
Visual C++ responds by displaying the New Class dialog box as shown in
figure(5) below:
19
Fig 5: New Class dialog box
Step 3:
Type the following Class information:
Name: CglView (note: use lowercase ‘L’ in CglView)
The GlView.cpp and Glview.h files will be automatically created.
Base Class: choose generic CWnd
Step 4: Click OK button on the New Class dialog box.
Step 5: Click OK button on the MFC ClassWizard dialog box.
5.3.3 Adding Code to GlView.cpp file
Step 1:
Open the GlView.cpp file from the project workspace menu by selecting the
File View tab.
Step 2:
Edit the CGlView::CGlView ()
{
}
function as follows:
20
CGlView::CGlView (CWnd *pclWnd)
{
m_pclWnd = pclWnd;
m_hWnd = pclWnd->m_hWnd;
m_hDC = ::GetDC (m_hWnd);
}
Step 3:
Add a new member function int CGlView::OnCreate() as follows:
o Click on the Class tab of the Project Workspace.
o Select CGlView class from the DlgOgl classes.
o Right Click the CGlView Class.
o The following figure(6) shows the menu that pops up:
Fig 6: pop up menu
o Choose the Add Member Function option.
o The figure(7) shows the Add Member Function dialog box.
21
Fig 7: Add Member Function dialog box
o Type the following:
Function type: int
Function Declaration: OnCreate ()
Step 4:
In a similar way, add the following functions to the GlView.cpp:
o BOOL CGlView::SetPixelformat(HDC hdc)
Function type: BOOL
Function Declaration: SetPixelformat(HDC hdc)
o GLvoid CGlView::ReSizeGLScene(GLsizei width, GLsizei height)
Function type: GLvoid
Function Declaration: ReSizeGLScene (GLsizei width,
GLsizei height)
o int CGlView::InitGL(GLvoid)
Function type: int
Function Declaration: InitGL(GLvoid)
o int CGlView::DrawGLScene (GLint pos)
Function type: int
Function Declaration: DrawGLScene (GLint pos)
The code to be added to each member function is given in Appendix-1
22
5.3.4 Adding Code to GlView.h file
Step 1:
Open the GlView.h file from the project workspace menu by selecting the File
View tab.
Step 2:
Include the following header files under the //CGlView comment line
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#include "math.h"
Step 3:
Add the following code to the GlView.h file after the #include declarations
(some of the code already exists by default)
class CGlView : public CWnd
{
// Construction
public:
CGlView(CWnd *pclWnd);
// Attributes
public:
HDC m_hDC;
// GDI Device Context
HGLRC m_hglRC; // Rendering Context
CWnd *m_pclWnd;
HWND m_hWnd;
// Operations
public:
BOOL SetPixelformat(HDC hdc);
GLvoid ReSizeGLScene(GLsizei width, GLsizei height);
int InitGL(GLvoid);
int DrawGLScene(GLint pos);
// Overrides ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CGlView)
protected:
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CGlView();
// Generated message map functions
protected:
public:
int OnCreate();
//{{AFX_MSG(CGlView)
//
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//
afx_msg void OnSize(UINT nType, int cx, int cy);
23
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
5.3.5 Adding Code to the DlgOglDlg.h file
#include “GlView.h” (put at top)
CglView* m_pclGlView; (put under public, use lower case ‘L’ for
m_pclGlView)
5.3.6 Adding Code to the DlgOglDlg.cpp file
Step1:
Open the DlgOglDlg.cpp file from the project workspace menu by selecting the
File View tab.
Step 2:
Edit the BOOL CDlgOglDlg::OnInitDialog() function as follows
(some of the code already exists by default)
…
…
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE);
// Set big icon
SetIcon(m_hIcon, FALSE);
// Set small icon
// TODO: Add extra initialization here
m_clSlider.SetRange(0,360);
m_clSlider.SetPageSize(60);
m_clSlider.SetPos(180);
CStatic *pclStatic = (CStatic *)GetDlgItem(IDC_OPENGLWIN);
m_pclGlView = new CGlView(pclStatic);
//OpenGL in Picture in a Dialog
CPaintDC dc(this); // device context for painting
HDC m_hDC;
m_hDC = ::GetDC(this->m_hWnd);
RECT rect;
GetClientRect(&rect);
int iWidth = -(rect.right - rect.left);
int iHeight = rect.top - rect.bottom;
m_pclGlView->OnCreate();
24
m_pclGlView->ReSizeGLScene(iWidth, iHeight);
m_pclGlView->InitGL();
m_pclGlView->DrawGLScene(0);
return TRUE; // return TRUE unless you set the focus to a control
…
Step 3:
Edit the BOOL CDlgOglDlg::OnPaint() function as follows:
(some of the code already exists by default)
…
…
…
CDialog::OnPaint();
}
//
m_pclGlView->DrawGLScene(m_clSlider.GetPos());
//
…
…
Step 4:
Add the OnHScroll( ) function to the DlgOglDlg.cpp program using the
ClassWizard as follows:
o Select the Message Maps tab, make sure CDlgOglDlg in the object
ID’s window is selected. In the messages window, select
WM_HSCROLL.
o Click the Add Function button and then click the edit button to add
the following code to the OnHScroll function.
The code fragment for the OnHScroll function is
{
m_pclGlView->DrawGLScene(m_clSlider.GetPos());
}
Step 5:
Add the OnDestroy( ) function to the DlgOglDlg.cpp program using the
ClassWizard as follows:
o Select the Message Maps tab and select WM_DESTROY under the
messages window.
o Click the Add Function button and click OK.
The code fragment for the OnDestroy function is automatically created by the
ClassWizard.
25
5.4 Executing the Program:
Step 1:
Select the Build DlgOgl.Exe option from the Build menu.
Step 2:
The application DlgOgl.exe can be executed by selecting Execute DlgOgl.Exe
from the Build menu.
The following figure(8) shows the screenshot of the output obtained
Fig 8: Output
26
6. Creation of Matlab Interface:
MATLAB is a powerful programming language as well as an interactive computational
environment. Files that contain code in the MATLAB language are called M-files. Mfiles are created using a text editor, and then used as any other MATLAB function or
command.
There are two kinds of M-files:
•
Scripts, which do not accept input arguments or return output arguments. They
operate on data in the workspace.
•
Functions, which can accept input arguments and return output arguments.
Internal variables are local to the function
6.1 Matlab Graphics:
6.1 .1 Handle Graphics:
MATLAB provides a set of low-level functions that allow the creation and manipulation
of lines, surfaces, and other graphics objects. This system is called Handle Graphics.
6.1.2 Graphics Objects:
Graphics objects are the basic drawing primitives of MATLAB's Handle Graphics
system. The objects are organized in a tree structured hierarchy. This reflects the
interdependence of the graphics objects. For example, Line objects require Axes objects
as a frame of reference. In turn, Axes objects exist only within Figure objects.
6.1.3 Graphics Objects:
There are eleven kinds of Handle Graphics objects:
•
The Root object is at the top of the hierarchy. It corresponds to the computer
screen. MATLAB automatically creates the Root object at the beginning of a
session.
•
Figure objects are the windows on the root screen other than the Command
window.
•
Uicontrol objects are user interface controls that execute a function when users
activate the object. These include pushbuttons, radio buttons, and sliders.
•
Axes objects define a region in a figure window and orient their children within
this region.
•
Uimenu objects are user interface menus that reside at the top of the figure
window.
27
•
Image objects are two-dimensional objects that MATLAB displays using the
elements of a rectangular array as indices into a colormap.
•
Line objects are the basic graphics primitive for most two-dimensional plots.
•
Patch objects are filled polygons with edges. A single Patch can contain multiple
faces, each colored independently with solid or interpolated colors.
•
Surface objects are three-dimensional representations of matrix data created by
plotting the value of the data as heights above the x-y plane.
•
Text objects are character strings.
•
Light objects define light sources that affect all objects within the Axes.
A simple Matlab Graphics Interface function is described below. The program uses the
Matlab Handle Graphics Objects described above. The advantage of this program is
avoiding the use of global variables. The function is self supportive and does not need
any external variables for its operation. This is only a template program and can be
further extended to include several other features. The program explanation is done in the
form of comment lines (in bold italics).
6.2 Function “MYGUI.m”:
% Dr Daniel Lau's Menu Creation program
% Declare the function MYGUI. The function has three input arguments str, arg1 &
% arg2
function h=MYGUI (str,arg1,arg2)
%
% Check for the number of input arguments using nargin. If the number of input
% arguments is less than one then set the str value to ‘INITIALIZE’.
if (nargin < 1)
str = 'INITIALIZE';
end
%
% Since there can be several cases based on the value of str, we use a switch-case
% statement to evaluate the function based on the value of str.
switch str
% Case if value of str = ‘INTIALIZE’
28
case 'INITIALIZE'
I = rand(100,100); % generate a random number array of 100x100.Tthis is used
% as a image for display purpose.
h = figure('POSITION',[100 100 120 150],...
'Resize','of'); % creates a figure window with specified properties
h(2) = axes('units','pixels',...
'position',[10,40,100,100]); % create axes based on the properties specified
imshow(I);
% display the array I as a 2D image
h(3) = uicontrol('style','pushbutton',...
'units','pixels',...
'position',[10 10 100 20],...
'string','ROTATE',...
'callback','MYGUI(''BUTTON'');'); % creates a user interface control, a
% pushbutton with caption
% ‘ROTATE’
h(4) = uimenu('label','mymenu');
h(5) = uimenu(h(4),'label','option1');
% create a user defined menu
% ‘mymenu’
% create a submenu in the
%‘mymenu’ label it as ‘option1’
set(h(2),'userdata',I);
% set the axes properties of axes to
% I with handle h(2)
set(h(1),'userdata',h);
% set the figure properties
% to the array h with handle h(1)
return;
% case if str = ‘BUTTON’, if the push button is pressed
case 'BUTTON'
h = get(gcf,'userdata');
% get userdata to handle of current
% figure
I = get(h(2),'userdata');
% get userdata with handle h(2)
I= rot90(I);
% rotate I by 90o and update
axes(h(2));
% reset the axes
29
imshow(I);
% display the rotated I
set(h(2),'userdata',I);
% set the userdata property to current
% with handle h(2)
I
return;
end
%
30
Appendix –1 (GlView.cpp)
/////////////////////////////////////////////////////////////////////////////
// CGlView message handlers
int CGlView::OnCreate()
{
m_hDC = ::GetDC(this->m_hWnd);
if(!SetPixelformat(m_hDC))
{
::MessageBox(::GetFocus(),"SetPixelformat Failed!","Error",MB_OK);
return -1;
}
m_hglRC = wglCreateContext(m_hDC);
int i= wglMakeCurrent(m_hDC,m_hglRC);
InitGL();
return 0;
}
BOOL CGlView::SetPixelformat(HDC hdc)
{
PIXELFORMATDESCRIPTOR *ppfd;
int pixelformat;
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1,
// version number
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL |
// support OpenGL
PFD_GENERIC_FORMAT |
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA,
// RGBA type
24,
// 24-bit color depth
0, 0, 0, 0, 0, 0,
// color bits ignored
8,
// no alpha buffer
0,
// shift bit ignored
8,
// no accumulation buffer
0, 0, 0, 0,
// accum bits ignored
32,
// 32-bit z-buffer
8,
// no stencil buffer
8,
// no auxiliary buffer
PFD_MAIN_PLANE,
// main layer
0,
// reserved
0, 0, 0
// layer masks ignored
};
31
ppfd = &pfd;
if ( (pixelformat = ChoosePixelFormat(hdc, ppfd)) == 0 )
{
::MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK);
return FALSE;
}
if (SetPixelFormat(hdc, pixelformat, ppfd) == FALSE)
{
::MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK);
return FALSE;
}
return TRUE;
}
GLvoid CGlView::ReSizeGLScene(GLsizei width, GLsizei height)// Resize And Initialize The GL
Window
{
if (height==0)
{
height=1;
}
glViewport(0,0,width,height);
// Reset The Current Viewport
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity();
// Reset The Projection Matrix
// Calculate The Aspect Ratio Of The Window
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity();
// Reset The Modelview Matrix
}
int CGlView::InitGL(GLvoid)
// All Setup For OpenGL Goes Here
{
//
glClearColor(0.0f, 0.0f, 1.0f, 1.0f); // Black Background
glClearDepth(1.0f);
// Depth Buffer Setup
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL);
// The Type Of Depth Testing To Do
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice
//Perspective calculations
//
return TRUE;
32
}
int CGlView::DrawGLScene(GLint pos)
{
// The Drawing Space
GLfloat mat_specular[] = {0.0,0.0,0.0,1.0};
GLfloat mat_shininess[] = {128.0};
GLfloat light_position[] = {1.0,0.0,0.0,1.0};
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glPushMatrix ();
//glTranslatef (0.0, 0.0, -5.0);
glPushMatrix ();
//
glRotated (0.0, 1.0, 0.0, 0.0);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glTranslated (0.0, 0.0, -5.5);
glEnable (GL_LIGHTING);
//
glPopMatrix ();
//
glTranslatef(0.0f,0.0f,-5.0f);
// Move Into The Screen
glRotatef((float)pos-180,0.0,1.0,0.0);
//glRotatef(-54.73561f,1.0f,0.0f,0.0f);
//glRotatef(45.0f,0.0f,1.0f,0.0f);
glBegin(GL_QUADS);
// Drawing Front
glVertex3f( -1.0f, 1.0f, 1.0f);
// Top Left
glVertex3f( 1.0f, 1.0f, 1.0f);
// Top Right
glVertex3f( 1.0f,-1.0f, 1.0f);
// Bottom Right
glVertex3f( -1.0f,-1.0f, 1.0f);
// Bottom Left
glEnd();
glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
glBegin(GL_QUADS);
// Drawing Right
glVertex3f( -1.0f, 1.0f, 1.0f);
// Top Left
glVertex3f( 1.0f, 1.0f, 1.0f);
// Top Right
glVertex3f( 1.0f,-1.0f, 1.0f);
// Bottom Right
glVertex3f( -1.0f,-1.0f, 1.0f);
// Bottom Left
glEnd();
glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
glBegin(GL_QUADS);
// Drawing Back
glVertex3f( -1.0f, 1.0f, 1.0f);
// Top Left
glVertex3f( 1.0f, 1.0f, 1.0f);
// Top Right
glVertex3f( 1.0f,-1.0f, 1.0f);
// Bottom Right
glVertex3f( -1.0f,-1.0f, 1.0f);
// Bottom Left
glEnd();
glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
glBegin(GL_QUADS);
// Drawing Left
glVertex3f( -1.0f, 1.0f, 1.0f);
// Top Left
glVertex3f( 1.0f, 1.0f, 1.0f);
// Top Right
glVertex3f( 1.0f,-1.0f, 1.0f);
// Bottom Right
glVertex3f( -1.0f,-1.0f, 1.0f);
// Bottom Left
33
glEnd();
glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
glBegin(GL_QUADS);
glVertex3f( -1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f,-1.0f, 1.0f);
glVertex3f( -1.0f,-1.0f, 1.0f);
glEnd();
glRotatef(180.0f, 1.0f, 0.0f, 0.0f);
glBegin(GL_QUADS);
glVertex3f( -1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f,-1.0f, 1.0f);
glVertex3f( -1.0f,-1.0f, 1.0f);
glEnd();
// Drawing Top
// Top Left
// Top Right
// Bottom Right
// Bottom Left
// Drawing Bottom
// Top Left
// Top Right
// Bottom Right
// Bottom Left
//
glPopMatrix ();
//
SwapBuffers(m_hDC);
return TRUE;
}
34
Bibliography
1) Ron Fosner, OpenGL programming for Windows 95 and Windows NT, Addison
Wesley Developers press, 1997, ISBN 0-201-40709-4
2) Woo, Neider, Davis, Shreiner, OpenGL Programming Guide, Addison Wesley,
3rd edition, ISBN 0-201- 60458-2
3) Richard S.Wright, Jr., Michael Sweet, OpenGL SuperBible, Waite Group press,
2nd edition, ISBN 1-57169-164-2
4) Davis Chapman, Sam’s Teach yourself Visual C++ 6.0 in 21 days, SAMS, 1998,
ISBN 0-672-31240-9
35
Download