[ First ]  [ Previous ]  [ Next ]  [ Last ]  [ Manuals ]

 

Chapter 3.

 

CodeWarrior for Win32: A Tutorial



This tutorial is designed to give you a quick start at learning how to use the CodeWarrior® IDE for Win32/x86 programming. These are the topics covered in this chapter:


Tutorial: Meet Ray the Tracer

In this tutorial, you will create a ray-tracing application program. Ray tracing is a method of rendering three-dimensional scenes with photo-realistic accuracy, including shading, shadows, and reflection of light. It operates by tracing the path from your eye to objects, and from the objects to the sources of light. The process is highly processor-intensive, requiring a significant amount of computation for each pixel displayed.


TIP

For best results when running your application, have your monitor set to display at least 16 thousand colors.


To give you some useful experience, the user interface for the program will be a Microsoft Foundation Classes (MFC) application, and the ray-tracing engine will be a dynamic link library (DLL). For more information, see:

The IDE User Guide contains general information on using the CodeWarrior IDE.


Phase 1: Creating a Project

Before you begin, copy the RayTracer folder from the CodeWarrior Reference CD to your hard disk. You'll find it in the CodeWarrior Examples folder, in the Win32 Examples sub-folder under Win32 Tutorial. Open the copy of the folder on your hard disk, start up the CodeWarrior IDE, then follow these steps to set up the project:

1. Create a new project.

Choose File > New. The New dialog appears, as shown in Figure 3.1.

New dialog:

2. Select the project category.

Select Win32 MFC Application Stationary from the list in the Project tab. Type RayTracer.mcp in the Project name field. By convention, a project name ends with .mcp. You may also use the Location text box or Set button to choose a different location to store the project. Then click OK.

3. Choose stationery.

Choose the MFC App project stationery from the Project Stationary area of the New Project window, as shown in Figure 3.2. Then, click OK.

New Project dialog:

4. Add source files to the project.

When the IDE finishes opening your project, a window bearing the project name you typed in the Project name text box of the New dialog appears. Click Sources in the project window, then choose Projet > Add Files. The Select files to add dialog appears, as shown in Figure 3.3. Navigate to the RayTracer folder, select Ray1.cpp, and click Add. The Add Files dialog appears, as shown in Figure 3.4. Add the file to all targets by enabling all options in the Targets area, then click OK.

Select files to add dialog:
Add Files dialog:

5. Remove the source file placeholder from your project.

Select main.cpp in the Sources group of the project window and choose Edit > Delete. Your project window should now look like Figure 3.5.

Project window with source file added:

6. Display the Target Settings dialog.

Choose Edit > Target Settings, where Target is the name of the project's currently selected target. Since we have selected an MFC App target for our RayTracer project, the command appears as MFC App Debug Settings. The Target Settings dialog appears.


TIP

You can also display the Target Settings dialog by clicking the Target Settings button, which is the left-most button in the project window.


7. Select the x86 Target panel.

Click x86 Target in the Target Settings Panels area of the Target Settings dialog. The x86 Target setting panel appears, as shown in Figure 3.6.

Target Settings dialog showing the x86 Target panel:

8. Specify the name and type of output file.

Make sure Application (EXE) is selected in the Output File Type list box, and type Ray.exe into the File Name text box. Then, click OK to confirm your settings and close the dialog.

9. Build the program.

Choose Project > Make. The CodeWarrior IDE will then compile and link your program.

10. Run the program.

Choose Project > Run. If all goes well, an empty window titled Ray the Tracer appears that you can manipulate on your screen: move it, resize it, maximize it, minimize it, and close it.


A Look at the Code

You have just built the first version of Ray the Tracer, which at this point only draws an empty window on the screen. Let's take a look at the code.

In the Ray.mcp project window, double-click the file Ray1.cpp to open an edit window displaying the contents of the file. At the top of the source file, the following line appears:


  #include <afxwin.h>

The header file afxwin.h defines the most common of the Microsoft Foundation Classes (MFC). MFC is an object-oriented interface to Win32, consisting of classes either used directly in programs or from which subclasses can be derived.

The next line of code reads:


  class CTracerWindow: public CFrameWnd

This line defines a class for our main window, CTracerWindow. This class is derived from the MFC class CFrameWnd, which implements the behavior of a standard Win32 window. The only member function in our derived class is the constructor, CTracerWindow. Looking down a little further at the definition of this function, you can see that it simply calls the Create function, inherited from the base class CFrameWnd, to create the window.

The line


  class CTracerApp: public CWinApp

defines a class derived from CWinApp, the MFC class representing a Win32 application. Its only member function is InitInstance, called during application startup. Our definition of this function creates a new window of class CTracerWindow and assigns it to member variable m_pMainWnd, inherited from the base class CWin-App. The next two statements display the window and update (paint) its contents on the screen. Finally, InitInstance returns TRUE to indicate that it has succeeded.

This last line in the source file,


  CTracerApp TracerApp;

may look innocuous, but it actually is responsible for the entire execution of the program. Here's what MFC does behind the scenes:

1. It declares a variable of type CTracerApp.

2. The constructor for this object calls the InitInstance function we just looked at.

3. InitInstance, in turn, creates the window.

4. The CTracerApp constructor then automatically initiates a message loop, which runs for the duration of the application.

5. Finally, the message loop receives input events (clicks and keystrokes) from the mouse and keyboard and dispatches it to the code that handles these events.

Currently, our program does not contain code to handle events, so it cannot do very much. However, MFC provides defaults that allow the user to manipulate the window on the screen in all the usual ways: move it, resize it, maximize it, minimize it, or close it. That's actually quite a lot for such a simple application. The main thing is, it's a working program. If getting started is the hardest part of a project, that part, at least, is done.

Now that we're finished examining the code, close the Ray1.cpp window.


Phase 2: Getting Resourceful

Most Win32 programs use resources to define menus, icons, and other objects that the program uses. In this phase, we will add a text resource file to our application project.

Alternatively, you can use the CodeWarrior Resource Editor application to work with resources. This application allows you to manipulate resources visually.

1. Open the project window.

If the project window is not already open, choose File > Open and open RayTracer.mcp.

2. Open the source file.

Double-click Ray.cpp in the Sources group of the project window. An edit window appears displaying the contents of the file.

3. Include the header file defining the resource identifiers.

After the line


  #include <afxwin.h>

at the beginning of the source code, add the line


  #include "resource.h"

4. Modify the code to create its window from a resource.

In the definition of the constructor function CTracerWindow, replace the statement


  Create(NULL, _T("Ray the Tracer"),    WS_OVERLAPPEDWINDOW, rectDefault);

with


  // Create the window with attributes from ray.rc    LoadFrame(IDR_MAINFRAME);

In Win32 resource files, each resource has an integer or string identifier. The LoadFrame function (inherited from base class CWndFrame) looks for resources with a specified identifier, then creates a window based on the information in those resources. In this case, it will find the window's menu, a string for the window's title, and an icon, all of which have the identifier IDR_MAINFRAME.

But where do these resources reside? In a resource file, of course. In this case, the file is named Ray.rc. To make the resources available, we must add the Ray.rc file to the project.

5. Add the resource file to the project.

Click Sources in the project window, then choose Project > Add Files. The Select files to add dialog appears, as shown in Figure 3.3. Navigate to the RayTracer folder, select Ray.rc, and click Add. The Add Files dialog appears, as shown in Figure 3.4. Add the file to all targets by enabling all options in the Targets area, then click OK.

Let's take a moment to look at the contents of the resource file. Open the resource file in an edit window by double-clicking its name, Ray.rc, in the project window. The file contents begins with the following line:


  #include "resource.h"

This includes the same header file as Ray.cpp. Next is a line reading:


  IDR_MAINFRAME MENU DISCARDABLE

This introduces the definition of the program's main menu. The lines that follow specify the menu's contents. In this case, since this is a main menu, its items are the names of the individual menus in the window's menu bar.

Now we're ready to build the new version of the program.

6. Build the program.

As before, choose Project > Make to compile and link the modified version of the program.

7. Run the new program.

Run the program to verify that it displays the menu bar for the window. You may notice that all commands are grayed out except for the Exit command on the File menu. This is because the program does not yet contain any code to handle these commands; MFC is clever enough to recognize this and disable the commands that the program has left not implemented.


Phase 3: Handling Commands

Now we are ready to make the program more interesting by handling the commands on its menus.

1. Open the project window.

If the project window is not already open on your screen, choose File > Open and open RayTracer.mcp.

2. Replace the source file Ray1.cpp with Ray2.cpp.

Select the Sources group in the project window, then choose Project > Add Files. The Select files to add dialog appears, as shown in Figure 3.3. Navigate to the RayTracer folder, select Ray2.cpp, and click Add. The Add Files dialog appears, as shown in Figure 3.4. Add the file to all targets by enabling all options in the Targets area, then click OK. Next, select Ray1.cpp in the project window and remove it by choosing Edit > Delete.

3. Display the contents of the new source file.

Double-click Ray2.cpp in the Sources group of the project window. An edit window appears displaying the contents of the file.

Looking at the code, you can see that we have added several new member functions to class CTracerWindow, such as OnPaint() and ViewFront(). OnPaint(), an MFC member function inherited from the base class CFrameWnd, will be called whenever our window receives a paint message, indicating that the contents of the window need to be repainted on the screen. The other new functions, such as ViewFront(), handle menu commands that the user chooses from View menu

Following the function declarations is the line


  DECLARE_MESSAGE_MAP()

This is an MFC macro that creates the message map for the window. The message map tells MFC how to map incoming messages sent to the window into calls to the appropriate member functions. The actual definition of the message map is accomplished by the following series of macro calls below the CTracerWindow class declaration:


  BEGIN_MESSAGE_MAP(CTracerWindow, CFrameWnd)    ON_WM_PAINT()
   ON_COMMAND(ID_VIEW_FRONT), ViewFront)
   ON_COMMAND(ID_VIEW_LEFT), ViewLeft)
   ON_COMMAND(ID_VIEW_RIGHT), ViewRight)
   ON_COMMAND(ID_VIEW_BACK), ViewBack)
  END_MESSAGE_MAP()

The ON_WM_PAINT macro specifies that our member function OnPaint() is to be called whenever the window receives a paint message. The next four lines identify the member functions that handle each of the four commands on the View menu. The identifiers defined in the resource header file Ray.rc, such as ID_VIEW_FRONT, are used to connect each menu command with the corresponding member function via the ON_COMMAND macro.

Right after the message map are the lines


  CDC Picture;   CBitmap Bitmap;

These are declarations for two MFC objects: a display context and a bitmap. Together, they allow us to draw a picture and display it in our window. How this actually works is beyond the scope of this tutorial, but we can still have a peek at the code.

Down a little further is the function definition for the member function OnPaint() of class CTracerWindow. This is the handler function for paint messages, the part of the program that actually puts something on the screen. This function calls GetClientRect() to get the dimensions of the window's client area (the area the program can draw into), then copies the bitmap (which will eventually contain our ray-traced image) into the window, using the function BitBlt().


NOTE

The functions GetClientRect() and BitBlt() belong to the Win32 application program interface (API). Here, they are used as MFC member functions. MFC member functions generally map directly into the corresponding API calls. If MFC were not being used, the API functions would be called directly.


The remaining member functions we've added to CTracerWindow (ViewRight(), ViewLeft(), ViewFront(), and ViewBack()) will eventually implement the actual ray-tracing procedures. For now, they're simply left empty.

4. Build the program.

Use Project > Make to compile and link this latest version of the program.

5. Run the program.

When you run the program, a black box 200 pixels wide by 200 high appears in the middle of the window. This is the (currently empty) bitmap that the BitBlt() function in our OnPaint() function copied into the window. Notice also that the From Front, From Left, From Right, and From Back commands on the View menu are enabled, since there are now defined functions to handle them (even though all of these functions are currently empty).


Phase 4: What, Pray Tell, Is a DLL?

The (admittedly meager) user interface of our ray-tracing application is now complete. All that is missing is code to do the actual ray tracing. Unless you have a Ph.D. in math (or can at least remember Algebra 2), writing this code yourself will be rather difficult. Fortunately, you have a friend (coincidentally named Ray) who offers you a ready-made, generic ray-tracing engine written in C++.

"The beauty of it," says Ray, "is that this engine is totally system-independent. The truth is, it knows nothing about the hardware or operating system it's running on."

We get suspicious when programmers talk about beauty and truth. But in this case, Ray does not exaggerate. The tracer engine defines a class RayTracer with a single abstract virtual member function named SetPixel(). All you have to do to use the engine is derive a class from RayTracer and define its SetPixel() function. Sounds easy enough.

You think that you might want to transport this code some day, such as when Windows 2005 comes along, so you decide to keep the tracer engine as independent as possible of your user-interface code.

The solution is to place the code in a dynamic link library (DLL). A DLL contains code that does not stand on its own as an application, but instead is used by other applications. It can be kept completely separate, residing in a different file from the applications that use it.

Creating a DLL is very similar to creating an application, so we will not elaborate much on the individual steps except where they differ from the ones we looked at earlier. Here's how to create a DLL from the ray-tracing engine:

1. Open a new project.

Choose File > New.

The New dialog appears, as shown in Figure 3.1.

2. Select the project category.

Select Win32 DLL Stationary from the list in the Project tab. Type TracerDLL.mcp in the Project name field. By convention, a project name ends with .mcp. You may also use the Location text box or Set button to choose a location on your hard disk to store the project. Then click OK.

3. Choose stationery.

Select Win32 C++ DLL from the Project Stationary area of the New Project window, as shown in Figure 3.2. Then, click OK.

4. Add the DLL's source file to the project.

When the IDE finishes opening your project, a window bearing the project name you typed in the Project name text box of the New dialog appears. Click Sources in the project window, then choose Projet > Add Files. The Select files to add dialog appears, as shown in Figure 3.3. Navigate to the RayTracer folder, select tracer.cpp, and click Add. The Add Files dialog appears, as shown in Figure 3.4. Add the file to all targets by enabling all options in the Targets area, then click OK.

5. Remove the source file placeholder from your project.

Click the file x86_Win32DLL.cp in the Sources group of the project window and choose Edit > Delete. Your project window should now look like Figure 3.7.

Tracer.mcp project window:

6. Display the Target Settings dialog.

Choose Edit > Target Settings, where Target is the name of the project's currently selected target. Since we have selected a C++ DLL target, the command appears as C++ DLL Debug Settings. The Target Settings dialog appears.


TIP

You can also display the Target Settings dialog by clicking Target Settings, which is the left-most button in the project window.


7. Select the x86 Target panel.

Click x86 Target in the Target Settings Panels area of the Target Settings dialog. The x86 Target setting panel appears, as shown in Figure 3.6.

8. Specify the name and type of output file.

Make sure Dynamic Link Library (DLL) is selected in the Output File Type list box, and type Tracer.dll into the File Name text box. Then, click OK to confirm your settings and close the dialog.

9. Build the DLL.

Choose Project > Make to compile and link the DLL. This command creates two files:

The DLL communicates with its client programs by exporting some of its functions and variables with the declaration modifier __declspec(dllexport). This modifier specifies that the declaration that follows is to be made available to the DLL's client programs. To see an example, double-click the source file tracer.cpp in the project window to open the file in an edit window. Choose Search > Find to search for the string __declspec. (Note the double underscore at the beginning of the name.) You will find the lines


  #ifdef __INTEL__   __declspec(dllexport)
  #endif
  void RayTracer::Trace(Object *ObjectList,
   LightSource *LightList, int w, int h,
   Vector ViewPos)

This says that if the code is being compiled for an Intel processor, the function Trace is to be exported as part of the DLL's client interface. Choose Search > Find Next and you will find several more exported names.


Phase 5: All Together Now

Now that we actually have some ray-tracing code (in a DLL) and a user interface (in an application), the only thing left to do is link them together. A header file named Tracer.h came with your friend Ray's tracing engine. You can add this file to your Ray.mcp project to define the interface from your application to the ray tracing engine. After that, it is only a matter of adding a little "glue" code to hold it all together.

1. Open the project window.

If the project window is not already open, choose File > Open and open RayTracer.mcp.

2. Add the interface library file to the project.

Click Sources in the project window, then choose Project > Add Files. The Select files to add dialog appears, as shown in Figure 3.3. Navigate to the TracerDLL folder, select Tracer.dll and Tracer.lib, then click Add. The Add Files dialog appears, as shown in Figure 3.4. Add the file to all targets by enabling all options in the Targets area, then click OK.

3. Add the appropriate source file to the project.

