0%

MFC显示OSGB模型

  1. 概览
  2. OSG读取OSGB模型并显示
  3. MFC视图显示OSGB模型

概览

OSG提供了一个浏览三维模型的视图osgViewer::Viewer,将.osg或.osgb文件通过osgDB::readNodeFile()读进来后用osgViewer::Viewer::setSceneData()添加到视图中,接着调用osgViewer::Viewer::run()即可开一个新的窗口显示。

MFC提供了C++软件程序框架,可以显示单个或多个窗口、对话框,并提供了文件、路径选择的接口,可以用来开发基础的OSGB浏览软件。将MFC视图的窗口句柄传递给osgViewer,并设置一些运行过程的控制,即可实现在MFC中使用OSG自带的视图显示OSGB模型。

OSG读取OSGB模型并显示

以下代码实现的功能:拖动一个.osgb文件到exe文件上,即可打开一个全屏的视图显示OSGB模型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<osgViewer/Viewer>
#include<osgDB/ReadFile>

int main(int argc, char* argv[])
{
if (argc != 2) return 1;
const std::string indexFilePath = argv[1];
std::cout << indexFilePath << std::endl;
const std::string dirPath = indexFilePath.substr(0, indexFilePath.find_last_of('\\'));

auto opts = new osgDB::Options();
opts->setDatabasePath(dirPath);
const osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(indexFilePath, opts);

// 关闭光照使均匀
node->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);

// 修改子节点路径为绝对
osg::Group* group = dynamic_cast<osg::Group*>(node.get());
for (size_t i = 0; i < group->getNumChildren(); i++)
{
osg::PagedLOD* plod = dynamic_cast<osg::PagedLOD*>(group->getChild(i));
plod->setDatabasePath(dirPath + plod->getDatabasePath());
std::cout << dirPath + plod->getDatabasePath() << std::endl;
}

// 创建视窗显示
osgViewer::Viewer viewer;
viewer.setSceneData(node);
viewer.setUpViewOnSingleScreen(1);
return viewer.run();
}

MFC视图显示OSGB模型

首先新建一个类:

COsgbViewer.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#pragma once
#include <string>
#include <osgViewer/Viewer>

class COsgbViewer
{
public:
COsgbViewer(HWND hWnd);
~COsgbViewer();

void InitOsgb(std::string filepath);

osg::ref_ptr<osgViewer::Viewer> GetViewer() const;

static void Render(void*);

private:
HWND m_hWnd;
osg::ref_ptr<osgViewer::Viewer> m_lpViewer;
};

COsgbViewer.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include "COsgbViewer.h"
#include <osgViewer/api/Win32/GraphicsWindowWin32>
#include <osgDB/ReadFile>
#include <osgGA/TrackballManipulator>

COsgbViewer::COsgbViewer(HWND hWnd)
{
this->m_hWnd = hWnd;
}

COsgbViewer::~COsgbViewer()
{
}

void COsgbViewer::InitOsgb(std::string filepath)
{
std::replace(filepath.begin(), filepath.end(), '/', '\\');
const std::string dirPath = filepath.substr(0, filepath.find_last_of('\\'));
auto opts = new osgDB::Options();
opts->setDatabasePath(dirPath);
const osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(filepath, opts);

if (node == nullptr)
return;

// 关闭光照使均匀
node->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);

// 修改子节点路径为绝对
osg::Group* group = dynamic_cast<osg::Group*>(node.get());
for (size_t i = 0; i < group->getNumChildren(); i++)
{
osg::PagedLOD* plod = dynamic_cast<osg::PagedLOD*>(group->getChild(i));
plod->setDatabasePath(dirPath + plod->getDatabasePath());
}

// 构建视窗
RECT m_rect;
::GetWindowRect(m_hWnd, &m_rect);

// 通过traits传递窗口句柄
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits();
osg::ref_ptr<osg::Referenced> windata = new osgViewer::GraphicsWindowWin32::WindowData(m_hWnd);

traits->x = 0;
traits->y = 0;
traits->width = m_rect.right - m_rect.left;
traits->height = m_rect.bottom - m_rect.top;
traits->windowDecoration = true;
traits->doubleBuffer = true;
traits->overrideRedirect = false;
traits->sharedContext = 0;
traits->setInheritedWindowPixelFormat = true;
traits->inheritedWindowData = windata;

// 创建视窗osgViewer,并修改其句柄为MFC窗口
m_lpViewer = new osgViewer::Viewer;
osg::ref_ptr<osg::Camera> camera = m_lpViewer->getCamera();
camera->setGraphicsContext(osg::GraphicsContext::createGraphicsContext(traits));
camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
m_lpViewer->setCamera(camera);

// 添加相机交互
m_lpViewer->setCameraManipulator(new osgGA::TrackballManipulator);

// 设置三维数据并显示
m_lpViewer->setSceneData(node);
m_lpViewer->realize();
m_lpViewer->getCamera()->setComputeNearFarMode(osg::CullSettings::COMPUTE_NEAR_FAR_USING_PRIMITIVES);
}

osg::ref_ptr<osgViewer::Viewer> COsgbViewer::GetViewer() const
{
return this->m_lpViewer;
}

void COsgbViewer::Render(void* ptr)
{
const auto self = (COsgbViewer*)ptr;
const auto viewer = self->GetViewer();
while (!viewer->done())
{
viewer->frame();
}
_endthread();
}

接着在MFC的view类(继承自CView,命名为:CBigViewerView)中添加成员变量:

1
2
private:
COsgbViewer* m_lpOsgbViewer;

接着重载虚函数OnInitialUpdate和三个消息响应函数:

OnInitialUpdate:加载模型文件后开一个线程处理渲染过程,在这里提供文件路径

1
2
3
4
5
6
7
8
void CBigViewerView::OnInitialUpdate()
{
CView::OnInitialUpdate();

// TODO: 在此添加专用代码和/或调用基类
m_lpOsgbViewer->InitOsgb("D:\\codes\\C++\\reconstruct-engine\\DJI_202207281718_006_1area04125cmc_1-2\\model-osgb\\Model.osgb");
_beginthread(&COsgbViewer::Render, 0, m_lpOsgbViewer);
}

OnCreate:视图类创建时先进行初始化,将窗口句柄给到自定义的viewer类中

1
2
3
4
5
6
7
8
9
10
int CBigViewerView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;

// TODO: 在此添加您专用的创建代码
m_lpOsgbViewer = new COsgbViewer(m_hWnd);

return 0;
}

OnEraseBkgnd:好像没啥用?无论是单文档还是多文档都无法触发

1
2
3
4
5
6
7
BOOL CBigViewerView::OnEraseBkgnd(CDC* pDC)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (m_lpOsgbViewer) return FALSE;

return CView::OnEraseBkgnd(pDC);
}

OnDestroy:退出时注销OSG视图类

1
2
3
4
5
6
7
8
9
10
void CBigViewerView::OnDestroy()
{
CView::OnDestroy();

// TODO: 在此处添加消息处理程序代码
if (m_lpOsgbViewer)
{
delete m_lpOsgbViewer;
}
}