需要實現:新建一個窗口Window1.xaml,然後在窗口中顯示osg場景。
這裏比較困難的地方是wpf中使用的是c#,是一種託管代碼;而osg的代碼是使用c++編寫,是一種非託管代碼。如何將二者結合起來使用就是最關鍵的步驟。
看網上說的可以使用C++/CLI可以實現c#和c++的混合編程,但由於一開始我就是寫的wpf程序,不想再新建工程,於是想到可以將c++的osg代碼寫成dll文件,然後在wpf程序中引用便可。
WPF中除主窗口外再新建一個窗口Window1.xaml;再在MainWindow頁面上隨便畫一個按鈕
代碼如下:
public partial class MainWindow : Window
{
//注意把ConsoleApplication1.dll文件放到wpf的debug文件夾下
[DllImport(@"ConsoleApplication1.dll",
EntryPoint = "osgRunCow", CallingConvention = CallingConvention.Cdecl)]
public static extern void osgRunCow(IntPtr m_hWnd);
IntPtr handle;//c#中用IntPtr來保存窗口句柄,對應c++中的HWND
Thread th;
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Window win = new Window1();//Window1是新建的Window
win.Show();
handle = new WindowInteropHelper(win).Handle;//獲取窗口句柄,提供給osgRunCow使用
th = new Thread(new ThreadStart(func));//多開一個線程防止界面假死
th.Start();
}
public void func()
{
osgRunCow(handle);
}
}
dll文件中新建一個module-definition file文件Source.def(add new item->visual c++ ->code->module definition file)
其代碼如下(注意沒有任何分號):
LIBRARY ConsoleApplication1.dll
EXPORTS
osgRunCow
主文件ConsoleApplication1.cpp的代碼如下:
// ConsoleApplication1.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include <osg/Group>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osgViewer/api/win32/GraphicsWindowWin32>
#include <osgDB/ReadFile>
#include <osgGA/OrbitManipulator>
#include <osgGA/StateSetManipulator>
#include <osgGA/CameraManipulator>
#include <osgGA/KeySwitchMatrixManipulator>
#include <osgGA/GUIEventHandler>
#include <osgUtil/Optimizer>
osg::ref_ptr m_Viewer;
osg::ref_ptr m_Root; // 場景根節點
void initSG()
{
// 創建場景分類樹樹
m_Root = new osg::Group;
m_Root->addChild(osgDB::readNodeFile("cow.osg"));
// 優化場景數據
//osgUtil::Optimizer optimizer;
//optimizer.optimize(m_Root);
}
void initCameraCfg(HWND m_hWnd)
{
// 本地窗口Size
RECT rect;
// 爲窗口創建Viewer
m_Viewer = new osgViewer::Viewer();
// 取消Esc鍵的事件響應
m_Viewer->setKeyEventSetsDone(0);
// 獲得當前窗口大小
::GetWindowRect(m_hWnd, &rect);
// 初始化圖形環境GraphicsContext Traits
osg::ref_ptr traits = new osg::GraphicsContext::Traits;
// Init the Windata Variable that holds the handle for the Window to display OSG in.
osg::ref_ptr windata = new osgViewer::GraphicsWindowWin32::WindowData(m_hWnd);
// 設置traits 參數
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;
// 創建圖形環境Graphics Context
osg::ref_ptr gc = osg::GraphicsContext::createGraphicsContext(traits.get());
{
// 根據分辨率確定合適的投影來保證顯示的圖形不變形
double fovy =0.0;
double aspectRatio =0.0;
double zNear =0.0;
double zFar= 0.0;
m_Viewer->getCamera()->getProjectionMatrixAsPerspective(fovy,aspectRatio,zNear,zFar);
double newAspectRatio =double(traits->width)/double(traits->height);
double aspectRatioChange = newAspectRatio/aspectRatio;
if (aspectRatioChange != 1.0)
{
m_Viewer->getCamera()->getProjectionMatrix() *= osg::Matrix::scale(1.0/aspectRatioChange,1.0,1.0);
}
}
// 設置相機視口
m_Viewer->getCamera()->setViewport(traits->x,traits->y,traits->width,traits->height);
// 爲相機分配圖形環境
m_Viewer->getCamera()->setGraphicsContext(gc.get());
// 爲Viewer的Camera添加操縱器
m_Viewer->setCameraManipulator(new osgGA::OrbitManipulator());
// 設置線程模型
m_Viewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
// 設置場景數據
m_Viewer->setSceneData(m_Root);
// 初始化並創建窗口
m_Viewer->realize();
}
void osgStartRender()
{
m_Viewer->run();
}
void osgRunCow(HWND m_hWnd)
{
initSG();
initCameraCfg(m_hWnd);
osgStartRender();
}
注意:對於託管和非託管我的理解並不深刻,所以使用的時候可能會出現線程安全性問題,但起碼我用的時候沒出什麼問題。
參考工程:ssDlgFrm(網上搜應該可以查到)
有關如何新建dll工程並在wpf中調用可以參照:http://blog.csdn.net/jarvischu/article/details/6634185
感謝每一個願意分享的人。