Using Coin with MFC and VisualC++
This tutorial will show you how to create a Windows application based on Coin, using VisualC++ and the Microsoft Foundation Classes (MFC) class library.
Creating the project
- Start VisualC++. This tutorial is based on VisualC++ v6.0. No service pack is required, but it is recommended that you use the latest service pack from Microsoft.
- Create a new project. File | New | Projects | MFC AppWizard (exe). This
tutorial assumes the project name is "MFCViewer". Use the default
parameters for the wizard, but note the following
- Make sure you create a MDI application (Multiple Documents) with Document/View Architecture support.
- Please refer to Tutorial - Part 1 - Microsoft Visual Studio Project Settings on how to integrate the Coin3D libraries with your project.
Adding code for SoWin and Coin
- Open the file MFCViewer.cpp.
- Add this line to the list of #includes:
#include <Inventor/Win/SoWin.h>
- Edit the function CMFCViewerApp::InitInstance() so that the first line
is
SoWin::init("");This will init the SoWin library and the Coin library, and must be called before any use of Coin or SoWin.
- Add this line to the list of #includes:
- Open the file MFCViewerView.h.
- Add the following lines to the list of #includes (below the
precompiled headers - #ifdef/#pragma once/#endif sequence):
#include <Inventor/Win/SoWin.h> #include <Inventor/Win/viewers/SoWinExaminerViewer.h>
- Add the public data member
SoWinExaminerViewer * viewer;
The ExaminerViewer givces us a place to render our scenegraph, as well as interacting with whatever is displayed (like rotating it, selecting parts of the scenegraph, etc)
- Add the following lines to the list of #includes (below the
precompiled headers - #ifdef/#pragma once/#endif sequence):
- Open the file MFCViewerView.cpp.
- Modify the constructor so that it looks like this:
CMFCViewerView::CMFCViewerView() { viewer = NULL; } - Modify the destructor so that it looks like this:
CMFCViewerView::CMFCViewerView() { if (viewer != NULL) delete viewer; } - Modify the method CMFCViewerView::OnDraw(CDC* pDC) so that it looks
like this
void CMFCViewerView::OnDraw(CDC* pDC) { CMFCViewerDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (viewer == NULL) { viewer = new SoWinExaminerViewer( m_hWnd ); viewer->setDecoration(FALSE); WINDOWPLACEMENT p; memset(&p, 0, sizeof(p)); p.length = sizeof(WINDOWPLACEMENT); p.showCmd = SW_SHOWMAXIMIZED; SetWindowPlacement(&p); } };This will embed the ExaminerViewer in the CMFCViewerView window. Decoration is turned off.
If you wondered what that last sentence meant, try doing viewer->setDecoration(TRUE); instead. You can also turn decoration on and off by right-clicking in the ExaminerViewer window while the application is running.
- Just to get something to look at (make sure you undo these changes
before the next step) :
- Add the following lines to the list of #includes:
#include <Inventor/nodes/SoSeparator.h> // remove me later #include <Inventor/nodes/SoCone.h> // remove me later
- Modify the method CMFCViewerView::OnDraw(CDC* pDC) so that it
looks like this
void CMFCViewerView::OnDraw(CDC* pDC) { CMFCViewerDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (viewer == NULL) { viewer = new SoWinExaminerViewer( m_hWnd ); viewer->setDecoration(FALSE); SoSeparator *root = new SoSeparator; // remove me later root->addChild(new SoCone); // remove me later viewer->setSceneGraph(root); // remove me later } }
- Add the following lines to the list of #includes:
- Modify the constructor so that it looks like this:
- Build the project and run. You should see a standard MFC MDI application
with one window containing a rather dull looking cone.
- Take the cone for a spin (hint: try the mouse)
- Remember to delete the lines marked "// remove me later" before
moving on to the next step of this tutorial
Creating a scenegraph
In this section we'll create a default scenegraph used when the users creates a new document (File | New). This is included just to let you get a feel for how to create a scenegraph using Coin.
- Open the file MFCViewerDoc.h
- Add the following line somewhere above the CMFCViewerDoc class
definition:
class SoSeparator;
This informs the compiler that we're going to use a class called SoSeparator.
- Add a public data member to the CMFCViewerDoc class:
SoSeparator *root;
This is the topmost node in our scenegraph.
- Add the following line somewhere above the CMFCViewerDoc class
definition:
- Open the file MFCViewerDoc.cpp
- Add the following lines to the list of #includes:
#include <Inventor/nodes/SoSeparator.h> #include <Inventor/nodes/SoMaterial.h> #include <Inventor/nodes/SoCone.h> #include <Inventor/nodes/SoTranslation.h> #include <Inventor/nodes/SoText2.h>
These are the nodes we are going to use in our scenegraph, and we have to include the headerfile for each node.
- Modify the method OnNewDocument() so that it looks like this:
BOOL CMFCViewerDoc::OnNewDocument() { if (!CDocument::OnNewDocument()) return FALSE; root = new SoSeparator; root->ref(); SoMaterial *myMaterial; root->addChild(myMaterial = new SoMaterial); myMaterial->diffuseColor.setValue(1.0, 0.0, 0.0); root->addChild(new SoCone); SoSeparator * instructsep = new SoSeparator; root->addChild(instructsep); instructsep->addChild(myMaterial = new SoMaterial); myMaterial->diffuseColor.setValue(0.5, 1.0, 1.0); SoTranslation * instructtrans = new SoTranslation; instructtrans->translation = SbVec3f(-2.0f, 1.3f, 2.0f); instructsep->addChild(instructtrans); SoText2 * instructions = new SoText2; const char * str[] = { "Instructions for the MFCViewer tutorial", "", "Left mouse button = rotate", "Middle mouse button = move", "CTRL + middle mouse button = zoom", "Right mouse button = options" }; instructions->string.setValues(0, sizeof(str) / sizeof(char *), str); instructions->justification = SoText2::LEFT; instructsep->addChild(instructions); return TRUE; }The scenegraph is built by adding children to the root node. Remember that Coin takes care of deleting all scenegraph objects, so there's no need for the application programmer to remember any pointers to these objects and delete them afterwards (in fact, it would be an error to do so). This is done by a technique known as reference counting, and the call to root->ref(); above makes sure Coin does not try to delete the root node or any of it's children.
- Modify the constructor and destructor so that they look like this:
CMFCViewerDoc::CMFCViewerDoc() { root = NULL; } CMFCViewerDoc::~CMFCViewerDoc() { if (root) root->unref(); }The call to root->unref(); tells Coin that we're not using the root node anymore, so it's ok for us if Coin wants to delete it.
- Add the following lines to the list of #includes:
- Open the file MFCViewerView.cpp
- Add the following line to the list of #includes:
#include <Inventor/nodes/SoSeparator.h>
- Modify the method CMFCViewerView::OnDraw(CDC* pDC) so that it looks
like this
void CMFCViewerView::OnDraw(CDC* pDC) { CMFCViewerDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (viewer == NULL) { viewer = new SoWinExaminerViewer( m_hWnd ); viewer->setDecoration(FALSE); SoSeparator *root = GetDocument()->root; viewer->setSceneGraph(root); } }We've now modified the OnDraw methot so that it gets the root of the scenegraph from the document object.
- Add the following line to the list of #includes:
Compile and run the project. The application should start with one window open, which shows the scenegraph we've just created.