Select Sources in the project window, then choose Projet > Add Files. The Select files to add dialog appears, as shown in Figure 3.3. Navigate to the RayTracer folder, select Ray.cpp, and click Add. The Add Files dialog appears, as shown in Figure 3.4. Add the file to all targets by enabling all options in the Targets area, then click OK.

4. Remove the old source file from your project.

Select ray2.cpp in the Sources group of the project window and choose Edit > Delete.

5. View the source code.

Double-click Ray.cpp in the project window to open an edit window and view the code. The first new section is shown in Listing 3.1.

Object definitions for the r ay-tracing program:


// Some serious balls
Sphere Object1(0.0, 0.0, 0.0, 5.0, 1.0, 0.5, 0.1, 0.5);
Sphere Object2(4.0, 2.0, -4.0, 2.0, 0.3, 1.0, 1.0, 0.5);
Sphere Object3(-4.0, 2.0, -4.0, 2.0, 0.3, 1.0, 1.0, 0.5);
// And a floor
BoundedPlane Object4(0.0, 1.0, 0.0, 6.0, -100.0, 100.0,
  -10000, 10000, -1000.0, 1000.0, 0.5, 0.5, 0.7, 0.2);

// And a ceiling
BoundedPlane Object5(0.0, 1.0, 0.0, -12.0, -100.0, 100.0,
  -10000, 10000, -1000.0, 1000.0, 0.7, 0.5, 0.5, 0.0);

// And a couple of lights
LightSource Light1(10.0, 10.0, -10.0, 50000.0, 50000.0, 50000.0);
LightSource Light2(-5.0, 4.0, 10.0, 30000.0, 30000.0, 30000.0);

This code defines objects in the virtual world that we are going to ray-trace. Each line is a constructor for a class exported from the Tracer.dll library and defined in the header file Tracer.h. You'll see the results when you run this final version of the program.

Next, you will see a definition for a class named MyTracer, which is derived from class RayTracer. The only member it defines is a function named SetPixel(). This function is called once for each pixel in an image rendered by the ray tracer, to set the pixel to the proper color in the destination bitmap. The destination could be the screen, a file, or a printer; it makes no difference to the ray-tracing engine.

ViewLeft function:


void CTracerWindow::ViewLeft()
{
  Vector View;
  
  // Set up view position to the left of the scene
  View[X] = -15.0;
  View[Y] = 0.0;
  View[Z] = 0.0;
  Ray.Trace(&Object1, &Light1,
       PICTURE_WIDTH, PICTURE_HEIGHT, View);
  InvalidateRect(NULL);
}

Somewhat further down in the source file, you will find the definition of function ViewLeft(), as shown in Listing 3.2. This function is called when a user chooses View > From Left; a similar function is provided for each of the other commands on the menu. Each of these commands views the same set of objects from a different angle; the corresponding functions set up a vector to define the viewing position, then call the ray-tracing engine's Trace function to generate the image as viewed from that point. The Trace function will call back to our SetPixel() function to set each pixel in the image to the appropriate color. Finally, we call the Win32 function InvalidateRect() to tell the system that part of our window has changed and must be repainted. This results eventually in a call to our OnPaint() function to display the newly generated bitmap.

Now we are finally ready to build the full application.

6. Build the program.

Choose Project > Make. The CodeWarrior IDE will then compile and link the ray-tracer program.

7. Run the program.

Choose Project > Run. Ray the Tracer displays the same window, menu, and black box as before. But this time, when you select one of the commands from the View menu, the program displays a ray-traced image of something that resembles a weirdly colored water molecule. (It may take some time for the program to compute the image; be patient.)

If you would like to experiment further with the code, read the comments in the Tracer.cpp source code to learn how the object constructors work. You can then try moving objects around, changing their colors, and so forth, to produce an infinite variety of images. If you are a bit more ambitious, you can try adding object classes of your own, such as a cylinder, cone, or pyramid.

 


[ First ]  [ Previous ]  [ Next ]  [ Last ]  [ Manuals ]

Visit the Metrowerks website at: http://www.metrowerks.com
For assistance contact Metrowerks Technical Support at: cw_support@metrowerks.com
Copyright © 2000, Metrowerks Corp. All rights reserved.

Last updated: July 21, 2000