长沙桥梁裂缝检测项目需要将处理后的桥底影像、裂缝纹理贴到桥梁模型上,用于交互式展示。
需求分析很重要,是系统开发的……
本桥梁纹理系统的需求为后期系统开发和维护……
理清需求,明确开发工作,有助于系统的开发和维护。
桥梁纹理系统是桥梁裂缝检测系统的系统前端,提供方便的可视化手段来展示整个桥梁裂缝检测系统的成果,即是系统宣传和公关工具,也有助于评价裂缝检测系统的效果。
本桥梁纹理系统的上游数据提供方,为本系统提供桥梁模型参数和纹理信息。
桥梁裂缝检测系统提供的桥面纹理图片,含位置信息、灰度信息和裂缝拓扑信息。
在原始纹理图片基础上,人工添加的纹理矢量线画图。
实现桥梁裂缝原始纹理图片和手工纹理图层的加载和显示,并可执行缩放、平移等常规操作。
桥梁模型和纹理平面的三维展示,有多个视图,有多种交互方式(Trackball,Bird view,etc)。并能对关键纹理贴图平面进行方便的二维显示。
在二维显示的基础上,在纹理图层上进行纹理编辑和保存。(可能需要额外的纹理编辑权限)
在三维显示的基础上,对纹理的放置位置和纹理贴合方式进行编辑修改。(可能需要额外的纹理编辑权限)
二维和三维联动,方便模型和纹理的查看和修改。
某需求分析参考(内部文档资料)
etc
Windows 8/10
OpenCV、OpenSceneGraph 库
如果系统的服务器端提供 Web 浏览方式,软件环境需求可相应削弱至一台配置了支持 WebGL 浏览器的 PC 或一般智能机。
桥梁纹理系统称为 [bt::System]
,主要分为后台服务器端([bt::Server]
)和前台客户端([bt::Client]
),总体模型为:
[bt::System]
整个系统被称为桥梁纹理系统,分为 [bt::Server]
和 [bt::Client]
,所有代码类放在 bt
命名空间中。
[bt::Server]
[bt::Auth]
[bt::Database]
[bt::Logger]
[bt::Computing]
[bt::Kernel]
[bt::Client]
[bt::Agent]
[bt::Project]
[bt::Project::Genisys]
[bt::Project::Builder]
[bt::Project::Management]
[bt::BridgeEditor]
[bt::BridgeViewer]
[bt::TextureEditor]
[bt::TextureViewer]
[bt::SessionLogger]
[bt::Server]
和 [bt::Client]
通过网络连接和通讯。下面简述各个模块的功能点。
[bt::Server]
后端服务器,为前端服务,分为 [bt::Auth]
(认证模块)、[bt::Database]
(数据库模块)、 [bt::Logger]
(日志模块)、[bt::Computing]
(计算模块)、 [bt::Kernel]
(服务器核心)。
各模块的主要工作:
[bt::Auth]
[bt::Database]
[bt::Kernel]
,继而让用户知道冲突的发生和处理结果[bt::Logger]
[bt::Computing]
[bt::ComputingTaskSubmission]
模块向服务器请求以使用计算模块[bt::Kernel]
[bt::Auth]
、[bt::Database]
和 [bt::Computing]
模块[bt::Client]
的登录[bt::Client]
对数据资源、计算资源的请求
[bt::Computing]
模块,当计算任务完成后,从 [bt::Computing]
模块接受返回结果并回传给用户[bt::Client]
前端客户端,包括 [bt::Agent]
(客户端代理模块)、[bt::Project]
(高级功能模块组)、 [bt::BridgeEditor]
(模型编辑模块)、[bt::TextureEditor]
(纹理编辑模块)、 [bt::ComputingTaskSubmission]
(计算任务提交模块)、[bt::SessionLogger]
(会话日志模块)、 [bt::BridgeReport]
(桥梁病害统计分析报告)。
[bt::Agent]
[bt::ComputingTaskSubmission]
模块计划让服务器处理的计算任务[bt::Project]
、[bt::TextureEditor]
、[bt::BridgeEditor]
等模块中执行的且需要全局生效的操作[bt::SessionLogger]
中记录的浏览编辑信息[bt::Project]
[bt::Project::Genisys]
2
[bt::Project::Builder]
[bt::Computing]
模块的帮助。且 [bt::Computing]
模块默认对 [bt::Project]
的请求更优先地执行[bt::Project::Management]
[bt::BridgeEditor]
[bt::BridgeViewer]
以实现基本的模型显示功能
[bt::BridgeViewer]
应提供多种方式的模型浏览操作模式[bt::BridgeViewer]
能与二维纹理浏览模块 [bt::TextureViewer]
联动[bt::TextureEditor]
[bt::TextureViewer]
子模块以显示二维纹理
[bt::TextureViewer]
提供对纹理图片的基本操作,如显示、切换、测量、缩放[bt::BridgeViewer]
模块的联动,从当前纹理图片转到模型相应视角[bt::ComputingTaskSubmission]
[bt::Client]
应当判断计算任务的复杂度,必要时将任务提交至服务器代为处理[bt::SessionLogger]
[bt::BridgeReport]
[bt::Server]
上线运行后,可以运行客户端软件连接服务器,查看桥梁模型和桥面纹理。基本使用流程是:
本 [bt::System]
处于桥梁裂缝检测系统的下游,和客户联系紧密。一方面要利用上游的桥梁和纹理数据为客户呈现活灵活现的三维、二维显示效果,还要在实际运行过程中收集信息、向系统上游反馈数据质量的不足和改进点。
[bt::System]
所处的位置决定了其重要性。只有把 [bt::System]
做好,出色地满足客户的各方面需求,桥梁裂缝检测系统才能得到应有的认可和好评。如果本桥梁纹理系统做得不好、使用体验差,导致用户不满意,不仅是本系统的失败,也是整个桥梁裂缝检测系统的失败。
(TODO:继续补充:论自我展示的重要性。)
鉴于 [bt::System]
的定位,在系统设计上,我们要:
综合考虑客户、开发者、维护者、投资者的利益,我们将系统设计成中心管理的方式,因为这种模式
是在数据保护和系统推广之间的折中选择。
从 总体模型 中可以提炼出总体功能架构:
总体功能架构图(略)
总体业务流程(概述、流程)
用例图(略)。
功能描述(略)。
[bt::Kernel]
应能处理多并发访问,有用足够的 IO 带宽,对每个用户的响应时间小于一定阈值(比如:resThresh = 300ms)。
[bt::Database]
支持查询优化。……
[bt::Auth]
应能满足用户第三方登录需求,满足用户通过邮箱、手机重置密码的需求,认证响应时间小于一定阈值(比如 authThresh = 400ms)。
[bt::Logger]
及时将所有信息记录下来。
[bt::Client]
应能保存一些离线数据,在偶然断网情况下也能浏览。
[bt::Agent]
应能把本地账户信息(由 [bt::SessionLogger]
收集)提交到中心服务器。
[bt::BridgeEditor]
和 [bt::TextureEditor]
应能充分利用计算机 GPU 能力,减少卡顿,并不应影响主界面消息循环线程。在三维浏览、编辑模块卡死时能重置这个模块。
[bt::SessionLogger]
应能记录用户的浏览操作,保存在当前账户中。
完成情况 | 功能点 | 备注 |
---|---|---|
☐ | 数据库 | null |
☑ | 二维浏览 | null |
☐ | 二维编辑 | null |
☑ | 三维浏览 | null |
☐ | 三维编辑 | null |
☐ | 二维浏览 | null |
(系统功能性测试只是一个概略,在 测试模块 有具体每个功能点的测试情况)
业务的完整性,和每个业务模块的可用性
单元测试、集成测试、系统测试、压力测试、etc
序号 | 系统 | 功能点 | 期望 | 测试结果 | 备注 |
---|---|---|---|---|---|
1 | [bt::Server] |
Web 服务 | 接收请求并应答 | ☐ | null |
2 | [bt::TextureViewer] |
缩放 | 接收请求并应答 | ☐ | null |
测试环境(与与运行环境一致,
被测模块 –> 单元测试 –> 集成测试 ->> 确认测试 –> 系统测试(测试过程图)
服务器端部署(应用服务器、数据服务器、备用网络服务器)
软件部署(需要哪些软件?多少套,型号)
客户端部署(安装包、浏览器)(安装包的获取,认证?账户注册流程……)
可运行+文档+培训
本项目的验收工作应遵循以下标准:
注:
这部分放在后期再完善。
目的、背景、参考资料、定义
数据库分布
逻辑结构设计
各数据库直接的数据交换
命名规则 + 对象命名规范 + 编码规范
bt::Server 子系统
bt::Client 子系统(PC 客户端、Web 客户端)
bt::Auth 子系统
?什么是数据字典?……
(本文档根据对相关资料的理解,通过对用户的交流,从满足用户业务应用需求的角度出发,结合项目建设的实际情况,对应急管理平台系统的界面进行详细描述。)
本系统作为**系统的一个子系统,是其它相关子系统在统一规划的基础上进行同步建设的。结合项目建设目标的要求及系统特点,进行定制开发和改造,从而实现整体规划目标。
MainWindow
Widget
Dialog
Dock
快捷键
二维纹理
编辑器
认证
保存
提交
上传
原图
《Qt 界面设计?》
浏览、索引(查找、搜索)、认证、编辑、提交更改(纹理修改是在另一个图层上,不修改原始图片)、最终 blend 显示(二维 alpha blend,三维两个纹理 transparent blend)
(后台审核后判断是否把编辑后的状态设为默认。)
osg::Switch
(是一种 Group 相比 Group 能快速地 hidden/show)osg::MatrixTransform
(translate/rotate/scale)getMatrix()
(位置姿态)、getInverseMatrix()
、handle()
(键鼠)杂七杂八的 Note:
// 设置 node 名字
node.setName( std::string );
// TODO: 从 config 中判断是否纹理需要重新贴
// (新建一个静态类,专门用来配置三维模型,配置放在静态的 `getInstance()`{.cpp} 里)
bool applied;
std::string textureImgUrl;
node.setUpdateCallback( cb );
if ( !applied ) {
apply();
applied = true;
}
// more snippets
osg::Matrix osg::Matrixd::rotate( dx, osg::X_AXIS,
dy, .....Y.....,
dz, .....Z..... );
* osg::GUIEventHandlerAdapter
+ KEYDOWN(键盘)
+ PUSH(单击)
+ DRAG(从代码上看更像是 MOVE)
// osg 里的矩阵运算用的是左乘
current pose = rot * trans
// osg, rot(0,0,0) 其实是朝向 (0,0,-1)(面对着 xy 平面)
// 参数里用的是弧度,有弧度角度转化的函数
osg::PI
# include them all
include( ${QT_USE_FILE} )
include_directories( ${OPENCV_INCLUDE_DIR} )
include_directories( ${Boost_INCLUDE_DIR} )
include_directories( ${OSG_INCLUDE_DIR} )
# link them all
link_directories( ${Boost_LIBRARY_DIR} )
link_directories( "D:/boost_1_58_0/stage/lib" )
link_directories( ${OSG_LIBRARY_DIR_DEBUG} )
link_directories( ${OSG_LIBRARY_DIR_RELEASE} )
target_link_libraries( ${PROJECT_NAME} ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${GLUT_LIBRARIES} ${OpenCV_LIBS} ${OpenSceneGraph_LIBS_Debug} ${OpenSceneGraph_LIBS_Release} Utils SignCutter )
// viewer
const std::string name="Main";
bool windowDecoration = false;
if ( MONO_VIWER == vt )
{
viewNum=1;
QWidget* widget = addViewWidget( createGraphicsWindow(0,0,200,200,name,windowDecoration), VIEW_PERSPECTIVE, true ); // set as main
QGridLayout* grid = new QGridLayout;
grid->addWidget( widget);
setLayout( grid );
}
else if ( FOUR_VIEWERS == vt )
{
//views=std::vector<osg::ref_ptr<osgViewer::View> >(4);
viewNum=4;
QWidget* widget1 = addViewWidget( createGraphicsWindow(0,0,100,100,name,windowDecoration), VIEW_PERSPECTIVE, true ); // set as main
QWidget* widget2 = addViewWidget( createGraphicsWindow(0,0,100,100,name,windowDecoration), VIEW_TOP );
QWidget* widget3 = addViewWidget( createGraphicsWindow(0,0,100,100,name,windowDecoration), VIEW_LEFT );
QWidget* widget4 = addViewWidget( createGraphicsWindow(0,0,100,100,name,windowDecoration), VIEW_FRONT );
QGridLayout* grid = new QGridLayout;
grid->addWidget( widget1, 0, 0 );
grid->addWidget( widget2, 0, 1 );
grid->addWidget( widget3, 1, 0 );
grid->addWidget( widget4, 1, 1 );
setLayout( grid );
} else if ( SEVEN_VIEWERS == vt ) {
viewNum=6;
QWidget* widget1 = addViewWidget( createGraphicsWindow(0,0,100,100,name,windowDecoration), VIEW_FRONT );
QWidget* widget2 = addViewWidget( createGraphicsWindow(0,0,100,100,name,windowDecoration), VIEW_TOP );
QWidget* widget3 = addViewWidget( createGraphicsWindow(0,0,200,200,name,windowDecoration), VIEW_PERSPECTIVE, true ); // set as main
QWidget* widget4 = addViewWidget( createGraphicsWindow(0,0,100,100,name,windowDecoration), VIEW_LEFT );
QWidget* widget5 = addViewWidget( createGraphicsWindow(0,0,100,100,name,windowDecoration), VIEW_LEFT );
QWidget* widget6 = addViewWidget( createGraphicsWindow(0,0,100, 50,name,windowDecoration), VIEW_FRONT );
QWidget* widget7 = addViewWidget( createGraphicsWindow(0,0,100, 50,name,windowDecoration), VIEW_TOP );
QGridLayout* grid = new QGridLayout;
grid->addWidget( widget1, 0, 0, 1, 2 );
grid->addWidget( widget2, 1, 0, 1, 2 );
grid->addWidget( widget3, 0, 2, 2, 4 );
grid->addWidget( widget4, 2, 0, 1, 2 );
grid->addWidget( widget5, 2, 2, 1, 2 );
grid->addWidget( widget6, 2, 4, 1, 1 );
grid->addWidget( widget7, 2, 5, 1, 1 );
setLayout( grid );
} else {
settingViewer();
}
connect( &_timer, SIGNAL(timeout()), this, SLOT(update()) );
_timer.start( 10 );
this->setGeometry( 100, 100, 800, 600 );
this->show();
// 一个直接添加,一个加点 Scribe 特效
osg::ref_ptr<osg::Node> cow = osgDB::readNodeFile( filename );
osg::ref_ptr<osgFX::Scribe> sc = new osgFX::Scribe;
sc->addChild( cow.get() );
group.addChild( cow.get() );
group.addChild( sc.get() );
camera->setViewMatrixAsLookAt( eyePoint, center, upDirection );
class ccCustomQTreeView (CloudCompare_tree.h)
话说 CloudCompare 的程序写的很赞,连 CMakeLists.txt 都很考究,包括但不限于空格、大小写、注释。6
配置的读取,
理论上应该支持
OpenCV, YAML
cv::FileStorage fs( filename.yaml, FileStorage::{READ,WRITE} );
// read
int frameCount = (int)fs2["frameCount"];
fs2["frameCount"] >> frameCount;
// write
fs << "key" << value; // object.pair
fs << "[:" << "item1" << item1 << "item2" << item2 << "]"; // object.array
fs << "{:" /* key-value pairs */ << "}" ; object
output:
%YAML:1.0
width: 30.
length: 60.
beamnum: 4
src/qCC/db_tree/ccDBRoot.h -> CloudCompare/ccDBRoot.h src/qCC/ui_templates/mainWindow.ui -> CloudCompare/mainWindow.ui
mainWindow.ui:
<customwidget>
<class>ccCustomQTreeView</class>
<extends>QTreeView</extends>
<header location="global">ccDBRoot.h</header>
</customwidget>
通过在 QtDesigner 里面对 UI 进行类型提升(设置提升后的 ClassName,基类 BaseClass,以及头文件名 Header.h)
Qt 中响应右键?这部分要看参考 CloudCompare 源码:src/qCC/db_tree/ccDBRoot.h
// 新建 action
m_addEmptyGroup = new QAction( "Add empty group", this );
// 连接 action
connect( m_addEmptyGroup, SIGNAL(triggered()),
this, SLOT(addEmptyGroup()) );
// 添加到菜单
QMenu menu;
menu.addAction( m_addEmptyGroup );
menu.addSeparator( );
// 右键响应其实是弹出 ContextMenu,先打开功能,再连接
widget->setContextMenuPolicy( Qt::CustomContextMenu );
connect( m_dbTreeWidget, SIGNAL(customContextMenuRequested(const QPoint&)),
this, SLOT(showContextMenu(const QPoint&)) );
// 再去实现:通过点击的位置,判断需要弹出什么样子右键菜单;一翻逻辑后显示 menu
void ccDBRoot::showContextMenu(const QPoint& menuPos)
QModelIndex index = m_dbTreeWidget->indexAt(menuPos);
if ( index.isValid() ) { /* process */ }
menu.exec(m_dbTreeWidget->mapToGlobal(menuPos));
上面的 menu 没有 parent,它的显示位置要把 menuPos 转化到全局的坐标小,所以用到了 QPoint QWidget::mapToGlobal(const QPoint & pos) const
关于 contextMenuPolicy : Qt::ContextMenuPolicy
The default value of this property is
Qt::DefaultContextMenu
, which means thecontextMenuEvent()
handler is called. Other values areQt::NoContextMenu
,Qt::PreventContextMenu
,Qt::ActionsContextMenu
, andQt::CustomContextMenu
. WithQt::CustomContextMenu
, the signal customContextMenuRequested() is emitted.
图片列表
model->setHorizontalHeaderLabels(
QStringList()<< tr("") << tr("Images") << tr("Path") );
icon = new QIcon( ":/images/folder.png" );
item = new QStandardItem( *icon, QString("Images") );
ToolButton 的设置
int btnSz = 24;
QString colorName = "red";
QColor colorRed = QColor( 255, 0, 0, 255 );
QPixmap colorPix( btnSz, btnSz );
colorPix.fill( colorRed );
QAction *colorAction = new QAction( colorName, this );
colorAction->setData( colorRed );
colorAction->setIcon( colorPix );
connect( colorAction, SIGNAL(triggered()),
this, SLOT(changeColorToRed()) );
QToolButton *colorBtn = new QToolButton;
colorBtn->setFixedSize( QSize( btnSz, btzSz ) );
colorBtn->setAutoRaise( true );
colorBtn->setDefaultAction( colorAction );
colorBtn->setToolTip( "set color to red." );
buttonsLayout->addWidget( colorBtn, 1, 3 );
<span id="nice"></span>
go nice
QString::number( integer, base );
QString::number( decimal, flag, precise );
显示缩略图的方法
// method 1 (from StackOverflow)
QStandardItemModel *model = new QStandardItemModel;
QImage image(":/cat/lovers/own/myCat.jpg");
QStandardItem *item = new QStandardItem();
item->setData(QVariant(QPixmap::fromImage(image)), Qt::DecorationRole);
model->setItem(0, 0, item);
ui->tableView->setModel(model);
// method 2(tried out)
QStandardItem *item =
new QStandardItem( QIcon(QPixmap(filename).scaledToHeight(40)), Utils::basename( filename ) );
model->appendRow( item ); // 会缩放至行高
有个很有意思的文档:c++ - Show image in a column of QTableView from QSqlTableModel - Stack Overflow
To do this, the concept is simple: provide QTableView with 【QVariant of QPixmap】 as QTableView render them according to Qt::DecorationRole
.
You may subclass QSqlTableModel
and reimplement the virtual function QVariant data(const QModelIndex & index, int role = Qt::DisplayRole)
and make the image column return the QPixmap as QVariant, with the decoration role. So do something like this:
QVariant CustomSqlTableModel::data(const QModelIndex &idx, int role = Qt::DisplayRole) const
{
if (idx.column() == imageColumn) {
QString imgFile = QSqlTableModel::data(idx, Qt::DisplayRole); // get path string
if (role == Qt::DisplayRole)
return QString(); // return the path string for display role
QImage image(imgFile);
/* some modification to the image, maybe */
QPixmap pixmap(imgFile);
if (role == Qt::DecorationRole)
return pixmap; // return QPixmap for decoration role
if (role == Qt::SizeHintRole)
return pixmap.size(); // in case need the image size
}
return QSqlTableModel::data( idx, role ); // use original data() outside the imageColumn
}
Besides, you can also try subclassing QStyledItemDelegate and reimplement paint()
function to customize your own delegate, but that will require a more complicated work. An example using delegate can be found here. You can paint whatever you want with delegate, even a button.
// m_pivotVisibilityPopupButton 可以弹出三个子菜单
//pivot center pop-up menu
{
m_pivotVisibilityPopupButton = new QToolButton();
QMenu* menu = new QMenu(m_pivotVisibilityPopupButton);
menu->addAction(actionSetPivotAlwaysOn);
menu->addAction(actionSetPivotRotationOnly);
menu->addAction(actionSetPivotOff);
m_pivotVisibilityPopupButton->setMenu(menu);
m_pivotVisibilityPopupButton->setPopupMode(QToolButton::InstantPopup);
m_pivotVisibilityPopupButton->setToolTip("Set pivot visibility");
m_pivotVisibilityPopupButton->setStatusTip(m_pivotVisibilityPopupButton->toolTip());
toolBarView->insertWidget(actionZoomAndCenter,m_pivotVisibilityPopupButton);
m_pivotVisibilityPopupButton->setEnabled(false); // 默认点不了
}
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>
using namespace rapidjson;
using namespace std;
int main() {
StringBuffer s;
Writer<StringBuffer> writer(s);
writer.StartObject();
writer.String("hello");
writer.String("world");
writer.String("t");
writer.Bool(true);
writer.String("f");
writer.Bool(false);
writer.String("n");
writer.Null();
writer.String("i");
writer.Uint(123);
writer.String("pi");
writer.Double(3.1416);
writer.String("a");
writer.StartArray();
for (unsigned i = 0; i < 4; i++)
writer.Uint(i);
writer.EndArray();
writer.EndObject();
cout << s.GetString() << endl;
return 0;
}
#include "rapidjson/reader.h"
#include <iostream>
using namespace rapidjson;
using namespace std;
struct MyHandler {
bool Null() { cout << "Null()" << endl; return true; }
bool Bool(bool b) { cout << "Bool(" << boolalpha << b << ")" << endl; return true; }
bool Int(int i) { cout << "Int(" << i << ")" << endl; return true; }
bool Uint(unsigned u) { cout << "Uint(" << u << ")" << endl; return true; }
bool Int64(int64_t i) { cout << "Int64(" << i << ")" << endl; return true; }
bool Uint64(uint64_t u) { cout << "Uint64(" << u << ")" << endl; return true; }
bool Double(double d) { cout << "Double(" << d << ")" << endl; return true; }
bool String(const char* str, SizeType length, bool copy) {
cout << "String(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
return true;
}
bool StartObject() { cout << "StartObject()" << endl; return true; }
bool Key(const char* str, SizeType length, bool copy) {
cout << "Key(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
return true;
}
bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; }
bool StartArray() { cout << "StartArray()" << endl; return true; }
bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; }
};
int main() {
const char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ";
MyHandler handler;
Reader reader;
StringStream ss(json);
reader.Parse(ss, handler);
return 0;
}
Refs
文档本由我撰写。可查看我的备份/最新:CVRS 编码规范 – 备份。↩
Genisys:创世纪,一个桥梁工程的发源。↩
如,将模型以某一方式旋转 30°,再 60°,就应能合并为 90°。↩
如果有这样的专家系统的话。↩
这简直是应用系统中的 Arch Linux。↩
那些 CMake 语句(说的不是变量)用大写的,都是逗逼。
cmake_minimum_required(VERSION 2.8)
project( CloudCompare )
set( VERSION_MAJOR 2 )
set( VERSION_MINOR 7 )
set( VERSION_PATCH 0 )
include_directories( ${GLEW_LIB_SOURCE_DIR}/include )
include_directories( ${CC_FBO_LIB_SOURCE_DIR}/include )
include_directories( ${CC_CORE_LIB_SOURCE_DIR}/include )
include_directories( ${QCC_DB_LIB_SOURCE_DIR} )
if( MSVC )
include_directories( ${QCC_DB_LIB_SOURCE_DIR}/msvc )
endif()
include_directories( ${QCC_IO_LIB_SOURCE_DIR} )
include_directories( ${QCC_GL_LIB_SOURCE_DIR} )
include_directories( ${EXTERNAL_LIBS_INCLUDE_DIR} )
include_directories( ${CloudComparePlugins_SOURCE_DIR} )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/db_tree )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/ui_templates )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../libs/qxt )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../libs/qcustomplot )
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
# QCustomPlot
set( QCUSTOMPLOT_HEADERS ../libs/qcustomplot/qcustomplot.h )
set( QCUSTOMPLOT_SOURCES ../libs/qcustomplot/qcustomplot.cpp )
file( GLOB header_list *.h db_tree/*.h ${QXT_HEADERS} ${QCUSTOMPLOT_HEADERS} )
file( GLOB source_list *.cpp db_tree/*.cpp ${QXT_SOURCES} ${QCUSTOMPLOT_SOURCES} )
# 3DX support (3dConnexion devices)
if ( ${OPTION_SUPPORT_3DCONNEXION_DEVICES} )
file( GLOB 3DX_header_list devices/3dConnexion/*.h )
file( GLOB 3DX_source_list devices/3dConnexion/*.cpp )
list( APPEND header_list ${3DX_header_list} )
list( APPEND source_list ${3DX_source_list} )
endif()
file( GLOB ui_list ui_templates/*.ui )
file( GLOB qrc_list *.qrc )
#file( GLOB rc_list *.rc )
file( GLOB txt_list TODO.txt bin_other/history.txt )
if ( USE_QT5 )
qt5_wrap_ui( generated_ui_list ${ui_list} )
qt5_add_resources( generated_qrc_list ${qrc_list} )
else()
# find Qt mocable files
find_mocable_files( mocable_list ${header_list} )
qt4_wrap_cpp( moc_list ${mocable_list} )
qt4_wrap_ui( generated_ui_list ${ui_list} )
qt4_add_resources( generated_qrc_list ${qrc_list} )
endif()
# App icon with MSVC
if( MSVC )
set( rc_list images/icon/cc_icon.rc )
endif()
if( MSVC )
#to get rid of the (system) console
add_executable( ${PROJECT_NAME} WIN32 ${header_list} ${source_list} ${moc_list} ${generated_ui_list} ${generated_qrc_list} ${rc_list} ${resource_list} ${txt_list} )
elseif( APPLE )
add_executable( ${PROJECT_NAME} MACOSX_BUNDLE ${header_list} ${source_list} ${moc_list} ${generated_ui_list} ${generated_qrc_list} ${resource_list} ${txt_list} )
else()
add_executable( ${PROJECT_NAME} ${header_list} ${source_list} ${moc_list} ${generated_ui_list} ${generated_qrc_list} ${rc_list} ${resource_list} ${txt_list} )
endif()
target_link_libraries( ${PROJECT_NAME} GLEW_LIB )
target_link_libraries( ${PROJECT_NAME} CC_FBO_LIB )
target_link_libraries( ${PROJECT_NAME} CC_CORE_LIB )
target_link_libraries( ${PROJECT_NAME} QCC_DB_LIB )
target_link_libraries( ${PROJECT_NAME} QCC_IO_LIB )
target_link_libraries( ${PROJECT_NAME} QCC_GL_LIB )
target_link_libraries( ${PROJECT_NAME} ${EXTERNAL_LIBS_LIBRARIES} )
if ( USE_QT5 )
if (WIN32)
target_link_libraries( ${PROJECT_NAME} Qt5::WinMain )
endif()
qt5_use_modules(${PROJECT_NAME} Core Gui Widgets OpenGL PrintSupport)
endif()
# contrib. libraries support
target_link_contrib( ${PROJECT_NAME} ${CLOUDCOMPARE_DEST_FOLDER} )
# 3dConnexion devices support
target_link_3DXWARE( ${PROJECT_NAME} )
# Default preprocessors
set_default_cc_preproc( ${PROJECT_NAME} )
# Add custom preprocessor definitions
set_property( TARGET ${PROJECT_NAME} APPEND PROPERTY COMPILE_DEFINITIONS USE_GLEW GLEW_STATIC )
if( WIN32 )
set_property( TARGET ${PROJECT_NAME} APPEND PROPERTY COMPILE_DEFINITIONS CC_USE_AS_DLL QCC_DB_USE_AS_DLL QCC_IO_USE_AS_DLL )
if (MSVC)
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES LINK_FLAGS " /MANIFEST:NO")
endif()
endif()
#set_property( TARGET ${PROJECT_NAME} APPEND PROPERTY COMPILE_DEFINITIONS_RELEASE XXX) #nothing right now
#set_property( TARGET ${PROJECT_NAME} APPEND PROPERTY COMPILE_DEFINITIONS_DEBUG XXX) #nothing right now
# App icon with Code::Blocks/MinGW
if( WIN32 )
if( MINGW )
add_custom_command( TARGET ${PROJECT_NAME} PRE_BUILD COMMAND windres -i ${CMAKE_CURRENT_SOURCE_DIR}/images/icon/cc_icon.rc --input-format=rc -o ${CMAKE_CURRENT_BINARY_DIR}/cc_icon.res -O coff )
endif()
endif()
# install program
install_ext( TARGETS ${PROJECT_NAME} ${CLOUDCOMPARE_DEST_FOLDER} )
# Auxiliary files
set( auxFiles bin_other/history.txt bin_other/license.txt bin_other/global_shift_list_template.txt )
if( WIN32 )
# Export Qt dlls
install_Qt_Dlls( ${CLOUDCOMPARE_DEST_FOLDER} )
install_Qt_ImageFormats( ${CLOUDCOMPARE_DEST_FOLDER} )
install_Qt5_plugins( ${CLOUDCOMPARE_DEST_FOLDER} )
# Additional auxiliary file(s)
list( APPEND auxFiles bin_other/start.bat )
endif()
# Install auxiliary files
foreach( filename ${auxFiles} )
install_ext( FILES ${filename} ${CLOUDCOMPARE_DEST_FOLDER} )
endforeach()
# in order to ensure that everything else is installed first, put the Mac bundling in its own subdirectory
if( APPLE )
set_property( TARGET ${PROJECT_NAME} PROPERTY MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Mac/CloudCompare.plist )
set( MACOSX_BUNDLE_ICON_FILE cc_icon.icns )
set( MACOSX_BUNDLE_SHORT_VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}" )
set( MACOSX_BUNDLE_LONG_VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}" )
set( MACOSX_BUNDLE_BUNDLE_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}" )
add_subdirectory( Mac )
endif()
# Export common shader files to all install destinations
if( APPLE )
install( FILES ${CC_FBO_LIB_SOURCE_DIR}/bilateral/bilateral.frag DESTINATION ${CLOUDCOMPARE_MAC_BASE_DIR}/Contents/Shaders )
install( FILES ${CC_FBO_LIB_SOURCE_DIR}/bilateral/bilateral.vert DESTINATION ${CLOUDCOMPARE_MAC_BASE_DIR}/Contents/Shaders )
install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/shaders/ColorRamp/color_ramp.frag DESTINATION ${CLOUDCOMPARE_MAC_BASE_DIR}/Contents/Shaders/ColorRamp )
#install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/shaders/Rendering/rendering.frag DESTINATION ${CLOUDCOMPARE_MAC_BASE_DIR}/Contents/Shaders/Rendering )
#install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/shaders/Rendering/rendering.vert DESTINATION ${CLOUDCOMPARE_MAC_BASE_DIR}/Contents/Shaders/Rendering )
else()
install_ext( FILES ${CC_FBO_LIB_SOURCE_DIR}/bilateral/bilateral.frag ${CLOUDCOMPARE_DEST_FOLDER} /shaders )
install_ext( FILES ${CC_FBO_LIB_SOURCE_DIR}/bilateral/bilateral.vert ${CLOUDCOMPARE_DEST_FOLDER} /shaders )
install_ext( FILES ${CMAKE_CURRENT_SOURCE_DIR}/shaders/ColorRamp/color_ramp.frag ${CLOUDCOMPARE_DEST_FOLDER} /shaders/ColorRamp )
#install_ext( FILES ${CMAKE_CURRENT_SOURCE_DIR}/shaders/Rendering/rendering.frag ${CLOUDCOMPARE_DEST_FOLDER} /shaders/Rendering )
#install_ext( FILES ${CMAKE_CURRENT_SOURCE_DIR}/shaders/Rendering/rendering.vert ${CLOUDCOMPARE_DEST_FOLDER} /shaders/Rendering )
endif()
参见我以前的笔记:直觉上我也觉得用 & 不对。↩