OSG + MFC单文档 实现第一个程序
前言:本文参考OSG 例子编写,建立在读者已经学会配置OSG环境基础上。
1.首先创建一个MFC单文档程序,步骤省略部分为默认;
2.文件拓展名加入(*.ive),读者如果不需要加载此类型模型,可以不写;
3.暂时不需要ActiveX控件,取消;点击完成;
4.新建一个C++类,内容和OSG例子中的类相同,例如类名:CRenderOSG;
其 RenderOSG.h 文件内容:
#pragma once
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osgViewer/api/win32/GraphicsWindowWin32>
#include <osgGA/TrackballManipulator>
#include <osgGA/KeySwitchMatrixManipulator>
#include <osgDB/DatabasePager>
#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgUtil/Optimizer>
#include <string>
class cOSG
{
public:
cOSG(HWND hWnd);
~cOSG();
void InitOSG(std::string filename); //初始化OSG,传入模型文件名
void InitManipulators(void); //初始化操作器
void InitSceneGraph(void); //初始化场景
void InitCameraConfig(void); //相机配置
void SetupWindow(void); //建立窗口
void SetupCamera(void); //建立相机
void PreFrameUpdate(void); //绘制前处理
void PostFrameUpdate(void); //绘制后处理
void Done(bool value) { mDone = value; }
bool Done(void) { return mDone; }
static void Render(void* ptr); //渲染
osgViewer::Viewer* getViewer() { return mViewer; }
private:
bool mDone;
std::string m_ModelName; //模型名
HWND m_hWnd; //窗口句柄
osgViewer::Viewer* mViewer;
osg::ref_ptr<osg::Group> mRoot; //根结点
osg::ref_ptr<osg::Node> mModel; //模型文件
osg::ref_ptr<osgGA::TrackballManipulator> trackball; //操作器
osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator; //加入keyswitch选择操作器
};
class CRenderingThread : public OpenThreads::Thread
{
public:
CRenderingThread( cOSG* ptr );
virtual ~CRenderingThread();
virtual void run();
protected:
cOSG* _ptr;
bool _done;
};
RenderOSG.cpp 文件内容如下:
// MFC_OSG.cpp : implementation of the cOSG class
//
#include "stdafx.h"
#include "RenderOSG.h"
cOSG::cOSG(HWND hWnd) :
m_hWnd(hWnd)
{
}
cOSG::~cOSG()
{
mViewer->setDone(true);
Sleep(1000);
mViewer->stopThreading();
delete mViewer;
}
void cOSG::InitOSG(std::string modelname)
{
// Store the name of the model to load
m_ModelName = modelname;
// Init different parts of OSG
InitManipulators();
InitSceneGraph();
InitCameraConfig();
}
void cOSG::InitManipulators(void)
{
// Create a trackball manipulator
trackball = new osgGA::TrackballManipulator();
// Create a Manipulator Switcher
keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;
// Add our trackball manipulator to the switcher
keyswitchManipulator->addMatrixManipulator( '1', "Trackball", trackball.get());
// Init the switcher to the first manipulator (in this case the only manipulator)
keyswitchManipulator->selectMatrixManipulator(0); // Zero based index Value
}
void cOSG::InitSceneGraph(void)
{
// Init the main Root Node/Group
mRoot = new osg::Group;
// Load the Model from the model name
mModel = osgDB::readNodeFile(m_ModelName);
if (!mModel) return;
// Optimize the model
osgUtil::Optimizer optimizer;
optimizer.optimize(mModel.get());
optimizer.reset();
// Add the model to the scene
mRoot->addChild(mModel.get());
}
void cOSG::InitCameraConfig(void)
{
// Local Variable to hold window size data
RECT rect;
// Create the viewer for this window
mViewer = new osgViewer::Viewer();
// Add a Stats Handler to the viewer
mViewer->addEventHandler(new osgViewer::StatsHandler);
// Get the current window size
::GetWindowRect(m_hWnd, &rect);
// Init the GraphicsContext Traits
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
// Init the Windata Variable that holds the handle for the Window to display OSG in.
osg::ref_ptr<osg::Referenced> windata = new osgViewer::GraphicsWindowWin32::WindowData(m_hWnd);
// Setup the traits parameters
traits->x = 0;
traits->y = 0;
traits->width = rect.right - rect.left;
traits->height = rect.bottom - rect.top;
traits->windowDecoration = false;
traits->doubleBuffer = true;
traits->sharedContext = 0;
traits->setInheritedWindowPixelFormat = true;
traits->inheritedWindowData = windata;
// Create the Graphics Context
osg::GraphicsContext* gc = osg::GraphicsContext::createGraphicsContext(traits.get());
// Init Master Camera for this View
osg::ref_ptr<osg::Camera> camera = mViewer->getCamera();
// Assign Graphics Context to the Camera
camera->setGraphicsContext(gc);
// Set the viewport for the Camera
camera->setViewport(new osg::Viewport(traits->x, traits->y, traits->width, traits->height));
// Set projection matrix and camera attribtues
camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
camera->setClearColor(osg::Vec4f(0.2f, 0.2f, 0.4f, 1.0f));
camera->setProjectionMatrixAsPerspective(
30.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 1.0, 1000.0);
// Add the Camera to the Viewer
//mViewer->addSlave(camera.get());
mViewer->setCamera(camera.get());
// Add the Camera Manipulator to the Viewer
mViewer->setCameraManipulator(keyswitchManipulator.get());
// Set the Scene Data
mViewer->setSceneData(mRoot.get());
// Realize the Viewer
mViewer->realize();
// Correct aspect ratio
/*double fovy,aspectRatio,z1,z2;
mViewer->getCamera()->getProjectionMatrixAsPerspective(fovy,aspectRatio,z1,z2);
aspectRatio=double(traits->width)/double(traits->height);
mViewer->getCamera()->setProjectionMatrixAsPerspective(fovy,aspectRatio,z1,z2);*/
}
void cOSG::PreFrameUpdate()
{
// Due any preframe updates in this routine
}
void cOSG::PostFrameUpdate()
{
// Due any postframe updates in this routine
}
void cOSG::Render(void* ptr)
{
cOSG* osg = (cOSG*)ptr;
osgViewer::Viewer* viewer = osg->getViewer();
// You have two options for the main viewer loop
// viewer->run() or
// while(!viewer->done()) { viewer->frame(); }
//viewer->run();
while(!viewer->done())
{
osg->PreFrameUpdate();
viewer->frame();
osg->PostFrameUpdate();
//Sleep(10); // Use this command if you need to allow other processes to have cpu time
}
// For some reason this has to be here to avoid issue:
// if you have multiple OSG windows up
// and you exit one then all stop rendering
AfxMessageBox(_T("Exit Rendering Thread"));
_endthread();
}
CRenderingThread::CRenderingThread( cOSG* ptr )
: OpenThreads::Thread(), _ptr(ptr), _done(false)
{
}
CRenderingThread::~CRenderingThread()
{
_done = true;
while( isRunning() )
OpenThreads::Thread::YieldCurrentThread();
}
void CRenderingThread::run()
{
if ( !_ptr )
{
_done = true;
return;
}
osgViewer::Viewer* viewer = _ptr->getViewer();
do
{
_ptr->PreFrameUpdate();
viewer->frame();
_ptr->PostFrameUpdate();
} while ( !testCancel() && !viewer->done() && !_done );
}
5.完成上述操作后,通过类向导添加如下消息响应:
6.在XXXView.h 中,头文件别忘了,添加如下成员变量:
protected:
cOSG* mOSG;
//HANDLE mThreadHandle;
CRenderingThread* mThreadHandle;
构造函数对指针进行初始化:
CZZU_OSGView::CZZU_OSGView():mOSG(0L)
{
// TODO: 在此处添加构造代码
}
7.在OnCreate函数中,添加:
int CZZU_OSGView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// Now that the window is created setup OSG
mOSG = new cOSG(m_hWnd);
return 0;
}
8.在OnDestroy()函数中,进行 delete:
void CZZU_OSGView::OnDestroy()
{
delete mThreadHandle;
if(mOSG != 0) delete mOSG;
//WaitForSingleObject(mThreadHandle, 1000);
CView::OnDestroy();
}
9.OnEraseBkgnd()函数,返回真:
BOOL CZZU_OSGView::OnEraseBkgnd(CDC* pDC)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//return CView::OnEraseBkgnd(pDC);
return true;
}
10.为了防止按Esc退出渲染,需要在OnKeyDown ()函数中,
void CZZU_OSGView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//CView::OnKeyDown(nChar, nRepCnt, nFlags);
// 防止按Esc退出
if(nChar == VK_ESCAPE)
{
GetParent()->SendMessage(WM_CLOSE);
}
}
11.运行程序,发现什么都没有;
12.需要添加一个虚函数,实现更新;
更新函数代码如下:
void CZZU_OSGView::OnInitialUpdate()
{
CView::OnInitialUpdate();
// Init the osg class
mOSG->InitOSG("cow.osg");
// Start the thread to do OSG Rendering
//mThreadHandle = (HANDLE)_beginthread(&cOSG::Render, 0, mOSG);
mThreadHandle = new CRenderingThread(mOSG);
mThreadHandle->start();
}
13.完成上述,运行程序: