本文目录
1. 工具准备
1.1 OSG3.6.4
关于OSG的编译我之前的博客已经写的很详细,这里贴出链接。
1.2 osgQt
osgQt的编译过程也在我之前的博客中,这里接着贴链接。
2. 正文开始
2.1 需求分析
在做osgb三维模型的分割处理的时候,因为要查看处理之后的三维模型,每次双击打开osgb文件再选择osgviewer.exe实在是麻烦,而且命令行使用osgviewer来同时查看多个模型,也着实麻烦。
基于此,就想着写个程序,能同时选中多个osgb文件,并在一个窗口中显示。
因为是模型展示工具,因此只需要能显示选中的三维模型就行。
2.2 源码分析
2.2.1 Widget.h
Widget类是继承自QWidget的类,而要使用的osgQt则是导入的外部库osgQOpenGL.lib。该库是结合Qt和OSG的第三方库,其中要用到的osgQOpenGLWidget也是继承自QWidget,因此该类使用起来和常规的窗口类一样。
具体代码如下:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QFileDialog>
#include <QDir>
#include <osgQOpenGL/osgQOpenGLWidget>
#include <osgGA/TrackballManipulator>
#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osg/Texture2D>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
osg::Camera *backGround(QString sImagePath, int iWidth, int iHeight);
void resizeEvent(QResizeEvent *event) override;
protected slots:
void onChoseFileButtonClicked();
void onChoseDirButtonClicked();
void onShowModelButtonClicked();
void onClearButtonClicked();
void initOsgWindow();
private:
Ui::Widget *ui;
osgQOpenGLWidget* _pOsgWidget;
osg::ref_ptr<osgViewer::Viewer> _viewer;
QStringList _lFileNames;
};
#endif // WIDGET_H
基本思路为:将选中的osgb文件通过OSG的组节点将模型读入到内存,然后使用osgQOpenGLWidget获取该窗口组件的viewer,将存储模型数据的group组节点设置为当前viewer的场景数据。
具体实现如下。
2.2.2 Widget.cpp
#include "Widget.h"
#include "ui_Widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
_pOsgWidget = new osgQOpenGLWidget(this);
_pOsgWidget->setGeometry(220, 0, 580, 580);
// _pOsgWidget->setHidden(true);
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(onChoseFileButtonClicked()));
connect(ui->pushButton_4, SIGNAL(clicked()), this, SLOT(onChoseDirButtonClicked()));
connect(ui->pushButton_2, SIGNAL(clicked()), this, SLOT(onShowModelButtonClicked()));
connect(ui->pushButton_3, SIGNAL(clicked()), this, SLOT(onClearButtonClicked()));
connect(_pOsgWidget, SIGNAL(initialized()), this, SLOT(initOsgWindow()));
}
Widget::~Widget()
{
delete ui;
}
osg::Camera *Widget::backGround(QString sImagePath, int iWidth, int iHeight)
{
osg::ref_ptr<osg::Camera> camera = new osg::Camera;
camera->setAllowEventFocus(false);
camera->setProjectionMatrixAsOrtho2D(0, iWidth, 0, iHeight);
camera->setViewport(0, 0, iWidth, iHeight);
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
camera->setRenderOrder(osg::Camera::PRE_RENDER);
camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
camera->setViewMatrix(osg::Matrix::identity());
// camera->setRenderOrder(osg::Camera::NESTED_RENDER);
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
// geode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
geode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
osg::ref_ptr<osg::Vec3Array> vertex = new osg::Vec3Array;
vertex->push_back(osg::Vec3(0, 0, 0));
vertex->push_back(osg::Vec3(iWidth, 0, 0));
vertex->push_back(osg::Vec3(iWidth, iHeight, 0));
vertex->push_back(osg::Vec3(0, iHeight, 0));
geometry->setVertexArray(vertex);
osg::ref_ptr<osg::Vec3Array> normal = new osg::Vec3Array;
normal->push_back(osg::Vec3(0.0, 0.0, 1.0));
geometry->setNormalArray(normal);
geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
osg::ref_ptr<osg::Vec2Array> texCoord = new osg::Vec2Array;
texCoord->push_back(osg::Vec2(0.0, 0.0));
texCoord->push_back(osg::Vec2(1.0, 0.0));
texCoord->push_back(osg::Vec2(1.0, 1.0));
texCoord->push_back(osg::Vec2(0.0, 1.0));
geometry->setTexCoordArray(0, texCoord);
geometry->addPrimitiveSet(new osg::DrawArrays(osg::DrawArrays::QUADS, 0, 4));
osg::ref_ptr<osg::Image> img = osgDB::readImageFile(sImagePath.toStdString());
osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D;
tex->setImage(img);
// tex->setUnRefImageDataAfterApply(true);
geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, tex.get(), osg::StateAttribute::ON);
geode->addDrawable(geometry);
camera->addChild(geode);
return camera.release();
}
void Widget::resizeEvent(QResizeEvent *)
{
_pOsgWidget->setGeometry(220, 0, this->width() - 120, this->height() - 20);
ui->textBrowser->setGeometry(10, 130, 190, this->height() - 150);
// initOsgWindow();
}
void Widget::onChoseFileButtonClicked()
{
_lFileNames.append(QFileDialog::getOpenFileNames(this, "Select osgb files", "C:/Users/Administrator/Deskop", "Osgb(*.osgb)"));
for(auto file: _lFileNames)
{
ui->textBrowser->append(file);
ui->textBrowser->append("\n");
}
}
void Widget::onChoseDirButtonClicked()
{
QFileInfoList infoList1 = QDir(QFileDialog::getExistingDirectory()).entryInfoList();
for(auto file: infoList1)
{
if(file.isFile() && file.suffix().toStdString() == "osgb")
{
_lFileNames.push_back(file.filePath());
ui->textBrowser->append(file.filePath());
ui->textBrowser->append("\n");
}
}
}
void Widget::onShowModelButtonClicked()
{
initOsgWindow();
}
void Widget::onClearButtonClicked()
{
_lFileNames.clear();
ui->textBrowser->setText("");
initOsgWindow();
}
void Widget::initOsgWindow()
{
_viewer = _pOsgWidget->getOsgViewer();
// +++++++++这一句很重要++++++++++++
_viewer->getCamera()->setClearMask(GL_DEPTH_BUFFER_BIT);
// +++++++++这一句很重要++++++++++++
_viewer->setCameraManipulator(new osgGA::TrackballManipulator);
osg::ref_ptr<osg::Group> group = new osg::Group;
group->addChild(backGround("here_add_your_back_ground_image_path", _pOsgWidget->width(), _pOsgWidget->height()));
for(auto file: _lFileNames)
{
group->addChild(osgDB::readNodeFile(file.toStdString()));
}
_viewer->setSceneData(group);
}
2.2.3 ui_Widget.h
这是根据ui文件生成的ui头文件,这里直接给出设计好的界面文件:
/********************************************************************************
** Form generated from reading UI file 'Widget.ui'
**
** Created by: Qt User Interface Compiler version 5.14.2
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef UI_WIDGET_H
#define UI_WIDGET_H
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QTextBrowser>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_Widget
{
public:
QTextBrowser *textBrowser;
QPushButton *pushButton;
QPushButton *pushButton_2;
QLabel *label_2;
QPushButton *pushButton_3;
QPushButton *pushButton_4;
void setupUi(QWidget *Widget)
{
if (Widget->objectName().isEmpty())
Widget->setObjectName(QString::fromUtf8("Widget"));
Widget->resize(800, 600);
textBrowser = new QTextBrowser(Widget);
textBrowser->setObjectName(QString::fromUtf8("textBrowser"));
textBrowser->setGeometry(QRect(10, 130, 191, 451));
QFont font;
font.setFamily(QString::fromUtf8("\345\256\213\344\275\223"));
font.setPointSize(10);
textBrowser->setFont(font);
textBrowser->setAutoFormatting(QTextEdit::AutoAll);
pushButton = new QPushButton(Widget);
pushButton->setObjectName(QString::fromUtf8("pushButton"));
pushButton->setGeometry(QRect(10, 0, 71, 31));
pushButton->setFont(font);
pushButton_2 = new QPushButton(Widget);
pushButton_2->setObjectName(QString::fromUtf8("pushButton_2"));
pushButton_2->setGeometry(QRect(90, 0, 61, 31));
pushButton_2->setFont(font);
label_2 = new QLabel(Widget);
label_2->setObjectName(QString::fromUtf8("label_2"));
label_2->setGeometry(QRect(10, 90, 191, 31));
QFont font1;
font1.setFamily(QString::fromUtf8("\345\256\213\344\275\223"));
font1.setPointSize(12);
label_2->setFont(font1);
pushButton_3 = new QPushButton(Widget);
pushButton_3->setObjectName(QString::fromUtf8("pushButton_3"));
pushButton_3->setGeometry(QRect(90, 40, 61, 31));
pushButton_3->setFont(font);
pushButton_4 = new QPushButton(Widget);
pushButton_4->setObjectName(QString::fromUtf8("pushButton_4"));
pushButton_4->setGeometry(QRect(10, 40, 71, 31));
pushButton_4->setFont(font);
retranslateUi(Widget);
QMetaObject::connectSlotsByName(Widget);
} // setupUi
void retranslateUi(QWidget *Widget)
{
Widget->setWindowTitle(QCoreApplication::translate("Widget", "Widget", nullptr));
pushButton->setText(QCoreApplication::translate("Widget", "\351\200\211\346\213\251\346\226\207\344\273\266", nullptr));
pushButton_2->setText(QCoreApplication::translate("Widget", "\346\230\276\347\244\272", nullptr));
label_2->setText(QCoreApplication::translate("Widget", "\345\275\223\345\211\215\350\246\201\346\230\276\347\244\272\347\232\204\346\226\207\344\273\266\357\274\232", nullptr));
pushButton_3->setText(QCoreApplication::translate("Widget", "\346\270\205\347\251\272", nullptr));
pushButton_4->setText(QCoreApplication::translate("Widget", "\351\200\211\346\213\251\346\226\207\344\273\266\345\244\271", nullptr));
} // retranslateUi
};
namespace Ui {
class Widget: public Ui_Widget {
};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_WIDGET_H
2.2.4 main.cpp
main函数里只需要实例化主窗口类并显示就行。代码如下:
#include "Widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
2.3 完整源码
见上文
3. 效果图
4. 不足
- 在窗口放大的时候,背景图片无法随之自适应大小,需点击一次显示按钮才能正常显示
- 模型显示窗口不支持osgViewer.exe打开时的各种快捷键操作,如W显示三角网或点云,S显示当前渲染信息等。
- 如移动模型之后继续添加新的模型进行显示,会将当前显示的模型恢复原位,跟新添加的模型无法进行直接对比,当然这一步可通过new一个新的osgQOpenGLWidget进行解决。
- 只支持三维模型的展示,别的操作啥都没有。
如果你觉得有用,不妨点个赞~~~