通過生成一個索引檔案,使用OpenSceneGraph加載傾斜攝影資料。
目錄
- 1. 概述
- 2. 執行個體
- 2.1. 代碼
- 2.2. 解析
- 3. 結果
ContextCapture(Smart3D)生成的傾斜攝影模型資料一般都形如如下組織結構:
在Data目錄下包含了分塊的瓦片資料,每個瓦片都是一個LOD檔案夾。osg能夠直接讀取osgb格式,理論上隻需要依次加載每個LOD的金字塔層級最高的osgb,整個傾斜攝影模型資料就加載進來了。不過有點麻煩的是這類資料缺乏一個整體加載的入口,如果每次加載都周遊整個檔案夾加載的話,會影響加載的效率。是以一般的資料檢視軟體都會為其增加一個索引。
這裡就給傾斜攝影資料添加一個osgb格式的索引檔案,生成後就可以通過OSG直接加載整個傾斜攝影模型資料。
具體的實作代碼如下:
#include <iostream>
#include <string>
#include <QDir>
#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
using namespace std;
//查找目錄下所有的檔案夾
static void findDir(string dir, vector<string>& subDirs)
{
//
subDirs.clear();
QDir fromDir(QString::fromLocal8Bit(dir.c_str()));
QStringList filters;
//
QFileInfoList fileInfoList = fromDir.entryInfoList(filters, QDir::AllDirs | QDir::Files);
foreach(QFileInfo fileInfo, fileInfoList)
{
if (fileInfo.fileName() == "." || fileInfo.fileName() == "..")
{
continue;
}
if (fileInfo.isDir())
{
QByteArray dir = fileInfo.filePath().toLocal8Bit();
subDirs.push_back(dir.data());
}
}
}
//得到檔案路徑的檔案名 C:\\b\\a(.txt) -> a
static std::string DirOrPathGetName(std::string filePath)
{
size_t m = filePath.find_last_of('/');
if (m == string::npos)
{
return filePath;
}
size_t p = filePath.find_last_of('.');
if (p != string::npos && p > m) //沒有點号或者
{
filePath.erase(p);
}
std::string dirPath = filePath;
dirPath.erase(0, m + 1);
return dirPath;
}
void createObliqueIndexes(std::string fileDir)
{
string dataDir = fileDir + "/Data";
osg::ref_ptr<osg::Group> group = new osg::Group();
vector<string> subDirs;
findDir(dataDir, subDirs);
for (size_t i = 0; i < subDirs.size(); i++)
{
string name = DirOrPathGetName(subDirs[i]);
string path = subDirs[i] + "/" + name + ".osgb";
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(path);
osg::ref_ptr<osg::PagedLOD> lod = new osg::PagedLOD();
auto bs = node->getBound();
auto c = bs.center();
auto r = bs.radius();
lod->setCenter(c);
lod->setRadius(r);
lod->setRangeMode(osg::LOD::RangeMode::PIXEL_SIZE_ON_SCREEN);
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->getOrCreateStateSet();
lod->addChild(geode.get());
std::string relativeFilePath = "./Data/" + name + "/" + name + ".osgb"; //相對路徑
lod->setFileName(0, "");
lod->setFileName(1, relativeFilePath);
lod->setRange(0, 0, 1.0); //第一層不可見
lod->setRange(1, 1.0, FLT_MAX);
lod->setDatabasePath("");
group->addChild(lod);
}
std::string outputLodFile = fileDir + "/Data.osgb";
osgDB::writeNodeFile(*group, outputLodFile);
}
int main(int argc, char *argv[])
{
string fileDir = "D:/Data/scene/city";
std::string outputLodFile = fileDir + "/Data.osgb";
createObliqueIndexes(fileDir);
osgViewer::Viewer viewer;
osg::Node * node = new osg::Node;
node = osgDB::readNodeFile(outputLodFile);
viewer.setSceneData(node);
return viewer.run();
}
如果直接讀取每一塊的LOD然後通過osgDB::writeNodeFile寫入到一個osgb檔案,這個檔案就會儲存所有塊的LOD第一層資訊。這樣在第二冊加載的時候還是會比較慢,是以這裡就建立了一個空的節點,形成了索引所有LOD塊的資料結構。對于每一塊資料,建立兩層LOD,第一層為自身的空白節點,第二層為分塊LOD的第一層資料:
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(path);
osg::ref_ptr<osg::PagedLOD> lod = new osg::PagedLOD();
auto bs = node->getBound();
auto c = bs.center();
auto r = bs.radius();
lod->setCenter(c);
lod->setRadius(r);
lod->setRangeMode(osg::LOD::RangeMode::PIXEL_SIZE_ON_SCREEN);
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->getOrCreateStateSet();
lod->addChild(geode.get());
std::string relativeFilePath = "./Data/" + name + "/" + name + ".osgb"; //相對路徑
lod->setFileName(0, "");
lod->setFileName(1, relativeFilePath);
lod->setRange(0, 0, 1.0); //第一層不可見
lod->setRange(1, 1.0, FLT_MAX);
lod->setDatabasePath("");
group->addChild(lod);
LOD的Center和Radius都非常重要,需要預先設定好;setRangeMode設定了細節層級排程的模式,一般都為PIXEL_SIZE_ON_SCREEN;setFileName設定了每一層的資料路徑,setRange确定了目前層級的範圍。由于這個LOD隻是個索引檔案,是以會設定第二層為極大的可見範圍值。
可以像加載普通OSGB檔案一樣加載這個索引檔案,通過osgviewer加載的效果如下: